Browse Source

feat: 添加菜单树结构事例

xiaoxian521 3 năm trước cách đây
mục cha
commit
10fa0ee8c8

+ 13 - 1
src/components/ReIcon/index.ts

@@ -13,6 +13,8 @@ export function findIconReg(icon: string) {
   const faReg = /^FA-/;
   // iconfont
   const iFReg = /^IF-/;
+  // remixicon
+  const riReg = /^RI-/;
   // typeof icon === "function" 属于SVG
   if (faReg.test(icon)) {
     const text = icon.split(faReg)[1];
@@ -25,12 +27,14 @@ export function findIconReg(icon: string) {
     return findIcon(icon.split(iFReg)[1], "IF");
   } else if (typeof icon === "function") {
     return findIcon(icon, "SVG");
+  } else if (riReg.test(icon)) {
+    return findIcon(icon.split(riReg)[1], "RI");
   } else {
     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) {
   if (type === "FA") {
     return defineComponent({
@@ -49,6 +53,14 @@ export function findIcon(icon: String, type = "EL", property?: string) {
       },
       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") {
     const components = iconComponents.filter(
       component => component.name === icon

+ 3 - 1
src/plugins/element-plus/index.ts

@@ -43,6 +43,7 @@ import {
   ElEmpty,
   ElCollapse,
   ElCollapseItem,
+  ElTreeV2,
   // 指令
   ElLoading,
   ElInfiniteScroll
@@ -94,7 +95,8 @@ const components = [
   ElAvatar,
   ElEmpty,
   ElCollapse,
-  ElCollapseItem
+  ElCollapseItem,
+  ElTreeV2
 ];
 
 // https://element-plus.org/zh-CN/component/icon.html

+ 2 - 0
src/plugins/i18n/config.ts

@@ -43,6 +43,7 @@ export const menusConfig = {
       permissionPage: "页面权限",
       permissionButton: "按钮权限",
       hstabs: "标签页操作",
+      hsMenuTree: "菜单树结构",
       externalLink: "外链"
     }
   },
@@ -80,6 +81,7 @@ export const menusConfig = {
       permissionPage: "Page Permission",
       permissionButton: "Button Permission",
       hstabs: "Tabs Operate",
+      hsMenuTree: "Menu Tree",
       externalLink: "External Link"
     }
   }

+ 1 - 0
src/router/modules/home.ts

@@ -7,6 +7,7 @@ const homeRouter = {
   redirect: "/welcome",
   meta: {
     icon: "HomeFilled",
+    title: "message.hshome",
     showLink: true,
     i18n: true,
     rank: 0

+ 2 - 0
src/router/modules/index.ts

@@ -3,6 +3,7 @@ import homeRouter from "./home";
 import errorRouter from "./error";
 import editorRouter from "./editor";
 import nestedRouter from "./nested";
+import menuTreeRouter from "./menuTree";
 import externalLink from "./externalLink";
 import flowChartRouter from "./flowchart";
 import remainingRouter from "./remaining";
@@ -22,6 +23,7 @@ const routes = [
   nestedRouter,
   externalLink,
   editorRouter,
+  menuTreeRouter,
   flowChartRouter,
   componentsRouter
 ];

+ 29 - 0
src/router/modules/menuTree.ts

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

+ 7 - 0
src/store/modules/permission.ts

@@ -1,6 +1,7 @@
 import { defineStore } from "pinia";
 import { store } from "/@/store";
 import { cacheType } from "./types";
+import { cloneDeep } from "lodash-es";
 import { RouteConfigs } from "/@/layout/types";
 import { constantMenus } from "/@/router/modules";
 import { ascending, filterTree } from "/@/router/utils";
@@ -12,6 +13,8 @@ export const usePermissionStore = defineStore({
     constantMenus,
     // 整体路由生成的菜单(静态、动态)
     wholeMenus: [],
+    // 深拷贝一个菜单树,与导航菜单不突出
+    menusTree: [],
     buttonAuth: [],
     // 缓存页面keepAlive
     cachePageList: []
@@ -24,6 +27,10 @@ export const usePermissionStore = defineStore({
         ascending(this.constantMenus.concat(routes))
       );
 
+      this.menusTree = cloneDeep(
+        filterTree(ascending(this.constantMenus.concat(routes)))
+      );
+
       const getButtonAuth = (arrRoutes: Array<RouteConfigs>) => {
         if (!arrRoutes || !arrRoutes.length) return;
         arrRoutes.forEach((v: RouteConfigs) => {

+ 42 - 0
src/utils/tree.ts

@@ -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 - 0
src/views/menu-tree/index.vue

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