feat(utils): Add color utils
This commit is contained in:
3
src/components/Menu/index.ts
Normal file
3
src/components/Menu/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import Menu from './src/Menu.vue'
|
||||
|
||||
export { Menu }
|
||||
143
src/components/Menu/src/Menu.vue
Normal file
143
src/components/Menu/src/Menu.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<script lang="tsx">
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import { ElMenu, ElScrollbar } from 'element-plus'
|
||||
import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { usePermissionStore } from '@/store/modules/permission'
|
||||
import type { LayoutType } from '@/config/app'
|
||||
import { useRenderMenuItem } from './components/useRenderMenuItem'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { isUrl } from '@/utils/is'
|
||||
import { lighten } from '@/utils/color'
|
||||
console.log(lighten('#001529', 6))
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Menu',
|
||||
setup() {
|
||||
const appStore = useAppStore()
|
||||
|
||||
const { push, currentRoute } = useRouter()
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
const preFixCls = getPrefixCls('menu')
|
||||
|
||||
const menuMode = computed(() => {
|
||||
// 水平模式
|
||||
const vertical: LayoutType[] = ['classic']
|
||||
|
||||
if (vertical.includes(appStore.getLayout)) {
|
||||
return 'vertical'
|
||||
} else {
|
||||
return 'horizontal'
|
||||
}
|
||||
})
|
||||
|
||||
const routers = computed(() => permissionStore.getRouters)
|
||||
|
||||
const collapse = computed(() => appStore.getCollapse)
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = currentRoute.value
|
||||
// if set path, the sidebar will highlight the path you set
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu as string
|
||||
}
|
||||
return path
|
||||
})
|
||||
|
||||
function menuSelect(index: string) {
|
||||
if (isUrl(index)) {
|
||||
window.open(index)
|
||||
} else {
|
||||
push(index)
|
||||
}
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div
|
||||
class={[
|
||||
preFixCls,
|
||||
'h-[100%] overflow-hidden',
|
||||
appStore.getCollapse
|
||||
? 'w-[var(--left-menu-min-width)]'
|
||||
: 'w-[var(--left-menu-max-width)]',
|
||||
'bg-[var(--left-menu-bg-color)]'
|
||||
]}
|
||||
>
|
||||
<ElScrollbar>
|
||||
<ElMenu
|
||||
defaultActive={activeMenu.value}
|
||||
mode={menuMode.value}
|
||||
collapse={collapse.value}
|
||||
backgroundColor="var(--left-menu-bg-color)"
|
||||
textColor="var(--left-menu-text-color)"
|
||||
activeTextColor="var(--left-menu-text-active-color)"
|
||||
onSelect={menuSelect}
|
||||
>
|
||||
{{
|
||||
default: () => {
|
||||
const { renderMenuItem } = useRenderMenuItem(routers.value)
|
||||
return renderMenuItem()
|
||||
}
|
||||
}}
|
||||
</ElMenu>
|
||||
</ElScrollbar>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@prefix-cls: ~'@{namespace}-menu';
|
||||
|
||||
@menuBgColor: var(--left-menu-bg-color);
|
||||
|
||||
.@{prefix-cls} {
|
||||
:deep(.el-menu) {
|
||||
border-right: none;
|
||||
|
||||
// 设置选中时子标题的颜色
|
||||
.is-active {
|
||||
& > .el-sub-menu__title {
|
||||
color: var(--left-menu-text-active-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置子菜单悬停的高亮和背景色
|
||||
.el-sub-menu__title,
|
||||
.el-menu-item {
|
||||
&:hover {
|
||||
color: var(--left-menu-text-active-color) !important;
|
||||
background-color: var(--left-menu-bg-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置选中时的高亮背景和高亮颜色
|
||||
.el-sub-menu.is-active,
|
||||
.el-menu-item.is-active {
|
||||
color: var(--left-menu-text-active-color) !important;
|
||||
background-color: var(--left-menu-bg-active-color) !important;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--left-menu-bg-active-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置子菜单的背景颜色
|
||||
.el-menu {
|
||||
.el-sub-menu__title,
|
||||
.el-menu-item:not(.is-active) {
|
||||
background-color: var(--left-menu-bg-light-color) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-menu--collapse) {
|
||||
width: var(--left-menu-min-width);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
49
src/components/Menu/src/components/useRenderMenuItem.tsx
Normal file
49
src/components/Menu/src/components/useRenderMenuItem.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { ElSubMenu, ElMenuItem } from 'element-plus'
|
||||
import type { RouteMeta } from 'vue-router'
|
||||
import { getAllParentPath, hasOneShowingChild } from '../helper'
|
||||
import { isUrl } from '@/utils/is'
|
||||
import { useRenderMenuTitle } from './useRenderMenuTitle'
|
||||
|
||||
export function useRenderMenuItem(allRouters: AppRouteRecordRaw[] = []) {
|
||||
function renderMenuItem(routers?: AppRouteRecordRaw[]) {
|
||||
return (routers || allRouters).map((v) => {
|
||||
const meta = (v.meta ?? {}) as RouteMeta
|
||||
if (!meta.hidden) {
|
||||
const { oneShowingChild, onlyOneChild } = hasOneShowingChild(v.children, v)
|
||||
|
||||
const fullPath = isUrl(v.path)
|
||||
? v.path
|
||||
: getAllParentPath<AppRouteRecordRaw>(allRouters, v.path).join('/')
|
||||
|
||||
const { renderMenuTitle } = useRenderMenuTitle()
|
||||
|
||||
if (
|
||||
oneShowingChild &&
|
||||
(!onlyOneChild?.children || onlyOneChild?.noShowingChildren) &&
|
||||
!meta?.alwaysShow
|
||||
) {
|
||||
return (
|
||||
<ElMenuItem index={fullPath}>
|
||||
{{
|
||||
default: () => renderMenuTitle(meta)
|
||||
}}
|
||||
</ElMenuItem>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<ElSubMenu index={fullPath}>
|
||||
{{
|
||||
title: () => renderMenuTitle(meta),
|
||||
default: () => renderMenuItem(v.children)
|
||||
}}
|
||||
</ElSubMenu>
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
renderMenuItem
|
||||
}
|
||||
}
|
||||
23
src/components/Menu/src/components/useRenderMenuTitle.tsx
Normal file
23
src/components/Menu/src/components/useRenderMenuTitle.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import type { RouteMeta } from 'vue-router'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
|
||||
export function useRenderMenuTitle() {
|
||||
function renderMenuTitle(meta: RouteMeta) {
|
||||
const { t } = useI18n()
|
||||
const { title = 'Please set title', icon } = meta
|
||||
|
||||
return icon ? (
|
||||
<>
|
||||
<Icon icon={meta.icon}></Icon>
|
||||
{t(title as string)}
|
||||
</>
|
||||
) : (
|
||||
t(title as string)
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
renderMenuTitle
|
||||
}
|
||||
}
|
||||
95
src/components/Menu/src/helper.ts
Normal file
95
src/components/Menu/src/helper.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import type { RouteMeta } from 'vue-router'
|
||||
import { ref, unref } from 'vue'
|
||||
|
||||
interface TreeConfig {
|
||||
id: string
|
||||
children: string
|
||||
pid: string
|
||||
}
|
||||
|
||||
type OnlyOneChildType = AppRouteRecordRaw & { noShowingChildren?: boolean }
|
||||
|
||||
interface HasOneShowingChild {
|
||||
oneShowingChild?: boolean
|
||||
onlyOneChild?: OnlyOneChildType
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: TreeConfig = {
|
||||
id: 'id',
|
||||
children: 'children',
|
||||
pid: 'pid'
|
||||
}
|
||||
|
||||
const getConfig = (config: Partial<TreeConfig>) => Object.assign({}, DEFAULT_CONFIG, config)
|
||||
|
||||
export function getAllParentPath<T = Recordable>(treeData: T[], path: string) {
|
||||
const menuList = findPath(treeData, (n) => n.path === path) as AppRouteRecordRaw[]
|
||||
return (menuList || []).map((item) => item.path)
|
||||
}
|
||||
|
||||
export function findPath<T = any>(
|
||||
tree: any,
|
||||
func: Fn,
|
||||
config: Partial<TreeConfig> = {}
|
||||
): T | T[] | null {
|
||||
config = getConfig(config)
|
||||
const path: T[] = []
|
||||
const list = [...tree]
|
||||
const visitedSet = new Set()
|
||||
const { children } = config
|
||||
while (list.length) {
|
||||
const node = list[0]
|
||||
if (visitedSet.has(node)) {
|
||||
path.pop()
|
||||
list.shift()
|
||||
} else {
|
||||
visitedSet.add(node)
|
||||
node[children!] && list.unshift(...node[children!])
|
||||
path.push(node)
|
||||
if (func(node)) {
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
export function hasOneShowingChild(
|
||||
children: AppRouteRecordRaw[] = [],
|
||||
parent: AppRouteRecordRaw
|
||||
): HasOneShowingChild {
|
||||
const onlyOneChild = ref<OnlyOneChildType>()
|
||||
|
||||
const showingChildren = children.filter((v) => {
|
||||
const meta = (v.meta ?? {}) as RouteMeta
|
||||
if (meta.hidden) {
|
||||
return false
|
||||
} else {
|
||||
// Temp set(will be used if only has one showing child)
|
||||
onlyOneChild.value = v
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
// When there is only one child router, the child router is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
return {
|
||||
oneShowingChild: true,
|
||||
onlyOneChild: unref(onlyOneChild)
|
||||
}
|
||||
}
|
||||
|
||||
// Show parent if there are no child router to display
|
||||
if (!showingChildren.length) {
|
||||
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
|
||||
return {
|
||||
oneShowingChild: true,
|
||||
onlyOneChild: unref(onlyOneChild)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
oneShowingChild: false,
|
||||
onlyOneChild: unref(onlyOneChild)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user