Explorar o código

权限+主题色配置

liujiejie hai 1 ano
pai
achega
d53b2dc691

+ 31 - 3
src/App.vue

@@ -1,17 +1,40 @@
 <template>
-  <div id="app">
+  <div id="app" :style="{ '--color': defaultTheme }" v-loading="defaultLoading">
+    <theme-picker v-show="false" />
+
     <router-view />
   </div>
 </template>
 <script>
+import ThemePicker from "@/components/ThemePicker";
 import { login } from "@/api/login";
 import { testApi } from "@/api/test";
-import axios from "axios";
+import "@/assets/style/index.css";
 export default {
   created() {
     // this.test('2122')
   },
-
+  components: { ThemePicker },
+  metaInfo() {
+    return {
+      title:
+        this.$store.state.settings.dynamicTitle &&
+        this.$store.state.settings.title,
+      titleTemplate: (title) => {
+        return title
+          ? `${title} - ${process.env.VUE_APP_TITLE}`
+          : process.env.VUE_APP_TITLE;
+      },
+    };
+  },
+  computed: {
+    defaultTheme() {
+      return this.$store.state.settings.theme;
+    },
+    defaultLoading() {
+      return this.$store.state.settings.loading;
+    },
+  },
   methods: {
     // 调取后端api方法
     addLog() {
@@ -40,3 +63,8 @@ body {
   height: 100%;
 }
 </style>
+<style scoped>
+#app .theme-picker {
+  display: none;
+}
+</style>

+ 31 - 0
src/assets/style/element-variables.module.scss

@@ -0,0 +1,31 @@
+/**
+* I think element-ui's default theme color is too light for long-term use.
+* So I modified the default color and you can modify it to your liking.
+**/
+
+/* theme color */
+$--color-primary: #00baad;
+$--color-success: #13ce66;
+$--color-warning: #ffba00;
+$--color-danger: #ff4949;
+// $--color-info: #1E1E1E;
+
+$--button-font-weight: 400;
+
+// $--color-text-regular: #1f2d3d;
+
+$--border-color-light: #dfe4ed;
+$--border-color-lighter: #e6ebf5;
+
+$--table-border: 1px solid #dfe6ec;
+
+/* icon font path, required */
+$--font-path: "~element-ui/lib/theme-chalk/fonts";
+
+@import "~element-ui/packages/theme-chalk/src/index";
+
+// the :export directive is the magic sauce for webpack
+// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
+:export {
+  theme: $--color-primary;
+}

+ 4 - 0
src/assets/style/index.css

@@ -0,0 +1,4 @@
+.el-table th.el-table__cell {
+  background-color: var(--color) !important;
+  color: aliceblue !important;
+}

+ 35 - 16
src/components/HeaderCom.vue

@@ -6,7 +6,12 @@
     </div>
     <div class="personalCenter">
       <p class="current-time">{{ currentDate }}</p>
-      <el-popover placement="bottom" width="220" trigger="click" style="cursor: pointer">
+      <el-popover
+        placement="bottom"
+        width="220"
+        trigger="click"
+        style="cursor: pointer"
+      >
         <img src="@/assets/portrait.png" slot="reference" />
         <div>
           <div class="cursor-pointer boxmini">
@@ -21,6 +26,14 @@
             <p>角色:超级管理员</p>
           </div>
           <el-divider></el-divider>
+          <div class="cursor-pointer boxmini">
+            <p>主题切换:</p>
+            <theme-picker
+              style="float: right; height: 26px; margin: -3px 8px 0 0"
+              @change="themeChange"
+            />
+          </div>
+          <el-divider></el-divider>
           <div class="cursor-pointer boxmini" @click="skip(1)">
             <p class="logout">退出登录</p>
           </div>
@@ -31,33 +44,43 @@
 </template>
 
 <script>
+import ThemePicker from "@/components/ThemePicker";
 export default {
   data() {
     return {
       currentDate: "",
-    }
+    };
+  },
+  components: {
+    ThemePicker,
   },
-
   mounted() {
     this.updateCurrentDateTime();
     setInterval(this.updateCurrentDateTime, 1000);
   },
 
   methods: {
+    themeChange(val) {
+      this.$store.dispatch("settings/changeSetting", {
+        key: "theme",
+        value: val,
+      });
+      this.theme = val;
+    },
     updateCurrentDateTime() {
       const now = new Date();
       const year = now.getFullYear();
-      const month = (now.getMonth() + 1).toString().padStart(2, '0');
-      const day = now.getDate().toString().padStart(2, '0');
-      const hours = now.getHours().toString().padStart(2, '0');
-      const minutes = now.getMinutes().toString().padStart(2, '0');
-      const seconds = now.getSeconds().toString().padStart(2, '0');
+      const month = (now.getMonth() + 1).toString().padStart(2, "0");
+      const day = now.getDate().toString().padStart(2, "0");
+      const hours = now.getHours().toString().padStart(2, "0");
+      const minutes = now.getMinutes().toString().padStart(2, "0");
+      const seconds = now.getSeconds().toString().padStart(2, "0");
 
       // 设置当前时间数据
       this.currentDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
-    }
-  }
-}
+    },
+  },
+};
 </script>
 
 <style lang="scss" scoped>
