mirror of
https://github.com/pure-admin/vue-pure-admin.git
synced 2025-06-08 01:17:23 +08:00
feat: 添加菜单树结构事例
This commit is contained in:
parent
eb0771e7ec
commit
10fa0ee8c8
@ -13,6 +13,8 @@ export function findIconReg(icon: string) {
|
|||||||
const faReg = /^FA-/;
|
const faReg = /^FA-/;
|
||||||
// iconfont
|
// iconfont
|
||||||
const iFReg = /^IF-/;
|
const iFReg = /^IF-/;
|
||||||
|
// remixicon
|
||||||
|
const riReg = /^RI-/;
|
||||||
// typeof icon === "function" 属于SVG
|
// typeof icon === "function" 属于SVG
|
||||||
if (faReg.test(icon)) {
|
if (faReg.test(icon)) {
|
||||||
const text = icon.split(faReg)[1];
|
const text = icon.split(faReg)[1];
|
||||||
@ -25,12 +27,14 @@ export function findIconReg(icon: string) {
|
|||||||
return findIcon(icon.split(iFReg)[1], "IF");
|
return findIcon(icon.split(iFReg)[1], "IF");
|
||||||
} else if (typeof icon === "function") {
|
} else if (typeof icon === "function") {
|
||||||
return findIcon(icon, "SVG");
|
return findIcon(icon, "SVG");
|
||||||
|
} else if (riReg.test(icon)) {
|
||||||
|
return findIcon(icon.split(riReg)[1], "RI");
|
||||||
} else {
|
} else {
|
||||||
return findIcon(icon, "EL");
|
return findIcon(icon, "EL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 支持fontawesome、iconfont、element-plus/icons、自定义svg
|
// 支持fontawesome、iconfont、remixicon、element-plus/icons、自定义svg
|
||||||
export function findIcon(icon: String, type = "EL", property?: string) {
|
export function findIcon(icon: String, type = "EL", property?: string) {
|
||||||
if (type === "FA") {
|
if (type === "FA") {
|
||||||
return defineComponent({
|
return defineComponent({
|
||||||
@ -49,6 +53,14 @@ export function findIcon(icon: String, type = "EL", property?: string) {
|
|||||||
},
|
},
|
||||||
template: `<i :class="icon" />`
|
template: `<i :class="icon" />`
|
||||||
});
|
});
|
||||||
|
} else if (type === "RI") {
|
||||||
|
return defineComponent({
|
||||||
|
name: "RIIcon",
|
||||||
|
data() {
|
||||||
|
return { icon: `ri-${icon}` };
|
||||||
|
},
|
||||||
|
template: `<i :class="icon" />`
|
||||||
|
});
|
||||||
} else if (type === "EL") {
|
} else if (type === "EL") {
|
||||||
const components = iconComponents.filter(
|
const components = iconComponents.filter(
|
||||||
component => component.name === icon
|
component => component.name === icon
|
||||||
|
@ -43,6 +43,7 @@ import {
|
|||||||
ElEmpty,
|
ElEmpty,
|
||||||
ElCollapse,
|
ElCollapse,
|
||||||
ElCollapseItem,
|
ElCollapseItem,
|
||||||
|
ElTreeV2,
|
||||||
// 指令
|
// 指令
|
||||||
ElLoading,
|
ElLoading,
|
||||||
ElInfiniteScroll
|
ElInfiniteScroll
|
||||||
@ -94,7 +95,8 @@ const components = [
|
|||||||
ElAvatar,
|
ElAvatar,
|
||||||
ElEmpty,
|
ElEmpty,
|
||||||
ElCollapse,
|
ElCollapse,
|
||||||
ElCollapseItem
|
ElCollapseItem,
|
||||||
|
ElTreeV2
|
||||||
];
|
];
|
||||||
|
|
||||||
// https://element-plus.org/zh-CN/component/icon.html
|
// https://element-plus.org/zh-CN/component/icon.html
|
||||||
|
@ -43,6 +43,7 @@ export const menusConfig = {
|
|||||||
permissionPage: "页面权限",
|
permissionPage: "页面权限",
|
||||||
permissionButton: "按钮权限",
|
permissionButton: "按钮权限",
|
||||||
hstabs: "标签页操作",
|
hstabs: "标签页操作",
|
||||||
|
hsMenuTree: "菜单树结构",
|
||||||
externalLink: "外链"
|
externalLink: "外链"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -80,6 +81,7 @@ export const menusConfig = {
|
|||||||
permissionPage: "Page Permission",
|
permissionPage: "Page Permission",
|
||||||
permissionButton: "Button Permission",
|
permissionButton: "Button Permission",
|
||||||
hstabs: "Tabs Operate",
|
hstabs: "Tabs Operate",
|
||||||
|
hsMenuTree: "Menu Tree",
|
||||||
externalLink: "External Link"
|
externalLink: "External Link"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ const homeRouter = {
|
|||||||
redirect: "/welcome",
|
redirect: "/welcome",
|
||||||
meta: {
|
meta: {
|
||||||
icon: "HomeFilled",
|
icon: "HomeFilled",
|
||||||
|
title: "message.hshome",
|
||||||
showLink: true,
|
showLink: true,
|
||||||
i18n: true,
|
i18n: true,
|
||||||
rank: 0
|
rank: 0
|
||||||
|
@ -3,6 +3,7 @@ import homeRouter from "./home";
|
|||||||
import errorRouter from "./error";
|
import errorRouter from "./error";
|
||||||
import editorRouter from "./editor";
|
import editorRouter from "./editor";
|
||||||
import nestedRouter from "./nested";
|
import nestedRouter from "./nested";
|
||||||
|
import menuTreeRouter from "./menuTree";
|
||||||
import externalLink from "./externalLink";
|
import externalLink from "./externalLink";
|
||||||
import flowChartRouter from "./flowchart";
|
import flowChartRouter from "./flowchart";
|
||||||
import remainingRouter from "./remaining";
|
import remainingRouter from "./remaining";
|
||||||
@ -22,6 +23,7 @@ const routes = [
|
|||||||
nestedRouter,
|
nestedRouter,
|
||||||
externalLink,
|
externalLink,
|
||||||
editorRouter,
|
editorRouter,
|
||||||
|
menuTreeRouter,
|
||||||
flowChartRouter,
|
flowChartRouter,
|
||||||
componentsRouter
|
componentsRouter
|
||||||
];
|
];
|
||||||
|
29
src/router/modules/menuTree.ts
Normal file
29
src/router/modules/menuTree.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import Layout from "/@/layout/index.vue";
|
||||||
|
|
||||||
|
const menuTreeRouter = {
|
||||||
|
path: "/menuTree",
|
||||||
|
name: "reMenuTree",
|
||||||
|
component: Layout,
|
||||||
|
redirect: "/menuTree/index",
|
||||||
|
meta: {
|
||||||
|
icon: "RI-node-tree",
|
||||||
|
title: "message.hsMenuTree",
|
||||||
|
i18n: true,
|
||||||
|
showLink: true,
|
||||||
|
rank: 9
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "/menuTree/index",
|
||||||
|
name: "reMenuTree",
|
||||||
|
component: () => import("/@/views/menu-tree/index.vue"),
|
||||||
|
meta: {
|
||||||
|
title: "message.hsMenuTree",
|
||||||
|
showLink: true,
|
||||||
|
i18n: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
export default menuTreeRouter;
|
@ -1,6 +1,7 @@
|
|||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { store } from "/@/store";
|
import { store } from "/@/store";
|
||||||
import { cacheType } from "./types";
|
import { cacheType } from "./types";
|
||||||
|
import { cloneDeep } from "lodash-es";
|
||||||
import { RouteConfigs } from "/@/layout/types";
|
import { RouteConfigs } from "/@/layout/types";
|
||||||
import { constantMenus } from "/@/router/modules";
|
import { constantMenus } from "/@/router/modules";
|
||||||
import { ascending, filterTree } from "/@/router/utils";
|
import { ascending, filterTree } from "/@/router/utils";
|
||||||
@ -12,6 +13,8 @@ export const usePermissionStore = defineStore({
|
|||||||
constantMenus,
|
constantMenus,
|
||||||
// 整体路由生成的菜单(静态、动态)
|
// 整体路由生成的菜单(静态、动态)
|
||||||
wholeMenus: [],
|
wholeMenus: [],
|
||||||
|
// 深拷贝一个菜单树,与导航菜单不突出
|
||||||
|
menusTree: [],
|
||||||
buttonAuth: [],
|
buttonAuth: [],
|
||||||
// 缓存页面keepAlive
|
// 缓存页面keepAlive
|
||||||
cachePageList: []
|
cachePageList: []
|
||||||
@ -24,6 +27,10 @@ export const usePermissionStore = defineStore({
|
|||||||
ascending(this.constantMenus.concat(routes))
|
ascending(this.constantMenus.concat(routes))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.menusTree = cloneDeep(
|
||||||
|
filterTree(ascending(this.constantMenus.concat(routes)))
|
||||||
|
);
|
||||||
|
|
||||||
const getButtonAuth = (arrRoutes: Array<RouteConfigs>) => {
|
const getButtonAuth = (arrRoutes: Array<RouteConfigs>) => {
|
||||||
if (!arrRoutes || !arrRoutes.length) return;
|
if (!arrRoutes || !arrRoutes.length) return;
|
||||||
arrRoutes.forEach((v: RouteConfigs) => {
|
arrRoutes.forEach((v: RouteConfigs) => {
|
||||||
|
42
src/utils/tree.ts
Normal file
42
src/utils/tree.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/**
|
||||||
|
* 提取菜单树中的每一项path
|
||||||
|
* @param {Object} {menuTree 菜单树}
|
||||||
|
* @param {return}} expandedPaths 每一项path组成的数组
|
||||||
|
*/
|
||||||
|
const expandedPaths = [];
|
||||||
|
export function extractPathList(menuTree) {
|
||||||
|
if (!Array.isArray(menuTree)) {
|
||||||
|
console.warn("menuTree must be an array");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!menuTree || menuTree.length === 0) return;
|
||||||
|
for (const node of menuTree) {
|
||||||
|
const hasChildren = node.children && node.children.length > 0;
|
||||||
|
if (hasChildren) {
|
||||||
|
extractPathList(node.children);
|
||||||
|
}
|
||||||
|
expandedPaths.push(node.path);
|
||||||
|
}
|
||||||
|
return expandedPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果父级下children的length为1,删除children
|
||||||
|
* @param {Object} {menuTree 菜单树}
|
||||||
|
* @param {return}}
|
||||||
|
*/
|
||||||
|
export function deleteChildren(menuTree) {
|
||||||
|
if (!Array.isArray(menuTree)) {
|
||||||
|
console.warn("menuTree must be an array");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!menuTree || menuTree.length === 0) return;
|
||||||
|
for (const node of menuTree) {
|
||||||
|
if (node.children && node.children.length === 1) delete node.children;
|
||||||
|
const hasChildren = node.children && node.children.length > 0;
|
||||||
|
if (hasChildren) {
|
||||||
|
deleteChildren(node.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return menuTree;
|
||||||
|
}
|
36
src/views/menu-tree/index.vue
Normal file
36
src/views/menu-tree/index.vue
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
export default {
|
||||||
|
name: "reMenuTree"
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import { extractPathList, deleteChildren } from "/@/utils/tree";
|
||||||
|
import { usePermissionStoreHook } from "/@/store/modules/permission";
|
||||||
|
|
||||||
|
let dataProps = ref({
|
||||||
|
value: "path",
|
||||||
|
children: "children"
|
||||||
|
});
|
||||||
|
|
||||||
|
let menusData = computed(() => {
|
||||||
|
return deleteChildren(usePermissionStoreHook().menusTree);
|
||||||
|
});
|
||||||
|
|
||||||
|
let expandedKeys = extractPathList(menusData.value);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-tree-v2
|
||||||
|
:data="menusData"
|
||||||
|
:props="dataProps"
|
||||||
|
show-checkbox
|
||||||
|
:height="500"
|
||||||
|
:default-expanded-keys="expandedKeys"
|
||||||
|
>
|
||||||
|
<template #default="{ data }">
|
||||||
|
<span>{{ $t(data.meta.title) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-tree-v2>
|
||||||
|
</template>
|
Loading…
x
Reference in New Issue
Block a user