feat(utils): Add color utils

This commit is contained in:
陈凯龙
2022-01-12 16:44:57 +08:00
parent b218ccc9cc
commit 71dfba21c5
21 changed files with 1268 additions and 635 deletions

View File

@@ -0,0 +1,3 @@
import Menu from './src/Menu.vue'
export { Menu }

View 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>

View 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
}
}

View 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
}
}

View 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)
}
}