feat: 添加菜单树结构事例

This commit is contained in:
xiaoxian521 2021-12-16 11:03:20 +08:00
parent eb0771e7ec
commit 10fa0ee8c8
9 changed files with 135 additions and 2 deletions

View File

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

View File

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

View File

@ -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"
} }
} }

View File

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

View File

@ -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
]; ];

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

View File

@ -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
View 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为1children
* @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;
}

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