@@ -66,7 +89,7 @@ export default {
   justify-content: space-between;
   align-items: center;
   height: 60px;
-  background: #FFF;
+  background: #fff;
   padding: 0 20px;
 
   .header-logo {
@@ -111,12 +134,9 @@ export default {
 .boxmini {
   display: flex;
 
-
   p {
-
     font-size: 14px;
     font-weight: 600;
-
   }
 
   .logout {
@@ -124,7 +144,6 @@ export default {
     text-align: center;
     width: 100%;
   }
-
 }
 
 .el-divider--horizontal {

+ 16 - 0
src/components/ThemePicker/chalk.js

@@ -0,0 +1,16 @@
+/*
+ * @Author: your name
+ * @Date: 2024-05-19 21:10:47
+ * @LastEditTime: 2024-05-19 21:10:55
+ * @LastEditors: bogon
+ * @Description: In User Settings Edit
+ * @FilePath: /performance-test/src/components/ThemePicker/chalk.js
+ */
+// chalk.js
+const chalkCss = `
+body {
+  background-color: #f0f0f0;
+}
+`;
+
+export { chalkCss };

+ 220 - 0
src/components/ThemePicker/index.vue

@@ -0,0 +1,220 @@
+<template>
+  <el-color-picker
+    v-model="theme"
+    :predefine="[
+      '#409EFF',
+      '#1890ff',
+      '#304156',
+      '#212121',
+      '#11a983',
+      '#13c2c2',
+      '#6959CD',
+      '#f5222d',
+    ]"
+    class="theme-picker"
+    popper-class="theme-picker-dropdown"
+    @change="change"
+  />
+</template>
+<script>
+// import chalkCss from "./chalk";
+const version = require("element-ui/package.json").version; // element-ui version from node_modules
+const ORIGINAL_THEME = "#409EFF"; // default color
+
+export default {
+  data() {
+    return {
+      chalk: "", // content of theme-chalk css
+      theme: "",
+    };
+  },
+  computed: {
+    defaultTheme() {
+      return this.$store.state.settings.theme;
+    },
+  },
+  watch: {
+    defaultTheme: {
+      handler: function (val, oldVal) {
+        this.theme = val;
+      },
+      immediate: true,
+    },
+    async theme(val) {
+      await this.setTheme(val);
+    },
+  },
+  created() {
+    if (this.defaultTheme !== ORIGINAL_THEME) {
+      this.setTheme(this.defaultTheme);
+    }
+  },
+
+  methods: {
+    async setTheme(val) {
+      const oldVal = this.chalk ? this.theme : ORIGINAL_THEME;
+      if (typeof val !== "string") return;
+      const themeCluster = this.getThemeCluster(val.replace("#", ""));
+      const originalCluster = this.getThemeCluster(oldVal.replace("#", ""));
+
+      const getHandler = (variable, id) => {
+        return () => {
+          const originalCluster = this.getThemeCluster(
+            ORIGINAL_THEME.replace("#", "")
+          );
+          const newStyle = this.updateStyle(
+            this[variable],
+            originalCluster,
+            themeCluster
+          );
+
+          let styleTag = document.getElementById(id);
+          if (!styleTag) {
+            styleTag = document.createElement("style");
+            styleTag.setAttribute("id", id);
+            document.head.appendChild(styleTag);
+          }
+          styleTag.innerText = newStyle;
+        };
+      };
+
+      if (!this.chalk) {
+        console.log(version, "version");
+        this.$store.commit("settings/changeLoading", {
+          key: "loading",
+          value: true,
+        });
+        const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`;
+        // // const url = require("@/assets/style/theme-chalk.css");
+        const res = await this.getCSSString(url, "chalk");
+        console.log(res, "res");
+        this.$store.commit("settings/changeLoading", {
+          key: "loading",
+          value: false,
+        });
+        //  this.chalk = chalkCss.replace(/@font-face{[^}]+}/, '') // 本地缓存,如果需要获取线上的就用上面那种方式,优点:切换无延迟,缺点:需要手动维护 css string
+      }
+
+      const chalkHandler = getHandler("chalk", "chalk-style");
+
+      chalkHandler();
+
+      const styles = [].slice
+        .call(document.querySelectorAll("style"))
+        .filter((style) => {
+          const text = style.innerText;
+          return (
+            new RegExp(oldVal, "i").test(text) && !/Chalk Variables/.test(text)
+          );
+        });
+      styles.forEach((style) => {
+        const { innerText } = style;
+        if (typeof innerText !== "string") return;
+        style.innerText = this.updateStyle(
+          innerText,
+          originalCluster,
+          themeCluster
+        );
+      });
+      // 主题更新完成后,设置 loading 状态为 false
+
+      this.$emit("change", val);
+      const that = this;
+      // setTimeout(() => {
+
+      // }, 2000);
+    },
+
+    updateStyle(style, oldCluster, newCluster) {
+      let newStyle = style;
+
+      // 执行完成时调用 resolve 表示异步操作完成
+      oldCluster.forEach((color, index) => {
+        newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]);
+      });
+      return newStyle;
+    },
+
+    getCSSString(url, variable) {
+      return new Promise((resolve) => {
+        const xhr = new XMLHttpRequest();
+        xhr.onreadystatechange = () => {
+          if (xhr.readyState === 4 && xhr.status === 200) {
+            this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, "");
+            resolve();
+          }
+        };
+        xhr.open("GET", url);
+        xhr.send();
+      });
+    },
+
+    getThemeCluster(theme) {
+      const tintColor = (color, tint) => {
+        let red = parseInt(color.slice(0, 2), 16);
+        let green = parseInt(color.slice(2, 4), 16);
+        let blue = parseInt(color.slice(4, 6), 16);
+
+        if (tint === 0) {
+          // when primary color is in its rgb space
+          return [red, green, blue].join(",");
+        } else {
+          red += Math.round(tint * (255 - red));
+          green += Math.round(tint * (255 - green));
+          blue += Math.round(tint * (255 - blue));
+
+          red = red.toString(16);
+          green = green.toString(16);
+          blue = blue.toString(16);
+
+          return `#${red}${green}${blue}`;
+        }
+      };
+
+      const shadeColor = (color, shade) => {
+        let red = parseInt(color.slice(0, 2), 16);
+        let green = parseInt(color.slice(2, 4), 16);
+        let blue = parseInt(color.slice(4, 6), 16);
+
+        red = Math.round((1 - shade) * red);
+        green = Math.round((1 - shade) * green);
+        blue = Math.round((1 - shade) * blue);
+
+        red = red.toString(16);
+        green = green.toString(16);
+        blue = blue.toString(16);
+
+        return `#${red}${green}${blue}`;
+      };
+
+      const clusters = [theme];
+      for (let i = 0; i <= 9; i++) {
+        clusters.push(tintColor(theme, Number((i / 10).toFixed(2))));
+      }
+      clusters.push(shadeColor(theme, 0.1));
+      return clusters;
+    },
+    change() {
+      const that = this;
+      console.log("dianji quding ");
+    },
+  },
+};
+</script>
+
+<style>
+.theme-message,
+.theme-picker-dropdown {
+  z-index: 99999 !important;
+}
+
+.theme-picker .el-color-picker__trigger {
+  height: 26px !important;
+  width: 26px !important;
+  padding: 2px;
+}
+
+.theme-picker-dropdown .el-color-dropdown__link-btn {
+  display: none;
+}
+</style>

+ 24 - 19
src/main.js

@@ -1,30 +1,35 @@
-import Vue from 'vue'
-import App from './App.vue'
-import router from './router'
-import { store } from './store/index.js'
-
+import Vue from "vue";
+import App from "./App.vue";
+import router from "./router";
+import { store } from "./store/index.js";
+import "./assets/style/element-variables.module.scss";
 // 引入element ui
-import ElementUI from 'element-ui'
-import 'element-ui/lib/theme-chalk/index.css'
-Vue.use(ElementUI)
-
+import ElementUI from "element-ui";
+import "element-ui/lib/theme-chalk/index.css";
+Vue.use(ElementUI);
 // 引入css
-import './styles/index.scss'
+import "./styles/index.scss";
 // 注册svg组件
-import SvgIcon from '@/components/SvgIcons/index.vue'
-Vue.component('SvgIcon', SvgIcon)
+import SvgIcon from "@/components/SvgIcons/index.vue";
+Vue.component("SvgIcon", SvgIcon);
 // 序列化post方法
-import qs from 'qs'
-Vue.prototype.$qs = qs
+import qs from "qs";
+Vue.prototype.$qs = qs;
 
 // 引入tailwind
 // https://www.tailwindcss.cn/docs/installation
 
-Vue.prototype.$BASE_URL = window?._BASE_CONFIG?.API
-Vue.config.productionTip = false
-
+Vue.prototype.$BASE_URL = window?._BASE_CONFIG?.API;
+Vue.config.productionTip = false;
+if (store.state.auth.userInfo.permission !== undefined) {
+  store.dispatch("auth/setAddRouter", {
+    resultRouter:
+      (store.state.auth.userInfo && store.state.auth.userInfo.permission) || [],
+    router,
+  });
+}
 new Vue({
   router,
   store,
-  render: (h) => h(App)
-}).$mount('#app')
+  render: (h) => h(App),
+}).$mount("#app");

+ 13 - 20
src/router/index.js

@@ -1,3 +1,4 @@
+// router/index.js
 import Vue from "vue";
 import VueRouter from "vue-router";
 import Home from "../views/home/Index.vue";
@@ -20,13 +21,15 @@ const routes = [
         path: "cockpitManage",
         name: "cockpitManage",
         component: () => import("../views/admin/cockpitManage/Index.vue"),
-      },
-      // 电子地图
-      {
-        path: "electronic-map",
-        name: "electronicMap",
-        component: () =>
-          import("../views/admin/cockpitManage/electronicMap.vue"),
+        children: [
+          // 电子地图
+          {
+            path: "electronic-map",
+            name: "electronicMap",
+            component: () =>
+              import("../views/admin/cockpitManage/electronicMap.vue"),
+          },
+        ],
       },
     ],
   },
@@ -35,28 +38,18 @@ const routes = [
     name: "login",
     component: () => import("../views/login/Index.vue"),
   },
-
-  {
-    path: "/:pathMatch(.*)*",
-    component: () => import("@/views/error/404.vue"),
-    meta: { hidden: true },
-  },
-  // {
-  //   path: '*',
-  //   redirect: '/'
-  // }
 ];
 
 const router = new VueRouter({
   mode: "history",
   base: process.env.BASE_URL,
-  scrollBehavior: () => ({
-    y: 0,
-  }),
+  scrollBehavior: () => ({ y: 0 }),
   routes,
 });
+
 const originalPush = VueRouter.prototype.push;
 VueRouter.prototype.push = function push(location) {
   return originalPush.call(this, location).catch((err) => err);
 };
+
 export default router;

+ 44 - 16
src/store/auth.js

@@ -1,38 +1,66 @@
-/*
- * @Author: your name
- * @Date: 2024-05-17 14:57:38
- * @LastEditTime: 2024-05-17 16:21:14
- * @LastEditors: bogon
- * @Description: In User Settings Edit
- * @FilePath: /dasheng/performance-test/src/store/auth.js
- */
 import { login } from "../api/login";
-import routerInit from "../router/index";
 import { getAuthRouterFn } from "@/utils/getAuth";
+
 export default {
-  namespaced: true, //子模块一定要开启命名空间
+  namespaced: true, // 子模块一定要开启命名空间
   state: {
     userInfo: {},
+    // routes: [],
   },
   getters: {},
   mutations: {
     SET_USERINFO(state, payload) {
       state.userInfo = payload;
     },
+    // SET_ROUTES(state, routes) {
+    //   state.routes = routes;
+    // },
   },
   actions: {
-    async goLogin({ commit, dispatch }, obj) {
+    async goLogin({ commit, dispatch }, { loginForm, router }) {
       try {
-        const result = await login(obj);
+        const result = await login(loginForm);
         commit("SET_USERINFO", result.data);
-        dispatch("setAddRouter", result.data.permission);
+        dispatch("setAddRouter", {
+          resultRouter: result.data.permission,
+          router,
+        });
       } catch (error) {
         console.error("Login error:", error);
       }
     },
-    setAddRouter({ commit, dispatch }, router) {
-      console.log(router, "router");
-      getAuthRouterFn(router);
+    setAddRouter({ commit }, { resultRouter, router }) {
+      console.log("1111");
+      const { authRouterList } = getAuthRouterFn(resultRouter);
+      // 获取home路由
+      const homeRoute = router.options.routes.find(
+        (route) => route.name === "home"
+      );
+      // 确保home路由存在
+      if (homeRoute) {
+        if (!homeRoute.children) {
+          homeRoute.children = [];
+        }
+        authRouterList.forEach((item) => {
+          // 动态路由的父路径设置为home
+          homeRoute.children.push(item);
+        });
+        // 重新添加home路由
+        router.addRoute(homeRoute);
+      }
+      // 添加404页面路由
+      router.addRoute({
+        path: "/:pathMatch(.*)*",
+        name: "NotFound",
+        component: () => import("@/views/error/404.vue"),
+        meta: { hidden: true },
+      });
+      console.log("router", router.getRoutes());
+      // 持久化存储动态路由
+      // commit("SET_ROUTES", homeRoute.children);
+      // localStorage.setItem("authRoutes", JSON.stringify(homeRoute.children));
+      // 确保导航到动态添加的路由
+      router.push("/home/system/userMag");
     },
   },
 };

+ 2 - 0
src/store/index.js

@@ -3,6 +3,7 @@ import Vue from "vue";
 import breadStore from "./breadStore.js"; //引入子模块
 import auth from "./auth.js";
 import createPersistedState from "vuex-persistedstate";
+import settings from "./settings.js";
 Vue.use(Vuex);
 export const store = new Vuex.Store({
   state: {
@@ -29,6 +30,7 @@ export const store = new Vuex.Store({
     //面包屑模块
     breadStore,
     auth,
+    settings,
   },
   plugins: [
     createPersistedState({

+ 35 - 0
src/store/settings.js

@@ -0,0 +1,35 @@
+const storageSetting = JSON.parse(localStorage.getItem("layout-setting")) || "";
+const state = {
+  title: "",
+  theme: storageSetting.theme || "#409EFF",
+  loading: false,
+};
+const mutations = {
+  CHANGE_SETTING: (state, { key, value }) => {
+    if (state.hasOwnProperty(key)) {
+      state[key] = value;
+    }
+  },
+  changeLoading(state, { key, value }) {
+    state.loading = value;
+  },
+};
+
+const actions = {
+  // 修改布局设置
+  changeSetting({ commit }, data) {
+    commit("CHANGE_SETTING", data);
+  },
+
+  // 设置网页标题
+  setTitle({ commit }, title) {
+    state.title = title;
+  },
+};
+
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 13 - 16
src/utils/getAuth.js

@@ -1,7 +1,7 @@
 /*
  * @Author: your name
  * @Date: 2024-05-17 16:09:03
- * @LastEditTime: 2024-05-17 17:45:31
+ * @LastEditTime: 2024-05-18 18:40:13
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /performance-test/src/utils/getAuth.js
@@ -9,11 +9,10 @@
 import { orgList } from "@/views/home/components/mockData";
 //返回可动态添加的路由
 export const getAuthRouterFn = (list) => {
-  console.log(list, orgList, "权限路由 前后端");
   // 将list 转成一维数组,按钮级别权限拿到 返回为[]格式
   const { result, anthBtnList } = flattenTree(list);
   const arr = filterTreeByPermissions(result, orgList);
-  console.log(arr, "routes");
+  return { authRouterList: arr, anthBtnList };
 };
 
 const flattenTree = (tree) => {
@@ -35,26 +34,24 @@ const flattenTree = (tree) => {
   return { result, anthBtnList };
 };
 const filterTreeByPermissions = (permissions, tree) => {
-  // 将 permissions 转换成一个 Set 以便于快速查找
   const permissionSet = new Set(permissions.map((item) => item.permissionName));
 
   const filterTreeRecursive = (nodes) => {
-    const filteredNodes = [];
+    return nodes.reduce((filteredNodes, node) => {
+      const newNode = { ...node };
 
-    nodes.forEach((node) => {
-      // 检查当前节点是否在权限列表中
-      if (permissionSet.has(node.name)) {
-        // 创建一个新的节点对象,避免修改原始树
-        const newNode = { ...node };
-        // 如果节点有子节点,则递归过滤子节点
-        if (node.children && node.children.length > 0) {
-          newNode.children = filterTreeRecursive(node.children);
-        }
+      if (newNode.children && newNode.children.length > 0) {
+        newNode.children = filterTreeRecursive(newNode.children);
+      }
+      if (
+        permissionSet.has(newNode.name) ||
+        (newNode.children && newNode.children.length > 0)
+      ) {
         filteredNodes.push(newNode);
       }
-    });
 
-    return filteredNodes;
+      return filteredNodes;
+    }, []);
   };
 
   return filterTreeRecursive(tree);

+ 140 - 77
src/views/home/components/Menu.vue

@@ -2,61 +2,125 @@
   <el-aside :width="!isCollapse ? '300px' : '100px'" class="asideBox">
     <div class="homeBox">
       <div class="logoItem" :class="{ 'flexCenter ': isCollapse }">
-        <div class="logoImg" v-show="isCollapse" @click="
-          handleChangeMenuUrl(
-            { name: '驾驶舱', id: 1, path: 'cockpitManage', iconName: 'gps' },
-            `/home/cockpitManage?name=驾驶舱`
-          )
-          ">
-          <SvgIcon name="dnao" class="dnao" width="40px" height="40px" color="#222"></SvgIcon>
+        <div
+          class="logoImg"
+          v-show="isCollapse"
+          @click="
+            handleChangeMenuUrl(
+              { name: '驾驶舱', id: 1, path: 'cockpitManage', iconName: 'gps' },
+              `/home/cockpitManage?name=驾驶舱`
+            )
+          "
+        >
+          <SvgIcon
+            name="dnao"
+            class="dnao"
+            width="40px"
+            height="40px"
+            color="#222"
+          ></SvgIcon>
         </div>
-        <span v-if="!isCollapse" @click="
-          handleChangeMenuUrl(
-            { name: '驾驶舱', id: 1, path: 'cockpitManage', iconName: 'gps' },
-            `/home/cockpitManage?name=驾驶舱`
-          )
-          " :class="isCollapse ? 'stop-animation' : 'active-animation'">风机运行管理平台</span>
+        <span
+          v-if="!isCollapse"
+          @click="
+            handleChangeMenuUrl(
+              { name: '驾驶舱', id: 1, path: 'cockpitManage', iconName: 'gps' },
+              `/home/cockpitManage?name=驾驶舱`
+            )
+          "
+          :class="isCollapse ? 'stop-animation' : 'active-animation'"
+          >风机运行管理平台</span
+        >
       </div>
     </div>
-    <el-menu ref="menu" class="mt-3 el-menu-vertical-demo" @open="handleOpen" @select="handleOpen" @close="handleClose"
-      background-color="#eff1f3" text-color="#000" active-text-color="#0754a1" :router="false"
-      :default-active="defaultActive" :unique-opened="true" :collapse="isCollapse">
-      <el-submenu v-for="(item, index) in orgList" :key="index" :index="item.id">
+    <el-menu
+      ref="menu"
+      class="mt-3 el-menu-vertical-demo"
+      @open="handleOpen"
+      @select="handleOpen"
+      @close="handleClose"
+      background-color="#eff1f3"
+      text-color="#000"
+      active-text-color="#0754a1"
+      :router="false"
+      :default-active="defaultActive"
+      :unique-opened="true"
+      :collapse="isCollapse"
+    >
+      <el-submenu
+        v-for="(item, index) in orgList"
+        :key="index"
+        :index="item.id"
+      >
         <template slot="title">
           <div class="flex items-center justify-start">
-            <i class="flex items-center justify-center mr-2" style="margin-left: 3px">
-              <SvgIcon :name="item.iconName" class="dnao" width="20px" height="22px" color="#222"></SvgIcon>
+            <i
+              class="flex items-center justify-center mr-2"
+              style="margin-left: 3px"
+            >
+              <SvgIcon
+                :name="item.iconName"
+                class="dnao"
+                width="20px"
+                height="22px"
+                color="#222"
+              ></SvgIcon>
             </i>
-            <span slot="title" style="display: inline-block" :class="{
-              'active-menu':
-                defaultActive === `/windField?id=${item.id}&name=${item.name}`
-            }">{{ item.name }}</span>
+            <span
+              slot="title"
+              style="display: inline-block"
+              :class="{
+                'active-menu':
+                  defaultActive ===
+                  `/windField?id=${item.id}&name=${item.name}`,
+              }"
+              >{{ item.name }}</span
+            >
           </div>
         </template>
-        <el-menu-item v-for="(child, childIndex) in item.children" :key="childIndex"
-          :index="`/home/${child.path}?id=${child.id}&name=${child.name}`">
+        <el-menu-item
+          v-for="(child, childIndex) in item.children"
+          :key="childIndex"
+          :index="`/home/${item.path}/${child.path}?id=${child.id}&name=${child.name}`"
+        >
           <template slot="title">
-            <span slot="title" style="padding-left: 22px; width: 100%; display: inline-block" :class="{
-              'active-menu':
-                defaultActive === `/dynamo?id=${item.id}&name=${item.name}`
-            }" @click="
+            <span
+              slot="title"
+              style="padding-left: 22px; width: 100%; display: inline-block"
+              :class="{
+                'active-menu':
+                  defaultActive === `/dynamo?id=${item.id}&name=${item.name}`,
+              }"
+              @click="
                 handleChangeMenuUrl(
                   item,
-                  `/home/${child.path}?id=${item.id}&name=${item.name}`
+                  `/home/${item.path}/${child.path}?id=${item.id}&name=${item.name}`
                 )
-                ">{{ child.name }}</span>
+              "
+              >{{ child.name }}</span
+            >
           </template>
         </el-menu-item>
       </el-submenu>
     </el-menu>
     <div class="foldBox flexCenter">
-      <span v-show="isCollapse" class="el-icon-s-unfold icon" @click="isCollapse = false">展开</span>
-      <span v-show="!isCollapse" class="el-icon-s-fold icon" @click="isCollapse = true">收起</span>
+      <span
+        v-show="isCollapse"
+        class="el-icon-s-unfold icon"
+        @click="isCollapse = false"
+        >展开</span
+      >
+      <span
+        v-show="!isCollapse"
+        class="el-icon-s-fold icon"
+        @click="isCollapse = true"
+        >收起</span
+      >
     </div>
   </el-aside>
 </template>
 <script>
-import { orgList } from './mockData'
+import { orgList } from "./mockData";
 
 export default {
   data() {
@@ -66,17 +130,17 @@ export default {
       // 三级菜单数组
       orgList: orgList,
       // 搜索值
-      searchInputValue: '',
-      defaultActive: '',
+      searchInputValue: "",
+      defaultActive: "",
       activeIndex: false,
-      keyObject: {}
-    }
+      keyObject: {},
+    };
   },
 
   computed: {
     currentMenuIndex() {
-      return this.$store.state.breadStore?.currentUrl?.routeUrl
-    }
+      return this.$store.state.breadStore?.currentUrl?.routeUrl;
+    },
   },
 
   watch: {
@@ -84,76 +148,76 @@ export default {
       deep: true,
       handler(newVale, oldVal) {
         if (newVale) {
-          this.$refs.menu.close(newVale)
+          this.$refs.menu.close(newVale);
         }
-      }
+      },
     },
     keyObject: {
       deep: true,
       handler(newVale) {
         if (newVale && newVale.key === this.defaultActive) {
-          this.getBreadcrumbList(newVale.keyPath)
+          this.getBreadcrumbList(newVale.keyPath);
         }
-      }
-    }
+      },
+    },
   },
-  created() { },
+  created() {},
   methods: {
     getBreadcrumbList(keyPath) {
-      const urls = keyPath
+      const urls = keyPath;
       const result = urls.map((url, index) => {
-        const params = new URLSearchParams(url.split('?')[1]) // 解析 URL 参数
-        const id = params.get('id')
-        const name = params.get('name')
-        const routeUrl = url
+        const params = new URLSearchParams(url.split("?")[1]); // 解析 URL 参数
+        const id = params.get("id");
+        const name = params.get("name");
+        const routeUrl = url;
         // 如果是最后一个路由,则添加额外属性
         if (index === urls.length - 1) {
-          return { id, name, routeUrl, currentPage: true }
+          return { id, name, routeUrl, currentPage: true };
         }
-        return { id, name, routeUrl }
-      })
+        return { id, name, routeUrl };
+      });
       // console.log(result)
-      this.$store.commit('breadStore/ADD_BREAD', result)
-      return result
+      this.$store.commit("breadStore/ADD_BREAD", result);
+      return result;
     },
     handleOpen(key, keyPath) {
       // console.log(key, keyPath)
-      this.activeIndex = false
+      this.activeIndex = false;
       this.keyObject = {
         key,
-        keyPath
-      }
+        keyPath,
+      };
     },
     handleClose(key, keyPath) {
       // console.log(key, keyPath)
-      this.activeIndex = false
+      this.activeIndex = false;
       this.keyObject = {
         key,
-        keyPath
-      }
+        keyPath,
+      };
     },
 
     shrinkTree() {
-      this.$refs.menu.close(`/orgsPage?id=${orgList.id}&name=${orgList.name}`)
+      this.$refs.menu.close(`/orgsPage?id=${orgList.id}&name=${orgList.name}`);
     },
     handleChangeRouter() {
-      this.activeIndex = true
-      this.defaultActive = ''
-      this.$store.commit('breadStore/ADD_BREAD', [])
-      this.$refs.menu.close(`/orgsPage?id=${orgList.id}&name=${orgList.name}`)
-      this.$router.push('/')
+      this.activeIndex = true;
+      this.defaultActive = "";
+      this.$store.commit("breadStore/ADD_BREAD", []);
+      this.$refs.menu.close(`/orgsPage?id=${orgList.id}&name=${orgList.name}`);
+      this.$router.push("/");
     },
     handleChangeMenuUrl(item, key) {
-      this.defaultActive = key
+      this.defaultActive = key;
       if (item) {
-        item.activeIndex = true
+        item.activeIndex = true;
       }
       this.$router.push({
-        path: key
-      })
-    }
-  }
-}
+        path: key,
+      });
+    },
+  },
+};
 </script>
 <style lang="scss" scoped>
 .el-menu-vertical-demo:not(.el-menu--collapse) {
@@ -272,8 +336,7 @@ export default {
         }
 
         // background-color: rgb(64, 158, 255, 0.6);
-        transition: width 0.5s ease,
-        transform 0.5s ease;
+        transition: width 0.5s ease, transform 0.5s ease;
         /* 过渡效果 */
         transform-origin: left;
         /* 设置变换的原点在左侧 */

+ 2 - 2
src/views/home/components/mockData.js

@@ -79,7 +79,7 @@ export const orgList = [
     children: [
       {
         id: 51,
-        name: "员工管理",
+        name: "用户管理",
         path: "userMag",
         component: () => import("@/views/system/userMag/index.vue"),
       },
@@ -91,7 +91,7 @@ export const orgList = [
       },
       {
         id: 53,
-        name: "菜单管理",
+        name: "菜单权限",
         path: "menuMag",
         component: () => import("@/views/system/menuMag/index.vue"),
       },

+ 4 - 1
src/views/login/Index.vue

@@ -77,7 +77,10 @@ export default {
       // 自带校验
       this.$refs[formName].validate((valid) => {
         if (valid) {
-          this.$store.dispatch("auth/goLogin", this.loginForm);
+          this.$store.dispatch("auth/goLogin", {
+            loginForm: this.loginForm,
+            router: this.$router,
+          });
           // this.$router.push('/')
         } else {
           console.log("error submit!!");

+ 4 - 2
src/views/system/index.vue

@@ -1,13 +1,15 @@
 <!--
  * @Author: your name
  * @Date: 2024-05-17 16:02:11
- * @LastEditTime: 2024-05-17 16:02:46
+ * @LastEditTime: 2024-05-19 22:59:28
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /dasheng/performance-test/src/views/system/index.vue
 -->
 <template>
-  <div>权限管理</div>
+  <div>
+    <router-view />
+  </div>
 </template>
 <script></script>
 <style scoped lang="scss"></style>

+ 84 - 4
src/views/system/menuMag/index.vue

@@ -1,13 +1,93 @@
 <!--
  * @Author: your name
  * @Date: 2024-05-17 15:59:21
- * @LastEditTime: 2024-05-17 15:59:21
+ * @LastEditTime: 2024-05-19 22:59:39
  * @LastEditors: bogon
  * @Description: In User Settings Edit
  * @FilePath: /dasheng/performance-test/src/views/system/menuMag/index.vue
 -->
 <template>
-  <div>菜单管理</div>
+  <div>
+    <el-table :data="tableData" style="width: 100%">
+      <el-table-column label="日期" width="180">
+        <template slot-scope="scope">
+          <i class="el-icon-time"></i>
+          <span style="margin-left: 10px">{{ scope.row.date }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="姓名" width="180">
+        <template slot-scope="scope">
+          <el-popover trigger="hover" placement="top">
+            <p>姓名: {{ scope.row.name }}</p>
+            <p>住址: {{ scope.row.address }}</p>
+            <div slot="reference" class="name-wrapper">
+              <el-tag size="medium">{{ scope.row.name }}</el-tag>
+            </div>
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作">
+        <template slot-scope="scope">
+          <el-button size="mini" @click="handleEdit(scope.$index, scope.row)"
+            >编辑</el-button
+          >
+          <el-button
+            size="mini"
+            type="danger"
+            @click="handleDelete(scope.$index, scope.row)"
+            >删除</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
 </template>
-<script></script>
-<style lang="scss"></style>
+<script>
+export default {
+  data() {
+    return {
+      tableData: [
+        {
+          date: "2016-05-02",
+          name: "王小虎",
+          address: "上海市普陀区金沙江路 1518 弄",
+        },
+        {
+          date: "2016-05-04",
+          name: "王小虎",
+          address: "上海市普陀区金沙江路 1517 弄",
+        },
+        {
+          date: "2016-05-01",
+          name: "王小虎",
+          address: "上海市普陀区金沙江路 1519 弄",
+        },
+        {
+          date: "2016-05-03",
+          name: "王小虎",
+          address: "上海市普陀区金沙江路 1516 弄",
+        },
+      ],
+    };
+  },
+  methods: {
+    handleEdit(index, row) {
+      console.log(index, row);
+    },
+    handleDelete(index, row) {
+      console.log(index, row);
+    },
+  },
+};
+</script>
+<!-- <style lang="scss">
+// .box {
+//   width: 100px;
+//   height: 100px;
+//   background-color: var(--color);
+// }
+.el-table th.el-table__cell {
+  background-color: var(--color);
+  color: aliceblue;
+}
+</style> -->

+ 2 - 1
vue.config.js

@@ -33,7 +33,8 @@ module.exports = {
   devServer: {
     proxy: {
       "/api": {
-        target: "http://192.168.50.235:16200",
+        // target: "http://192.168.50.235:16200",
+        target: "http://106.120.102.238:16600",
         changeOrigin: true,
         pathRewrite: {
           "^/api": "", //需要rewrite重写的,