侧边导航栏优化
parent
206adfc16a
commit
4d2d06a46b
|
|
@ -1,104 +1,126 @@
|
||||||
import {
|
import { ElButton, ElIcon, ElMenu, ElMenuItem, ElMenuItemGroup, ElSubMenu, type MenuItemRegistered } from "element-plus";
|
||||||
ElButton,
|
import { elIcons } from "@/common/element/element.ts";
|
||||||
ElIcon,
|
import AIcon from "@/components/a-icon/AIcon.tsx";
|
||||||
ElMenu,
|
import type { IconName } from "@/components/a-icon/iconfont.ts";
|
||||||
ElMenuItem,
|
import styles from "@/pages/a-frame/aaside.module.styl";
|
||||||
ElMenuItemGroup,
|
|
||||||
ElSubMenu,
|
|
||||||
type MenuItemRegistered,
|
|
||||||
} from 'element-plus'
|
|
||||||
import { elIcons } from '@/common/element/element.ts'
|
|
||||||
import AIcon from '@/components/a-icon/AIcon.tsx'
|
|
||||||
import type { IconName } from '@/components/a-icon/iconfont.ts'
|
|
||||||
import styles from '@/pages/a-frame/aaside.module.styl'
|
|
||||||
|
|
||||||
export interface Menu extends G.TreeNode {
|
export interface Menu extends G.TreeNode {
|
||||||
// Id
|
// Id
|
||||||
id: string
|
id: string;
|
||||||
// 编码
|
// 编码
|
||||||
sn: string
|
sn: string;
|
||||||
// 上级 Id; 层级为 1 的节点值为 0
|
// 上级 Id; 层级为 1 的节点值为 0
|
||||||
pid: string
|
pid: string;
|
||||||
// 菜单名称
|
// 菜单名称
|
||||||
title: string
|
title: string;
|
||||||
// 图标
|
// 图标
|
||||||
icon: string
|
icon: string;
|
||||||
// 层级; >= 1
|
// 层级; >= 1
|
||||||
tier: number
|
tier: number;
|
||||||
// 排序
|
// 排序
|
||||||
sort: number
|
sort: number;
|
||||||
// 路由名称
|
// 路由名称
|
||||||
routeName: string
|
routeName: string;
|
||||||
// 面包路径
|
// 面包路径
|
||||||
breadcrumb: string[]
|
breadcrumb: string[];
|
||||||
// 类型
|
// 类型
|
||||||
menuCategory: 'Catalog' | 'Group' | 'Page' | 'SubPage' | 'Btn'
|
menuCategory: "Catalog" | "Group" | "Page" | "SubPage" | "Btn";
|
||||||
// 子菜单
|
// 子菜单
|
||||||
children?: Menu[]
|
children?: Menu[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent(
|
export default defineComponent(
|
||||||
(props, {emit}) => {
|
(props, { emit }) => {
|
||||||
const onMenuClick = (it: MenuItemRegistered) => emit('menuClick', it.index)
|
let defaultId = "";
|
||||||
const renderMenu = (it: Menu) => {
|
let path: any = props.defaultPath.split("/");
|
||||||
let renderChildNode: (() => VNode[] | undefined) | undefined = undefined
|
path = path[path.length - 1];
|
||||||
if (it.children != null && it.children.length > 0) {
|
props.menus.forEach((item) => {
|
||||||
renderChildNode = () => (it.children?.map(renderMenu))
|
if (item.routeName == path) {
|
||||||
|
defaultId = item.id;
|
||||||
|
} else if (item.children && item.children.length) {
|
||||||
|
item.children.forEach((item2) => {
|
||||||
|
if (item2.routeName == path) {
|
||||||
|
defaultId = item2.id;
|
||||||
|
} else if (item2.children && item2.children.length) {
|
||||||
|
item2.children.forEach((item3) => {
|
||||||
|
if (item3.routeName == path) {
|
||||||
|
defaultId = item3.id;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
let currentNode: VNode
|
});
|
||||||
|
|
||||||
|
const onMenuClick = (it: MenuItemRegistered) => emit("menuClick", it.index);
|
||||||
|
const renderMenu = (it: Menu) => {
|
||||||
|
let renderChildNode: (() => VNode[] | undefined) | undefined = undefined;
|
||||||
|
if (it.children != null && it.children.length > 0) {
|
||||||
|
renderChildNode = () => it.children?.map(renderMenu);
|
||||||
|
}
|
||||||
|
let currentNode: VNode;
|
||||||
switch (it.menuCategory) {
|
switch (it.menuCategory) {
|
||||||
case 'Catalog': {
|
case "Catalog": {
|
||||||
currentNode = (<ElSubMenu index={it.id}>
|
currentNode = (
|
||||||
{{
|
<ElSubMenu index={it.id}>
|
||||||
title: () => (<>
|
{{
|
||||||
<AIcon class={styles.aIcon} name={it.icon as IconName}/>
|
title: () => (
|
||||||
<span>{it.title}</span>
|
<>
|
||||||
</>),
|
<AIcon class={styles.aIcon} name={it.icon as IconName} />
|
||||||
default: renderChildNode,
|
<span>{it.title}</span>
|
||||||
}}
|
</>
|
||||||
</ElSubMenu>)
|
),
|
||||||
break
|
default: renderChildNode,
|
||||||
|
}}
|
||||||
|
</ElSubMenu>
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case 'Group': {
|
case "Group": {
|
||||||
currentNode = (<ElMenuItemGroup title={it.title}>
|
currentNode = (
|
||||||
{{
|
<ElMenuItemGroup title={it.title}>
|
||||||
default: renderChildNode,
|
{{
|
||||||
}}
|
default: renderChildNode,
|
||||||
</ElMenuItemGroup>)
|
}}
|
||||||
break
|
</ElMenuItemGroup>
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case 'Page': {
|
case "Page": {
|
||||||
currentNode = (<ElMenuItem index={it.id} onClick={onMenuClick}>
|
currentNode = (
|
||||||
{{
|
<ElMenuItem index={it.id} onClick={onMenuClick}>
|
||||||
title: () => <span>{it.title}</span>,
|
{{
|
||||||
default: () => <AIcon class={styles.aIcon} name={it.icon as IconName}/>,
|
title: () => <span>{it.title}</span>,
|
||||||
}}
|
default: () => <AIcon class={styles.aIcon} name={it.icon as IconName} />,
|
||||||
</ElMenuItem>)
|
}}
|
||||||
break
|
</ElMenuItem>
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
currentNode = (<></>)
|
currentNode = <></>;
|
||||||
}
|
}
|
||||||
return currentNode
|
return currentNode;
|
||||||
}
|
};
|
||||||
const isCollapse = ref(false)
|
const isCollapse = ref(false);
|
||||||
|
|
||||||
return () => (<>
|
return () => (
|
||||||
<ElMenu collapse={isCollapse.value} style={{height: '100%', overflow: 'auto', '--el-menu-base-level-padding': '10px'}} class={[ styles.aMenus, 'menus' ]}>
|
<>
|
||||||
{{
|
<ElMenu default-active={defaultId} collapse={isCollapse.value} style={{ height: "100%", overflow: "auto", "--el-menu-base-level-padding": "10px" }} class={[styles.aMenus, "menus"]}>
|
||||||
default: () => props.menus.map(renderMenu),
|
{{
|
||||||
}}
|
default: () => props.menus.map(renderMenu),
|
||||||
</ElMenu>
|
}}
|
||||||
<ElButton style={{position: 'absolute', right: '6px', bottom: '6px', width: '32px', height: '32px'}} onClick={() => {
|
</ElMenu>
|
||||||
isCollapse.value = !isCollapse.value
|
<ElButton
|
||||||
}}>
|
style={{ position: "absolute", right: "6px", bottom: "6px", width: "32px", height: "32px" }}
|
||||||
<ElIcon style={{cursor: 'pointer'}}>
|
onClick={() => {
|
||||||
{
|
isCollapse.value = !isCollapse.value;
|
||||||
isCollapse.value ? <elIcons.Fold/> : <elIcons.Expand/>
|
}}
|
||||||
}
|
>
|
||||||
</ElIcon>
|
<ElIcon style={{ cursor: "pointer" }}>{isCollapse.value ? <elIcons.Fold /> : <elIcons.Expand />}</ElIcon>
|
||||||
</ElButton>
|
</ElButton>
|
||||||
</>)
|
</>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -107,10 +129,14 @@ export default defineComponent(
|
||||||
required: true,
|
required: true,
|
||||||
validator: (value: Menu[]) => value != null && value.length > 0,
|
validator: (value: Menu[]) => value != null && value.length > 0,
|
||||||
},
|
},
|
||||||
|
defaultPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: {
|
emits: {
|
||||||
menuClick: (id: string) => id != null,
|
menuClick: (id: string) => id != null,
|
||||||
},
|
},
|
||||||
name: 'AAside',
|
name: "AAside",
|
||||||
})
|
}
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,64 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import AAside from '@/pages/a-frame/AAside.tsx'
|
import AAside from "@/pages/a-frame/AAside.tsx";
|
||||||
import AAvatar from '@/pages/a-frame/AAvatar.vue'
|
import AAvatar from "@/pages/a-frame/AAvatar.vue";
|
||||||
import { appName } from '@/common'
|
import { appName } from "@/common";
|
||||||
import Colls from '@/common/utils/colls.ts'
|
import Colls from "@/common/utils/colls.ts";
|
||||||
import { MenuCategory } from '@/common/app/constants.ts'
|
import { MenuCategory } from "@/common/app/constants.ts";
|
||||||
import { useAppSettingStore } from '@/common/app/app-setting-store.ts'
|
import { useAppSettingStore } from "@/common/app/app-setting-store.ts";
|
||||||
import Nav from '@/common/router/nav.ts'
|
import Nav from "@/common/router/nav.ts";
|
||||||
import Evt from '@/common/utils/evt.ts'
|
import Evt from "@/common/utils/evt.ts";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
const appSettingStore = useAppSettingStore()
|
const appSettingStore = useAppSettingStore();
|
||||||
const id_menu_map = computed(() => {
|
const id_menu_map = computed(() => {
|
||||||
return Colls.keyObj(
|
return Colls.keyObj(
|
||||||
appSettingStore.menus.filter((it) => it.menuCategory === MenuCategory.Page || it.menuCategory === MenuCategory.Group || it.menuCategory === MenuCategory.Catalog),
|
appSettingStore.menus.filter((it) => it.menuCategory === MenuCategory.Page || it.menuCategory === MenuCategory.Group || it.menuCategory === MenuCategory.Catalog),
|
||||||
(it) => it.id,
|
(it) => it.id,
|
||||||
(it) => it,
|
(it) => it
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
function onMenuClick(id: string) {
|
function onMenuClick(id: string) {
|
||||||
const menu = id_menu_map.value[id]
|
const menu = id_menu_map.value[id];
|
||||||
Nav.open({
|
Nav.open({
|
||||||
insId: menu?.routeName ?? '',
|
insId: menu?.routeName ?? "",
|
||||||
routeName: menu?.routeName ?? '',
|
routeName: menu?.routeName ?? "",
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuTree = computed(() => {
|
const menuTree = computed(() => {
|
||||||
return Colls.toTree(
|
return Colls.toTree(appSettingStore.menus.filter((it) => it.menuCategory === MenuCategory.Page || it.menuCategory === MenuCategory.Group || it.menuCategory === MenuCategory.Catalog).sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)));
|
||||||
appSettingStore.menus
|
});
|
||||||
.filter((it) => it.menuCategory === MenuCategory.Page || it.menuCategory === MenuCategory.Group || it.menuCategory === MenuCategory.Catalog)
|
|
||||||
.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)),
|
const router = useRouter();
|
||||||
)
|
const defaultPath= ref<string>(router.currentRoute.value.path);
|
||||||
})
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
Evt.emit('connect_ws')
|
Evt.emit("connect_ws");
|
||||||
})
|
});
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
Evt.emit('disconnect_ws')
|
Evt.emit("disconnect_ws");
|
||||||
})
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElContainer class="a-frame">
|
<ElContainer class="a-frame">
|
||||||
<ElHeader>
|
<ElHeader>
|
||||||
<div>
|
<div>
|
||||||
<img alt="" src="@/assets/images/循环.svg"/>
|
<img alt="" src="@/assets/images/循环.svg" />
|
||||||
<div>{{ appName }}</div>
|
<div>{{ appName }}</div>
|
||||||
</div>
|
</div>
|
||||||
<AAvatar/>
|
<AAvatar />
|
||||||
</ElHeader>
|
</ElHeader>
|
||||||
<ElContainer>
|
<ElContainer>
|
||||||
<ElAside>
|
<ElAside>
|
||||||
<AAside :menus="menuTree" @menu-click="onMenuClick"/>
|
<AAside :defaultPath="defaultPath" :menus="menuTree" @menu-click="onMenuClick" />
|
||||||
</ElAside>
|
</ElAside>
|
||||||
<ElMain>
|
<ElMain>
|
||||||
<RouterView #="{ Component }">
|
<RouterView #="{ Component }">
|
||||||
<Transition name="el-fade-in-linear">
|
<Transition name="el-fade-in-linear">
|
||||||
<component :is="Component"/>
|
<component :is="Component" />
|
||||||
</Transition>
|
</Transition>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
</ElMain>
|
</ElMain>
|
||||||
|
|
@ -118,7 +119,6 @@ onUnmounted(() => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<style>
|
<style>
|
||||||
.menus:not(.el-menu--collapse) {
|
.menus:not(.el-menu--collapse) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue