mirror of
				https://github.com/pure-admin/vue-pure-admin.git
				synced 2025-11-03 13:44:47 +08:00 
			
		
		
		
	
							parent
							
								
									a2dde02994
								
							
						
					
					
						commit
						ef05b2b614
					
				@ -126,13 +126,13 @@ If you think this project is helpful to you, you can help the author buy a cup o
 | 
			
		||||
 | 
			
		||||
## License
 | 
			
		||||
 | 
			
		||||
In principle, no fees and copyrights are charged, so you can use it with confidence
 | 
			
		||||
In principle, no fees and copyrights are charged, and you can use it with confidence, but if you need secondary open source, please contact the author for permission!
 | 
			
		||||
 | 
			
		||||
[MIT © xiaoxian521-2020](./LICENSE)
 | 
			
		||||
 | 
			
		||||
## Backers
 | 
			
		||||
 | 
			
		||||
Thank you very much for your support, I believe the project will get better and better! ! ! :heart:
 | 
			
		||||
Thank you very much for your support, I believe the project will get better and better :heart:
 | 
			
		||||
 | 
			
		||||
|                                                                xueyuheng                                                                 |                                                                taolei1990                                                                 |                                                                hang-kim                                                                 |                                                               madwolfcrazy                                                                |                                                                limuen                                                                 |
 | 
			
		||||
| :--------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: |
 | 
			
		||||
@ -140,6 +140,6 @@ Thank you very much for your support, I believe the project will get better and
 | 
			
		||||
 | 
			
		||||
## Contributors
 | 
			
		||||
 | 
			
		||||
This project exists thanks to all the people who contribute!!! :heart:
 | 
			
		||||
This project exists thanks to all the people who contribute :heart:
 | 
			
		||||
 | 
			
		||||
<a href="https://github.com/xiaoxian521/vue-pure-admin/graphs/contributors"><img src="https://contrib.rocks/image?repo=xiaoxian521/vue-pure-admin" /></a>
 | 
			
		||||
 | 
			
		||||
@ -132,13 +132,13 @@ pnpm build
 | 
			
		||||
 | 
			
		||||
## 许可证
 | 
			
		||||
 | 
			
		||||
原则上不收取任何费用及版权,可以放心使用
 | 
			
		||||
原则上不收取任何费用及版权,可以放心使用,不过如需二次开源(比如用此平台二次开发并开源)请联系作者获取许可!
 | 
			
		||||
 | 
			
		||||
[MIT © xiaoxian521-2020](./LICENSE)
 | 
			
		||||
 | 
			
		||||
## 捐赠者
 | 
			
		||||
 | 
			
		||||
非常感谢你们的支持,相信项目会越来越好!!!:heart:
 | 
			
		||||
非常感谢你们的支持,相信项目会越来越好 :heart:
 | 
			
		||||
 | 
			
		||||
|                                                                xueyuheng                                                                 |                                                                taolei1990                                                                 |                                                                hang-kim                                                                 |                                                               madwolfcrazy                                                                |                                                                limuen                                                                 |
 | 
			
		||||
| :--------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------: |
 | 
			
		||||
@ -146,6 +146,6 @@ pnpm build
 | 
			
		||||
 | 
			
		||||
## 贡献者
 | 
			
		||||
 | 
			
		||||
这个项目的存在感谢所有做出贡献的人!!!:heart:
 | 
			
		||||
这个项目的存在感谢所有做出贡献的人 :heart:
 | 
			
		||||
 | 
			
		||||
<a href="https://github.com/xiaoxian521/vue-pure-admin/graphs/contributors"><img src="https://contrib.rocks/image?repo=xiaoxian521/vue-pure-admin" /></a>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								package.json
									
									
									
									
									
								
							@ -33,17 +33,17 @@
 | 
			
		||||
    "@ctrl/tinycolor": "^3.4.0",
 | 
			
		||||
    "@logicflow/core": "0.7.1",
 | 
			
		||||
    "@logicflow/extension": "0.7.1",
 | 
			
		||||
    "@vueuse/core": "^7.5.5",
 | 
			
		||||
    "@vueuse/core": "^7.6.2",
 | 
			
		||||
    "@vueuse/motion": "^2.0.0-beta.9",
 | 
			
		||||
    "@vueuse/shared": "^7.5.5",
 | 
			
		||||
    "@vueuse/shared": "^7.6.2",
 | 
			
		||||
    "animate.css": "^4.1.1",
 | 
			
		||||
    "axios": "^0.25.0",
 | 
			
		||||
    "cropperjs": "^1.5.11",
 | 
			
		||||
    "css-color-function": "^1.3.3",
 | 
			
		||||
    "dayjs": "^1.10.7",
 | 
			
		||||
    "driver.js": "^0.9.8",
 | 
			
		||||
    "echarts": "^5.2.1",
 | 
			
		||||
    "element-plus": "^2.0.0",
 | 
			
		||||
    "echarts": "^5.3.0",
 | 
			
		||||
    "element-plus": "^2.0.2",
 | 
			
		||||
    "element-resize-detector": "^1.2.3",
 | 
			
		||||
    "js-cookie": "^3.0.1",
 | 
			
		||||
    "lodash-es": "^4.17.21",
 | 
			
		||||
@ -57,7 +57,7 @@
 | 
			
		||||
    "responsive-storage": "^1.0.11",
 | 
			
		||||
    "rgb-hex": "^4.0.0",
 | 
			
		||||
    "v-contextmenu": "3.0.0",
 | 
			
		||||
    "vue": "^3.2.29",
 | 
			
		||||
    "vue": "^3.2.31",
 | 
			
		||||
    "vue-i18n": "^9.2.0-beta.30",
 | 
			
		||||
    "vue-json-pretty": "^2.0.2",
 | 
			
		||||
    "vue-router": "^4.0.12",
 | 
			
		||||
@ -84,9 +84,9 @@
 | 
			
		||||
    "@types/qs": "^6.9.7",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^5.10.2",
 | 
			
		||||
    "@typescript-eslint/parser": "^5.10.2",
 | 
			
		||||
    "@vitejs/plugin-legacy": "^1.6.4",
 | 
			
		||||
    "@vitejs/plugin-vue": "^2.1.0",
 | 
			
		||||
    "@vitejs/plugin-vue-jsx": "^1.3.3",
 | 
			
		||||
    "@vitejs/plugin-legacy": "^1.7.0",
 | 
			
		||||
    "@vitejs/plugin-vue": "^2.2.0",
 | 
			
		||||
    "@vitejs/plugin-vue-jsx": "^1.3.4",
 | 
			
		||||
    "@vue/eslint-config-prettier": "^7.0.0",
 | 
			
		||||
    "@vue/eslint-config-typescript": "^10.0.0",
 | 
			
		||||
    "@zougt/vite-plugin-theme-preprocessor": "^1.4.4",
 | 
			
		||||
@ -97,7 +97,7 @@
 | 
			
		||||
    "eslint-plugin-vue": "^8.4.1",
 | 
			
		||||
    "husky": "^7.0.4",
 | 
			
		||||
    "lint-staged": "11.1.2",
 | 
			
		||||
    "postcss": "8.2.6",
 | 
			
		||||
    "postcss": "^8.4.6",
 | 
			
		||||
    "postcss-html": "^1.3.0",
 | 
			
		||||
    "postcss-import": "14.0.0",
 | 
			
		||||
    "postcss-scss": "^4.0.3",
 | 
			
		||||
@ -115,12 +115,12 @@
 | 
			
		||||
    "stylelint-order": "^5.0.0",
 | 
			
		||||
    "typescript": "^4.5.5",
 | 
			
		||||
    "unplugin-element-plus": "^0.2.0",
 | 
			
		||||
    "vite": "^2.7.13",
 | 
			
		||||
    "vite": "^2.8.1",
 | 
			
		||||
    "vite-plugin-live-reload": "^2.1.0",
 | 
			
		||||
    "vite-plugin-mock": "^2.9.6",
 | 
			
		||||
    "vite-plugin-remove-console": "^0.0.6",
 | 
			
		||||
    "vite-plugin-style-import": "^1.4.1",
 | 
			
		||||
    "vite-plugin-windicss": "^1.6.1",
 | 
			
		||||
    "vite-plugin-style-import": "1.4.1",
 | 
			
		||||
    "vite-plugin-windicss": "^1.7.0",
 | 
			
		||||
    "vite-svg-loader": "2.2.0",
 | 
			
		||||
    "vue-eslint-parser": "^8.2.0",
 | 
			
		||||
    "windicss": "^3.4.3"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1216
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1216
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,71 +1,43 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { useI18n } from "vue-i18n";
 | 
			
		||||
import { emitter } from "/@/utils/mitt";
 | 
			
		||||
import { useNav } from "../hooks/nav";
 | 
			
		||||
import { useRoute } from "vue-router";
 | 
			
		||||
import Notice from "./notice/index.vue";
 | 
			
		||||
import mixNav from "./sidebar/mixNav.vue";
 | 
			
		||||
import avatars from "/@/assets/avatars.jpg";
 | 
			
		||||
import { transformI18n } from "/@/plugins/i18n";
 | 
			
		||||
import Hamburger from "./sidebar/hamBurger.vue";
 | 
			
		||||
import { useRouter, useRoute } from "vue-router";
 | 
			
		||||
import { storageSession } from "/@/utils/storage";
 | 
			
		||||
import { watch, getCurrentInstance } from "vue";
 | 
			
		||||
import Breadcrumb from "./sidebar/breadCrumb.vue";
 | 
			
		||||
import { useAppStoreHook } from "/@/store/modules/app";
 | 
			
		||||
import { unref, watch, getCurrentInstance } from "vue";
 | 
			
		||||
import { deviceDetection } from "/@/utils/deviceDetection";
 | 
			
		||||
import screenfull from "../components/screenfull/index.vue";
 | 
			
		||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
 | 
			
		||||
import globalization from "/@/assets/svg/globalization.svg?component";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const { locale } = useI18n();
 | 
			
		||||
const instance =
 | 
			
		||||
  getCurrentInstance().appContext.config.globalProperties.$storage;
 | 
			
		||||
const pureApp = useAppStoreHook();
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
let usename = storageSession.getItem("info")?.username;
 | 
			
		||||
const { locale } = useI18n();
 | 
			
		||||
 | 
			
		||||
const getDropdownItemStyle = computed(() => {
 | 
			
		||||
  return t => {
 | 
			
		||||
    return {
 | 
			
		||||
      background: locale.value === t ? useEpThemeStoreHook().epThemeColor : "",
 | 
			
		||||
      color: locale.value === t ? "#f4f4f5" : "#000"
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
const {
 | 
			
		||||
  logout,
 | 
			
		||||
  onPanel,
 | 
			
		||||
  changeTitle,
 | 
			
		||||
  toggleSideBar,
 | 
			
		||||
  pureApp,
 | 
			
		||||
  usename,
 | 
			
		||||
  getDropdownItemStyle
 | 
			
		||||
} = useNav();
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => locale.value,
 | 
			
		||||
  () => {
 | 
			
		||||
    //@ts-ignore
 | 
			
		||||
    document.title = transformI18n(
 | 
			
		||||
      //@ts-ignore
 | 
			
		||||
      unref(route.meta.title),
 | 
			
		||||
      unref(route.meta.i18n)
 | 
			
		||||
    ); // 动态title
 | 
			
		||||
    changeTitle(route.meta);
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// 退出登录
 | 
			
		||||
const logout = (): void => {
 | 
			
		||||
  storageSession.removeItem("info");
 | 
			
		||||
  router.push("/login");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function onPanel() {
 | 
			
		||||
  emitter.emit("openPanel");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function toggleSideBar() {
 | 
			
		||||
  pureApp.toggleSideBar();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 简体中文
 | 
			
		||||
function translationCh() {
 | 
			
		||||
  instance.locale = { locale: "zh" };
 | 
			
		||||
  locale.value = "zh";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// English
 | 
			
		||||
function translationEn() {
 | 
			
		||||
  instance.locale = { locale: "en" };
 | 
			
		||||
  locale.value = "en";
 | 
			
		||||
@ -75,14 +47,17 @@ function translationEn() {
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="navbar">
 | 
			
		||||
    <Hamburger
 | 
			
		||||
      v-if="pureApp.layout !== 'mix'"
 | 
			
		||||
      :is-active="pureApp.sidebar.opened"
 | 
			
		||||
      class="hamburger-container"
 | 
			
		||||
      @toggleClick="toggleSideBar"
 | 
			
		||||
    />
 | 
			
		||||
 | 
			
		||||
    <Breadcrumb class="breadcrumb-container" />
 | 
			
		||||
    <Breadcrumb v-if="pureApp.layout !== 'mix'" class="breadcrumb-container" />
 | 
			
		||||
 | 
			
		||||
    <div class="vertical-header-right">
 | 
			
		||||
    <mixNav v-if="pureApp.layout === 'mix'" />
 | 
			
		||||
 | 
			
		||||
    <div v-if="pureApp.layout === 'vertical'" class="vertical-header-right">
 | 
			
		||||
      <!-- 通知 -->
 | 
			
		||||
      <Notice id="header-notice" />
 | 
			
		||||
      <!-- 全屏 -->
 | 
			
		||||
@ -93,7 +68,7 @@ function translationEn() {
 | 
			
		||||
        <template #dropdown>
 | 
			
		||||
          <el-dropdown-menu class="translation">
 | 
			
		||||
            <el-dropdown-item
 | 
			
		||||
              :style="getDropdownItemStyle('zh')"
 | 
			
		||||
              :style="getDropdownItemStyle(locale, 'zh')"
 | 
			
		||||
              @click="translationCh"
 | 
			
		||||
              ><IconifyIconOffline
 | 
			
		||||
                class="check-zh"
 | 
			
		||||
@ -102,7 +77,7 @@ function translationEn() {
 | 
			
		||||
              />简体中文</el-dropdown-item
 | 
			
		||||
            >
 | 
			
		||||
            <el-dropdown-item
 | 
			
		||||
              :style="getDropdownItemStyle('en')"
 | 
			
		||||
              :style="getDropdownItemStyle(locale, 'en')"
 | 
			
		||||
              @click="translationEn"
 | 
			
		||||
              ><el-icon class="check-en" v-show="locale === 'en'"
 | 
			
		||||
                ><IconifyIconOffline icon="check" /></el-icon
 | 
			
		||||
 | 
			
		||||
@ -61,6 +61,7 @@ let themeColors = ref<Array<themeColorsType>>([
 | 
			
		||||
 | 
			
		||||
const verticalRef = templateRef<HTMLElement | null>("verticalRef", null);
 | 
			
		||||
const horizontalRef = templateRef<HTMLElement | null>("horizontalRef", null);
 | 
			
		||||
const mixRef = templateRef<HTMLElement | null>("mixRef", null);
 | 
			
		||||
 | 
			
		||||
let layoutTheme =
 | 
			
		||||
  ref(storageLocal.getItem("responsive-layout")) ||
 | 
			
		||||
@ -101,7 +102,7 @@ const getThemeColorStyle = computed(() => {
 | 
			
		||||
  };
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function changeStorageConfigure(key, val) {
 | 
			
		||||
function storageConfigureChange<T>(key: string, val: T): void {
 | 
			
		||||
  const storageConfigure = instance.configure;
 | 
			
		||||
  storageConfigure[key] = val;
 | 
			
		||||
  instance.configure = storageConfigure;
 | 
			
		||||
@ -117,7 +118,7 @@ function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
 | 
			
		||||
// 灰色模式设置
 | 
			
		||||
const greyChange = (value): void => {
 | 
			
		||||
  toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
 | 
			
		||||
  changeStorageConfigure("grey", value);
 | 
			
		||||
  storageConfigureChange("grey", value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 色弱模式设置
 | 
			
		||||
@ -127,29 +128,30 @@ const weekChange = (value): void => {
 | 
			
		||||
    "html-weakness",
 | 
			
		||||
    document.querySelector("html")
 | 
			
		||||
  );
 | 
			
		||||
  changeStorageConfigure("weak", value);
 | 
			
		||||
  storageConfigureChange("weak", value);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const tagsChange = () => {
 | 
			
		||||
  let showVal = settings.tabsVal;
 | 
			
		||||
  changeStorageConfigure("hideTabs", showVal);
 | 
			
		||||
  storageConfigureChange("hideTabs", showVal);
 | 
			
		||||
  emitter.emit("tagViewsChange", showVal);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const multiTagsCacheChange = () => {
 | 
			
		||||
  let multiTagsCache = settings.multiTagsCache;
 | 
			
		||||
  changeStorageConfigure("multiTagsCache", multiTagsCache);
 | 
			
		||||
  storageConfigureChange("multiTagsCache", multiTagsCache);
 | 
			
		||||
  useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// 清空缓存并返回登录页
 | 
			
		||||
function onReset() {
 | 
			
		||||
  toggleClass(getConfig().Grey, "html-grey", document.querySelector("html"));
 | 
			
		||||
  toggleClass(
 | 
			
		||||
    getConfig().Weak,
 | 
			
		||||
    "html-weakness",
 | 
			
		||||
    document.querySelector("html")
 | 
			
		||||
  );
 | 
			
		||||
  router.push("/login");
 | 
			
		||||
  const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
 | 
			
		||||
  useAppStoreHook().setLayout(Layout);
 | 
			
		||||
  useEpThemeStoreHook().setEpThemeColor(EpThemeColor);
 | 
			
		||||
  useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
 | 
			
		||||
  toggleClass(Grey, "html-grey", document.querySelector("html"));
 | 
			
		||||
  toggleClass(Weak, "html-weakness", document.querySelector("html"));
 | 
			
		||||
  useMultiTagsStoreHook().handleTags("equal", [
 | 
			
		||||
    {
 | 
			
		||||
      path: "/welcome",
 | 
			
		||||
@ -161,23 +163,20 @@ function onReset() {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  ]);
 | 
			
		||||
  useMultiTagsStoreHook().multiTagsCacheChange(getConfig().MultiTagsCache);
 | 
			
		||||
  useEpThemeStoreHook().setEpThemeColor(getConfig().EpThemeColor);
 | 
			
		||||
  storageLocal.clear();
 | 
			
		||||
  storageSession.clear();
 | 
			
		||||
  router.push("/login");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function onChange(label) {
 | 
			
		||||
  changeStorageConfigure("showModel", label);
 | 
			
		||||
  storageConfigureChange("showModel", label);
 | 
			
		||||
  emitter.emit("tagViewsShowModel", label);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 侧边栏Logo
 | 
			
		||||
function logoChange() {
 | 
			
		||||
  unref(logoVal)
 | 
			
		||||
    ? changeStorageConfigure("showLogo", true)
 | 
			
		||||
    : changeStorageConfigure("showLogo", false);
 | 
			
		||||
    ? storageConfigureChange("showLogo", true)
 | 
			
		||||
    : storageConfigureChange("showLogo", false);
 | 
			
		||||
  emitter.emit("logoChange", unref(logoVal));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -192,10 +191,17 @@ watch(instance, ({ layout }) => {
 | 
			
		||||
    case "vertical":
 | 
			
		||||
      toggleClass(true, isSelect, unref(verticalRef));
 | 
			
		||||
      debounce(setFalse([horizontalRef]), 50);
 | 
			
		||||
      debounce(setFalse([mixRef]), 50);
 | 
			
		||||
      break;
 | 
			
		||||
    case "horizontal":
 | 
			
		||||
      toggleClass(true, isSelect, unref(horizontalRef));
 | 
			
		||||
      debounce(setFalse([verticalRef]), 50);
 | 
			
		||||
      debounce(setFalse([mixRef]), 50);
 | 
			
		||||
      break;
 | 
			
		||||
    case "mix":
 | 
			
		||||
      toggleClass(true, isSelect, unref(mixRef));
 | 
			
		||||
      debounce(setFalse([verticalRef]), 50);
 | 
			
		||||
      debounce(setFalse([horizontalRef]), 50);
 | 
			
		||||
      break;
 | 
			
		||||
  }
 | 
			
		||||
});
 | 
			
		||||
@ -315,7 +321,7 @@ nextTick(() => {
 | 
			
		||||
 | 
			
		||||
    <el-divider>导航栏模式</el-divider>
 | 
			
		||||
    <ul class="pure-theme">
 | 
			
		||||
      <el-tooltip class="item" content="左侧菜单模式" placement="bottom">
 | 
			
		||||
      <el-tooltip class="item" content="左侧模式" placement="bottom">
 | 
			
		||||
        <li
 | 
			
		||||
          :class="layoutTheme.layout === 'vertical' ? $style.isSelect : ''"
 | 
			
		||||
          ref="verticalRef"
 | 
			
		||||
@ -326,7 +332,7 @@ nextTick(() => {
 | 
			
		||||
        </li>
 | 
			
		||||
      </el-tooltip>
 | 
			
		||||
 | 
			
		||||
      <el-tooltip class="item" content="顶部菜单模式" placement="bottom">
 | 
			
		||||
      <el-tooltip class="item" content="顶部模式" placement="bottom">
 | 
			
		||||
        <li
 | 
			
		||||
          :class="layoutTheme.layout === 'horizontal' ? $style.isSelect : ''"
 | 
			
		||||
          ref="horizontalRef"
 | 
			
		||||
@ -336,6 +342,17 @@ nextTick(() => {
 | 
			
		||||
          <div></div>
 | 
			
		||||
        </li>
 | 
			
		||||
      </el-tooltip>
 | 
			
		||||
 | 
			
		||||
      <el-tooltip class="item" content="混合模式" placement="bottom">
 | 
			
		||||
        <li
 | 
			
		||||
          :class="layoutTheme.layout === 'mix' ? $style.isSelect : ''"
 | 
			
		||||
          ref="mixRef"
 | 
			
		||||
          @click="setLayoutModel('mix')"
 | 
			
		||||
        >
 | 
			
		||||
          <div></div>
 | 
			
		||||
          <div></div>
 | 
			
		||||
        </li>
 | 
			
		||||
      </el-tooltip>
 | 
			
		||||
    </ul>
 | 
			
		||||
 | 
			
		||||
    <el-divider v-show="!dataTheme">主题色</el-divider>
 | 
			
		||||
@ -481,15 +498,14 @@ nextTick(() => {
 | 
			
		||||
.pure-theme {
 | 
			
		||||
  margin-top: 25px;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100px;
 | 
			
		||||
  height: 50px;
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-wrap: wrap;
 | 
			
		||||
  justify-content: space-around;
 | 
			
		||||
 | 
			
		||||
  li {
 | 
			
		||||
    margin: 10px;
 | 
			
		||||
    width: 36%;
 | 
			
		||||
    height: 70px;
 | 
			
		||||
    width: 18%;
 | 
			
		||||
    height: 45px;
 | 
			
		||||
    background: #f0f2f5;
 | 
			
		||||
    position: relative;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
@ -527,6 +543,27 @@ nextTick(() => {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    &:nth-child(3) {
 | 
			
		||||
      div {
 | 
			
		||||
        &:nth-child(1) {
 | 
			
		||||
          width: 100%;
 | 
			
		||||
          height: 30%;
 | 
			
		||||
          background: #1b2a47;
 | 
			
		||||
          box-shadow: 0 0 1px #888;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        &:nth-child(2) {
 | 
			
		||||
          width: 30%;
 | 
			
		||||
          height: 70%;
 | 
			
		||||
          bottom: 0;
 | 
			
		||||
          left: 0;
 | 
			
		||||
          background: #fff;
 | 
			
		||||
          box-shadow: 0 0 1px #888;
 | 
			
		||||
          position: absolute;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,126 +1,61 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import {
 | 
			
		||||
  computed,
 | 
			
		||||
  unref,
 | 
			
		||||
  watch,
 | 
			
		||||
  nextTick,
 | 
			
		||||
  onMounted,
 | 
			
		||||
  getCurrentInstance
 | 
			
		||||
} from "vue";
 | 
			
		||||
import { useI18n } from "vue-i18n";
 | 
			
		||||
import { emitter } from "/@/utils/mitt";
 | 
			
		||||
import { useNav } from "../../hooks/nav";
 | 
			
		||||
import Notice from "../notice/index.vue";
 | 
			
		||||
import { templateRef } from "@vueuse/core";
 | 
			
		||||
import SidebarItem from "./sidebarItem.vue";
 | 
			
		||||
import avatars from "/@/assets/avatars.jpg";
 | 
			
		||||
import screenfull from "../screenfull/index.vue";
 | 
			
		||||
import { useRoute, useRouter } from "vue-router";
 | 
			
		||||
import { storageSession } from "/@/utils/storage";
 | 
			
		||||
import { deviceDetection } from "/@/utils/deviceDetection";
 | 
			
		||||
import { watch, nextTick, onMounted, getCurrentInstance } from "vue";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
import globalization from "/@/assets/svg/globalization.svg?component";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const { locale } = useI18n();
 | 
			
		||||
const routers = useRouter().options.routes;
 | 
			
		||||
const menuRef = templateRef<ElRef | null>("menu", null);
 | 
			
		||||
const instance =
 | 
			
		||||
  getCurrentInstance().appContext.config.globalProperties.$storage;
 | 
			
		||||
 | 
			
		||||
const title =
 | 
			
		||||
  getCurrentInstance().appContext.config.globalProperties.$config?.Title;
 | 
			
		||||
 | 
			
		||||
const menuRef = templateRef<ElRef | null>("menu", null);
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const router = useRouter();
 | 
			
		||||
const routers = useRouter().options.routes;
 | 
			
		||||
let usename = storageSession.getItem("info")?.username;
 | 
			
		||||
const { locale, t } = useI18n();
 | 
			
		||||
const {
 | 
			
		||||
  logout,
 | 
			
		||||
  backHome,
 | 
			
		||||
  onPanel,
 | 
			
		||||
  changeTitle,
 | 
			
		||||
  handleResize,
 | 
			
		||||
  menuSelect,
 | 
			
		||||
  usename,
 | 
			
		||||
  getDropdownItemStyle
 | 
			
		||||
} = useNav();
 | 
			
		||||
 | 
			
		||||
const getDropdownItemStyle = computed(() => {
 | 
			
		||||
  return t => {
 | 
			
		||||
    return {
 | 
			
		||||
      background: locale.value === t ? "#1b2a47" : "",
 | 
			
		||||
      color: locale.value === t ? "#f4f4f5" : "#000"
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  nextTick(() => {
 | 
			
		||||
    handleResize(menuRef.value);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => locale.value,
 | 
			
		||||
  () => {
 | 
			
		||||
    //@ts-ignore
 | 
			
		||||
    // 动态title
 | 
			
		||||
    document.title = t(unref(route.meta.title));
 | 
			
		||||
    changeTitle(route.meta);
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// 退出登录
 | 
			
		||||
const logout = (): void => {
 | 
			
		||||
  storageSession.removeItem("info");
 | 
			
		||||
  router.push("/login");
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function onPanel() {
 | 
			
		||||
  emitter.emit("openPanel");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const activeMenu = computed((): string => {
 | 
			
		||||
  const { meta, path } = route;
 | 
			
		||||
  if (meta.activeMenu) {
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    return meta.activeMenu;
 | 
			
		||||
  }
 | 
			
		||||
  return path;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const menuSelect = (indexPath: string): void => {
 | 
			
		||||
  let parentPath = "";
 | 
			
		||||
  let parentPathIndex = indexPath.lastIndexOf("/");
 | 
			
		||||
  if (parentPathIndex > 0) {
 | 
			
		||||
    parentPath = indexPath.slice(0, parentPathIndex);
 | 
			
		||||
  }
 | 
			
		||||
  // 找到当前路由的信息
 | 
			
		||||
  function findCurrentRoute(routes) {
 | 
			
		||||
    return routes.map(item => {
 | 
			
		||||
      if (item.path === indexPath) {
 | 
			
		||||
        // 切换左侧菜单 通知标签页
 | 
			
		||||
        emitter.emit("changLayoutRoute", {
 | 
			
		||||
          indexPath,
 | 
			
		||||
          parentPath
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        if (item.children) findCurrentRoute(item.children);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  findCurrentRoute(routers);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function backHome() {
 | 
			
		||||
  router.push("/welcome");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleResize() {
 | 
			
		||||
  // @ts-ignore
 | 
			
		||||
  menuRef.value.handleResize();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 简体中文
 | 
			
		||||
function translationCh() {
 | 
			
		||||
  instance.locale = { locale: "zh" };
 | 
			
		||||
  locale.value = "zh";
 | 
			
		||||
  handleResize();
 | 
			
		||||
  handleResize(menuRef.value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// English
 | 
			
		||||
function translationEn() {
 | 
			
		||||
  instance.locale = { locale: "en" };
 | 
			
		||||
  locale.value = "en";
 | 
			
		||||
  handleResize();
 | 
			
		||||
  handleResize(menuRef.value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  nextTick(() => {
 | 
			
		||||
    handleResize();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
@ -135,12 +70,11 @@ onMounted(() => {
 | 
			
		||||
    </div>
 | 
			
		||||
    <el-menu
 | 
			
		||||
      ref="menu"
 | 
			
		||||
      :default-active="activeMenu"
 | 
			
		||||
      unique-opened
 | 
			
		||||
      router
 | 
			
		||||
      class="horizontal-header-menu"
 | 
			
		||||
      mode="horizontal"
 | 
			
		||||
      @select="menuSelect"
 | 
			
		||||
      :default-active="route.path"
 | 
			
		||||
      router
 | 
			
		||||
      @select="indexPath => menuSelect(indexPath, routers)"
 | 
			
		||||
    >
 | 
			
		||||
      <sidebar-item
 | 
			
		||||
        v-for="route in usePermissionStoreHook().wholeMenus"
 | 
			
		||||
@ -160,14 +94,14 @@ onMounted(() => {
 | 
			
		||||
        <template #dropdown>
 | 
			
		||||
          <el-dropdown-menu class="translation">
 | 
			
		||||
            <el-dropdown-item
 | 
			
		||||
              :style="getDropdownItemStyle('zh')"
 | 
			
		||||
              :style="getDropdownItemStyle(locale, 'zh')"
 | 
			
		||||
              @click="translationCh"
 | 
			
		||||
              ><el-icon class="check-zh" v-show="locale === 'zh'"
 | 
			
		||||
                ><IconifyIconOffline icon="check" /></el-icon
 | 
			
		||||
              >简体中文</el-dropdown-item
 | 
			
		||||
            >
 | 
			
		||||
            <el-dropdown-item
 | 
			
		||||
              :style="getDropdownItemStyle('en')"
 | 
			
		||||
              :style="getDropdownItemStyle(locale, 'en')"
 | 
			
		||||
              @click="translationEn"
 | 
			
		||||
              ><el-icon class="check-en" v-show="locale === 'en'"
 | 
			
		||||
                ><IconifyIconOffline icon="check" /></el-icon
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										239
									
								
								src/layout/components/sidebar/mixNav.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										239
									
								
								src/layout/components/sidebar/mixNav.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,239 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { useI18n } from "vue-i18n";
 | 
			
		||||
import Notice from "../notice/index.vue";
 | 
			
		||||
import { useNav } from "../../hooks/nav";
 | 
			
		||||
import { templateRef } from "@vueuse/core";
 | 
			
		||||
import avatars from "/@/assets/avatars.jpg";
 | 
			
		||||
import { transformI18n } from "/@/plugins/i18n";
 | 
			
		||||
import screenfull from "../screenfull/index.vue";
 | 
			
		||||
import { useRoute, useRouter } from "vue-router";
 | 
			
		||||
import { deviceDetection } from "/@/utils/deviceDetection";
 | 
			
		||||
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
 | 
			
		||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
 | 
			
		||||
import { getParentPaths, findRouteByPath } from "/@/router/utils";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
import globalization from "/@/assets/svg/globalization.svg?component";
 | 
			
		||||
import { ref, watch, nextTick, onMounted, getCurrentInstance } from "vue";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const { locale } = useI18n();
 | 
			
		||||
const routers = useRouter().options.routes;
 | 
			
		||||
const menuRef = templateRef<ElRef | null>("menu", null);
 | 
			
		||||
const instance =
 | 
			
		||||
  getCurrentInstance().appContext.config.globalProperties.$storage;
 | 
			
		||||
const wholeMenus = usePermissionStoreHook().wholeMenus;
 | 
			
		||||
 | 
			
		||||
const {
 | 
			
		||||
  logout,
 | 
			
		||||
  onPanel,
 | 
			
		||||
  changeTitle,
 | 
			
		||||
  toggleSideBar,
 | 
			
		||||
  handleResize,
 | 
			
		||||
  menuSelect,
 | 
			
		||||
  resolvePath,
 | 
			
		||||
  pureApp,
 | 
			
		||||
  usename,
 | 
			
		||||
  getDropdownItemStyle
 | 
			
		||||
} = useNav();
 | 
			
		||||
 | 
			
		||||
let defaultActive = ref(null);
 | 
			
		||||
 | 
			
		||||
function getDefaultActive(routePath) {
 | 
			
		||||
  // 当前路由的父级路径
 | 
			
		||||
  const parentRoutes = getParentPaths(routePath, wholeMenus)[0];
 | 
			
		||||
  defaultActive.value = findRouteByPath(
 | 
			
		||||
    parentRoutes,
 | 
			
		||||
    wholeMenus
 | 
			
		||||
  )?.children[0]?.path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  getDefaultActive(route.path);
 | 
			
		||||
  nextTick(() => {
 | 
			
		||||
    handleResize(menuRef.value);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => locale.value,
 | 
			
		||||
  () => {
 | 
			
		||||
    changeTitle(route.meta);
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => route.path,
 | 
			
		||||
  () => {
 | 
			
		||||
    getDefaultActive(route.path);
 | 
			
		||||
  }
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
function translationCh() {
 | 
			
		||||
  instance.locale = { locale: "zh" };
 | 
			
		||||
  locale.value = "zh";
 | 
			
		||||
  handleResize(menuRef.value);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function translationEn() {
 | 
			
		||||
  instance.locale = { locale: "en" };
 | 
			
		||||
  locale.value = "en";
 | 
			
		||||
  handleResize(menuRef.value);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="horizontal-header">
 | 
			
		||||
    <div
 | 
			
		||||
      :class="classes.container"
 | 
			
		||||
      :title="pureApp.sidebar.opened ? '点击折叠' : '点击展开'"
 | 
			
		||||
      @click="toggleSideBar"
 | 
			
		||||
    >
 | 
			
		||||
      <svg
 | 
			
		||||
        :fill="useEpThemeStoreHook().fill"
 | 
			
		||||
        :class="[
 | 
			
		||||
          'hamburger',
 | 
			
		||||
          pureApp.sidebar.opened ? 'is-active-hamburger' : ''
 | 
			
		||||
        ]"
 | 
			
		||||
        viewBox="0 0 1024 1024"
 | 
			
		||||
        xmlns="http://www.w3.org/2000/svg"
 | 
			
		||||
        width="64"
 | 
			
		||||
        height="64"
 | 
			
		||||
      >
 | 
			
		||||
        <path
 | 
			
		||||
          d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
 | 
			
		||||
        />
 | 
			
		||||
      </svg>
 | 
			
		||||
    </div>
 | 
			
		||||
    <el-menu
 | 
			
		||||
      ref="menu"
 | 
			
		||||
      class="horizontal-header-menu"
 | 
			
		||||
      mode="horizontal"
 | 
			
		||||
      :default-active="defaultActive"
 | 
			
		||||
      router
 | 
			
		||||
      @select="indexPath => menuSelect(indexPath, routers)"
 | 
			
		||||
    >
 | 
			
		||||
      <el-menu-item
 | 
			
		||||
        v-for="route in wholeMenus"
 | 
			
		||||
        :key="route.path"
 | 
			
		||||
        :index="resolvePath(route) || route.redirect"
 | 
			
		||||
      >
 | 
			
		||||
        <template #title>
 | 
			
		||||
          <el-icon v-show="route.meta.icon" :class="route.meta.icon">
 | 
			
		||||
            <component
 | 
			
		||||
              :is="useRenderIcon(route.meta && route.meta.icon)"
 | 
			
		||||
            ></component>
 | 
			
		||||
          </el-icon>
 | 
			
		||||
          <span>{{ transformI18n(route.meta.title, route.meta.i18n) }}</span>
 | 
			
		||||
          <FontIcon
 | 
			
		||||
            v-if="route.meta.extraIcon"
 | 
			
		||||
            width="30px"
 | 
			
		||||
            height="30px"
 | 
			
		||||
            style="position: absolute; right: 10px"
 | 
			
		||||
            :icon="route.meta.extraIcon.name"
 | 
			
		||||
            :svg="route.meta.extraIcon.svg ? true : false"
 | 
			
		||||
          ></FontIcon>
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-menu-item>
 | 
			
		||||
    </el-menu>
 | 
			
		||||
    <div class="horizontal-header-right">
 | 
			
		||||
      <!-- 通知 -->
 | 
			
		||||
      <Notice id="header-notice" />
 | 
			
		||||
      <!-- 全屏 -->
 | 
			
		||||
      <screenfull id="header-screenfull" v-show="!deviceDetection()" />
 | 
			
		||||
      <!-- 国际化 -->
 | 
			
		||||
      <el-dropdown id="header-translation" trigger="click">
 | 
			
		||||
        <globalization />
 | 
			
		||||
        <template #dropdown>
 | 
			
		||||
          <el-dropdown-menu class="translation">
 | 
			
		||||
            <el-dropdown-item
 | 
			
		||||
              :style="getDropdownItemStyle(locale, 'zh')"
 | 
			
		||||
              @click="translationCh"
 | 
			
		||||
              ><el-icon class="check-zh" v-show="locale === 'zh'"
 | 
			
		||||
                ><IconifyIconOffline icon="check" /></el-icon
 | 
			
		||||
              >简体中文</el-dropdown-item
 | 
			
		||||
            >
 | 
			
		||||
            <el-dropdown-item
 | 
			
		||||
              :style="getDropdownItemStyle(locale, 'en')"
 | 
			
		||||
              @click="translationEn"
 | 
			
		||||
              ><el-icon class="check-en" v-show="locale === 'en'"
 | 
			
		||||
                ><IconifyIconOffline icon="check" /></el-icon
 | 
			
		||||
              >English</el-dropdown-item
 | 
			
		||||
            >
 | 
			
		||||
          </el-dropdown-menu>
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-dropdown>
 | 
			
		||||
      <!-- 退出登陆 -->
 | 
			
		||||
      <el-dropdown trigger="click">
 | 
			
		||||
        <span class="el-dropdown-link">
 | 
			
		||||
          <img :src="avatars" />
 | 
			
		||||
          <p>{{ usename }}</p>
 | 
			
		||||
        </span>
 | 
			
		||||
        <template #dropdown>
 | 
			
		||||
          <el-dropdown-menu class="logout">
 | 
			
		||||
            <el-dropdown-item @click="logout">
 | 
			
		||||
              <IconifyIconOffline
 | 
			
		||||
                icon="logout-circle-r-line"
 | 
			
		||||
                style="margin: 5px"
 | 
			
		||||
              />
 | 
			
		||||
              {{ $t("buttons.hsLoginOut") }}</el-dropdown-item
 | 
			
		||||
            >
 | 
			
		||||
          </el-dropdown-menu>
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-dropdown>
 | 
			
		||||
      <el-icon
 | 
			
		||||
        class="el-icon-setting"
 | 
			
		||||
        :title="$t('buttons.hssystemSet')"
 | 
			
		||||
        @click="onPanel"
 | 
			
		||||
      >
 | 
			
		||||
        <IconifyIconOffline icon="setting" />
 | 
			
		||||
      </el-icon>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style module="classes" scoped>
 | 
			
		||||
.container {
 | 
			
		||||
  padding: 0 15px;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.hamburger {
 | 
			
		||||
  width: 20px;
 | 
			
		||||
  height: 20px;
 | 
			
		||||
 | 
			
		||||
  &:hover {
 | 
			
		||||
    cursor: pointer;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.is-active-hamburger {
 | 
			
		||||
  transform: rotate(180deg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.translation {
 | 
			
		||||
  ::v-deep(.el-dropdown-menu__item) {
 | 
			
		||||
    padding: 5px 40px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .check-zh {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left: 20px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .check-en {
 | 
			
		||||
    position: absolute;
 | 
			
		||||
    left: 20px;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.logout {
 | 
			
		||||
  max-width: 120px;
 | 
			
		||||
 | 
			
		||||
  ::v-deep(.el-dropdown-menu__item) {
 | 
			
		||||
    min-width: 100%;
 | 
			
		||||
    display: inline-flex;
 | 
			
		||||
    flex-wrap: wrap;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -1,21 +1,14 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import {
 | 
			
		||||
  ref,
 | 
			
		||||
  PropType,
 | 
			
		||||
  nextTick,
 | 
			
		||||
  computed,
 | 
			
		||||
  CSSProperties,
 | 
			
		||||
  getCurrentInstance
 | 
			
		||||
} from "vue";
 | 
			
		||||
import { ref, PropType, nextTick, computed, CSSProperties } from "vue";
 | 
			
		||||
import path from "path";
 | 
			
		||||
import { useNav } from "../../hooks/nav";
 | 
			
		||||
import { childrenType } from "../../types";
 | 
			
		||||
import { transformI18n } from "/@/plugins/i18n";
 | 
			
		||||
import { useAppStoreHook } from "/@/store/modules/app";
 | 
			
		||||
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
 | 
			
		||||
 | 
			
		||||
const instance = getCurrentInstance().appContext.app.config.globalProperties;
 | 
			
		||||
const menuMode = instance.$storage.layout?.layout === "vertical";
 | 
			
		||||
const pureApp = useAppStoreHook();
 | 
			
		||||
const { pureApp } = useNav();
 | 
			
		||||
const menuMode = ["vertical", "mix"].includes(pureApp.layout);
 | 
			
		||||
 | 
			
		||||
const props = defineProps({
 | 
			
		||||
  item: {
 | 
			
		||||
@ -105,7 +98,6 @@ function hoverMenu(key) {
 | 
			
		||||
      : Object.assign(key, {
 | 
			
		||||
          showTooltip: false
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
    hoverMenuMap.set(key, true);
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
@ -132,8 +124,8 @@ function hasOneShowingChild(
 | 
			
		||||
 | 
			
		||||
function resolvePath(routePath) {
 | 
			
		||||
  const httpReg = /^http(s?):\/\//;
 | 
			
		||||
  if (httpReg.test(routePath)) {
 | 
			
		||||
    return props.basePath + "/" + routePath;
 | 
			
		||||
  if (httpReg.test(routePath) || httpReg.test(props.basePath)) {
 | 
			
		||||
    return routePath || props.basePath;
 | 
			
		||||
  } else {
 | 
			
		||||
    return path.resolve(props.basePath, routePath);
 | 
			
		||||
  }
 | 
			
		||||
@ -162,6 +154,18 @@ function resolvePath(routePath) {
 | 
			
		||||
          "
 | 
			
		||||
        ></component>
 | 
			
		||||
      </el-icon>
 | 
			
		||||
      <div
 | 
			
		||||
        v-if="
 | 
			
		||||
          !pureApp.sidebar.opened &&
 | 
			
		||||
          pureApp.layout === 'mix' &&
 | 
			
		||||
          props.item?.pathList?.length === 2
 | 
			
		||||
        "
 | 
			
		||||
        :style="getDivStyle"
 | 
			
		||||
      >
 | 
			
		||||
        <span :style="getMenuTextStyle">
 | 
			
		||||
          {{ transformI18n(onlyOneChild.meta.title, onlyOneChild.meta.i18n) }}
 | 
			
		||||
        </span>
 | 
			
		||||
      </div>
 | 
			
		||||
      <template #title>
 | 
			
		||||
        <div :style="getDivStyle">
 | 
			
		||||
          <span v-if="!menuMode">{{
 | 
			
		||||
 | 
			
		||||
@ -1,60 +1,56 @@
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import Logo from "./logo.vue";
 | 
			
		||||
import { emitter } from "/@/utils/mitt";
 | 
			
		||||
import { useNav } from "../../hooks/nav";
 | 
			
		||||
import SidebarItem from "./sidebarItem.vue";
 | 
			
		||||
import { storageLocal } from "/@/utils/storage";
 | 
			
		||||
import { useRoute, useRouter } from "vue-router";
 | 
			
		||||
import { computed, ref, onBeforeMount } from "vue";
 | 
			
		||||
import { useAppStoreHook } from "/@/store/modules/app";
 | 
			
		||||
import { ref, computed, watch, onBeforeMount } from "vue";
 | 
			
		||||
import { findRouteByPath, getParentPaths } from "/@/router/utils";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
 | 
			
		||||
const route = useRoute();
 | 
			
		||||
const pureApp = useAppStoreHook();
 | 
			
		||||
const router = useRouter().options.routes;
 | 
			
		||||
const routers = useRouter().options.routes;
 | 
			
		||||
const showLogo = ref(
 | 
			
		||||
  storageLocal.getItem("responsive-configure")?.showLogo ?? true
 | 
			
		||||
);
 | 
			
		||||
const isCollapse = computed(() => {
 | 
			
		||||
  return !pureApp.getSidebarStatus;
 | 
			
		||||
});
 | 
			
		||||
const activeMenu = computed((): string => {
 | 
			
		||||
  const { meta, path } = route;
 | 
			
		||||
  if (meta.activeMenu) {
 | 
			
		||||
    // @ts-ignore
 | 
			
		||||
    return meta.activeMenu;
 | 
			
		||||
  }
 | 
			
		||||
  return path;
 | 
			
		||||
 | 
			
		||||
const { pureApp, isCollapse, menuSelect } = useNav();
 | 
			
		||||
 | 
			
		||||
let subMenuData = ref([]);
 | 
			
		||||
 | 
			
		||||
const menuData = computed(() => {
 | 
			
		||||
  return pureApp.layout === "mix"
 | 
			
		||||
    ? subMenuData.value
 | 
			
		||||
    : usePermissionStoreHook().wholeMenus;
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const menuSelect = (indexPath: string): void => {
 | 
			
		||||
  let parentPath = "";
 | 
			
		||||
  let parentPathIndex = indexPath.lastIndexOf("/");
 | 
			
		||||
  if (parentPathIndex > 0) {
 | 
			
		||||
    parentPath = indexPath.slice(0, parentPathIndex);
 | 
			
		||||
function getSubMenuData(path) {
 | 
			
		||||
  // path的上级路由组成的数组
 | 
			
		||||
  const parentPathArr = getParentPaths(
 | 
			
		||||
    path,
 | 
			
		||||
    usePermissionStoreHook().wholeMenus
 | 
			
		||||
  );
 | 
			
		||||
  // 当前路由的父级路由信息
 | 
			
		||||
  const parenetRoute = findRouteByPath(
 | 
			
		||||
    parentPathArr[0] || path,
 | 
			
		||||
    usePermissionStoreHook().wholeMenus
 | 
			
		||||
  );
 | 
			
		||||
  if (!parenetRoute?.children) return;
 | 
			
		||||
  subMenuData.value = parenetRoute?.children;
 | 
			
		||||
}
 | 
			
		||||
  // 找到当前路由的信息
 | 
			
		||||
  // eslint-disable-next-line no-inner-declarations
 | 
			
		||||
  function findCurrentRoute(routes) {
 | 
			
		||||
    return routes.map(item => {
 | 
			
		||||
      if (item.path === indexPath) {
 | 
			
		||||
        // 切换左侧菜单 通知标签页
 | 
			
		||||
        emitter.emit("changLayoutRoute", {
 | 
			
		||||
          indexPath,
 | 
			
		||||
          parentPath
 | 
			
		||||
        });
 | 
			
		||||
      } else {
 | 
			
		||||
        if (item.children) findCurrentRoute(item.children);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
  findCurrentRoute(router);
 | 
			
		||||
};
 | 
			
		||||
getSubMenuData(route.path);
 | 
			
		||||
 | 
			
		||||
onBeforeMount(() => {
 | 
			
		||||
  emitter.on("logoChange", key => {
 | 
			
		||||
    showLogo.value = key;
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
watch(
 | 
			
		||||
  () => route.path,
 | 
			
		||||
  () => getSubMenuData(route.path)
 | 
			
		||||
);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
@ -62,21 +58,21 @@ onBeforeMount(() => {
 | 
			
		||||
    <Logo v-if="showLogo" :collapse="isCollapse" />
 | 
			
		||||
    <el-scrollbar wrap-class="scrollbar-wrapper">
 | 
			
		||||
      <el-menu
 | 
			
		||||
        :default-active="activeMenu"
 | 
			
		||||
        :default-active="route.path"
 | 
			
		||||
        :collapse="isCollapse"
 | 
			
		||||
        unique-opened
 | 
			
		||||
        router
 | 
			
		||||
        :collapse-transition="false"
 | 
			
		||||
        mode="vertical"
 | 
			
		||||
        class="outer-most"
 | 
			
		||||
        @select="menuSelect"
 | 
			
		||||
        @select="indexPath => menuSelect(indexPath, routers)"
 | 
			
		||||
      >
 | 
			
		||||
        <sidebar-item
 | 
			
		||||
          v-for="route in usePermissionStoreHook().wholeMenus"
 | 
			
		||||
          :key="route.path"
 | 
			
		||||
          :item="route"
 | 
			
		||||
          v-for="routes in menuData"
 | 
			
		||||
          :key="routes.path"
 | 
			
		||||
          :item="routes"
 | 
			
		||||
          class="outer-most"
 | 
			
		||||
          :base-path="route.path"
 | 
			
		||||
          :base-path="routes.path"
 | 
			
		||||
        />
 | 
			
		||||
      </el-menu>
 | 
			
		||||
    </el-scrollbar>
 | 
			
		||||
 | 
			
		||||
@ -106,7 +106,11 @@ const iconIsActive = computed(() => {
 | 
			
		||||
 | 
			
		||||
const dynamicTagView = () => {
 | 
			
		||||
  const index = multiTags.value.findIndex(item => {
 | 
			
		||||
    if (item?.query) {
 | 
			
		||||
      return isEqual(route?.query, item?.query);
 | 
			
		||||
    } else {
 | 
			
		||||
      return item.path === route.path;
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  moveToView(index);
 | 
			
		||||
};
 | 
			
		||||
@ -423,6 +427,11 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleCommand(command: object) {
 | 
			
		||||
  const { key, item } = command;
 | 
			
		||||
  onClickDrop(key, item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 触发右键中菜单的点击事件
 | 
			
		||||
function selectTag(key, item) {
 | 
			
		||||
  onClickDrop(key, item, currentSelect.value);
 | 
			
		||||
@ -709,7 +718,11 @@ const getContextMenuStyle = computed((): CSSProperties => {
 | 
			
		||||
        </el-icon>
 | 
			
		||||
      </li>
 | 
			
		||||
      <li>
 | 
			
		||||
        <el-dropdown trigger="click" placement="bottom-end">
 | 
			
		||||
        <el-dropdown
 | 
			
		||||
          trigger="click"
 | 
			
		||||
          placement="bottom-end"
 | 
			
		||||
          @command="handleCommand"
 | 
			
		||||
        >
 | 
			
		||||
          <el-icon>
 | 
			
		||||
            <IconifyIconOffline icon="arrow-down" />
 | 
			
		||||
          </el-icon>
 | 
			
		||||
@ -718,9 +731,9 @@ const getContextMenuStyle = computed((): CSSProperties => {
 | 
			
		||||
              <el-dropdown-item
 | 
			
		||||
                v-for="(item, key) in tagsViews"
 | 
			
		||||
                :key="key"
 | 
			
		||||
                :command="{ key, item }"
 | 
			
		||||
                :divided="item.divided"
 | 
			
		||||
                :disabled="item.disabled"
 | 
			
		||||
                @click="onClickDrop(key, item)"
 | 
			
		||||
              >
 | 
			
		||||
                <component
 | 
			
		||||
                  :is="item.icon"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										110
									
								
								src/layout/hooks/nav.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/layout/hooks/nav.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
			
		||||
import { computed } from "vue";
 | 
			
		||||
import { router } from "/@/router";
 | 
			
		||||
import { emitter } from "/@/utils/mitt";
 | 
			
		||||
import { routeMetaType } from "../types";
 | 
			
		||||
import { transformI18n } from "/@/plugins/i18n";
 | 
			
		||||
import { storageSession } from "/@/utils/storage";
 | 
			
		||||
import { useAppStoreHook } from "/@/store/modules/app";
 | 
			
		||||
import { Title } from "../../../public/serverConfig.json";
 | 
			
		||||
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
 | 
			
		||||
 | 
			
		||||
export function useNav() {
 | 
			
		||||
  const pureApp = useAppStoreHook();
 | 
			
		||||
  // 用户名
 | 
			
		||||
  const usename: string = storageSession.getItem("info")?.username;
 | 
			
		||||
 | 
			
		||||
  // 设置国际化选中后的样式
 | 
			
		||||
  const getDropdownItemStyle = computed(() => {
 | 
			
		||||
    return (locale, t) => {
 | 
			
		||||
      return {
 | 
			
		||||
        background: locale === t ? useEpThemeStoreHook().epThemeColor : "",
 | 
			
		||||
        color: locale === t ? "#f4f4f5" : "#000"
 | 
			
		||||
      };
 | 
			
		||||
    };
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const isCollapse = computed(() => {
 | 
			
		||||
    return !pureApp.getSidebarStatus;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // 动态title
 | 
			
		||||
  function changeTitle(meta: routeMetaType) {
 | 
			
		||||
    if (Title)
 | 
			
		||||
      document.title = `${transformI18n(meta.title, meta.i18n)} | ${Title}`;
 | 
			
		||||
    else document.title = transformI18n(meta.title, meta.i18n);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // 退出登录
 | 
			
		||||
  function logout() {
 | 
			
		||||
    storageSession.removeItem("info");
 | 
			
		||||
    router.push("/login");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function backHome() {
 | 
			
		||||
    router.push("/welcome");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function onPanel() {
 | 
			
		||||
    emitter.emit("openPanel");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function toggleSideBar() {
 | 
			
		||||
    pureApp.toggleSideBar();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function handleResize(menuRef) {
 | 
			
		||||
    menuRef.handleResize();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function resolvePath(route) {
 | 
			
		||||
    const httpReg = /^http(s?):\/\//;
 | 
			
		||||
    const routeChildPath = route.children[0]?.path;
 | 
			
		||||
    if (httpReg.test(routeChildPath)) {
 | 
			
		||||
      return route.path + "/" + routeChildPath;
 | 
			
		||||
    } else {
 | 
			
		||||
      return routeChildPath;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function menuSelect(indexPath: string, routers): void {
 | 
			
		||||
    let parentPath = "";
 | 
			
		||||
    const parentPathIndex = indexPath.lastIndexOf("/");
 | 
			
		||||
    if (parentPathIndex > 0) {
 | 
			
		||||
      parentPath = indexPath.slice(0, parentPathIndex);
 | 
			
		||||
    }
 | 
			
		||||
    // 找到当前路由的信息
 | 
			
		||||
    function findCurrentRoute(indexPath: string, routes) {
 | 
			
		||||
      return routes.map(item => {
 | 
			
		||||
        if (item.path === indexPath) {
 | 
			
		||||
          if (item.redirect) {
 | 
			
		||||
            findCurrentRoute(item.redirect, item.children);
 | 
			
		||||
          } else {
 | 
			
		||||
            // 切换左侧菜单 通知标签页
 | 
			
		||||
            emitter.emit("changLayoutRoute", {
 | 
			
		||||
              indexPath,
 | 
			
		||||
              parentPath
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          if (item.children) findCurrentRoute(indexPath, item.children);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    findCurrentRoute(indexPath, routers);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    logout,
 | 
			
		||||
    backHome,
 | 
			
		||||
    onPanel,
 | 
			
		||||
    changeTitle,
 | 
			
		||||
    toggleSideBar,
 | 
			
		||||
    menuSelect,
 | 
			
		||||
    handleResize,
 | 
			
		||||
    resolvePath,
 | 
			
		||||
    isCollapse,
 | 
			
		||||
    pureApp,
 | 
			
		||||
    usename,
 | 
			
		||||
    getDropdownItemStyle
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
@ -171,7 +171,8 @@ const layoutHeader = defineComponent({
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        default: () => [
 | 
			
		||||
          !pureSetting.hiddenSideBar && layout.value.includes("vertical")
 | 
			
		||||
          !pureSetting.hiddenSideBar &&
 | 
			
		||||
          (layout.value.includes("vertical") || layout.value.includes("mix"))
 | 
			
		||||
            ? h(navbar)
 | 
			
		||||
            : h("div"),
 | 
			
		||||
          !pureSetting.hiddenSideBar && layout.value.includes("horizontal")
 | 
			
		||||
@ -213,7 +214,10 @@ const layoutHeader = defineComponent({
 | 
			
		||||
      @click="useAppStoreHook().toggleSideBar()"
 | 
			
		||||
    />
 | 
			
		||||
    <Vertical
 | 
			
		||||
      v-show="!pureSetting.hiddenSideBar && layout.includes('vertical')"
 | 
			
		||||
      v-show="
 | 
			
		||||
        !pureSetting.hiddenSideBar &&
 | 
			
		||||
        (layout.includes('vertical') || layout.includes('mix'))
 | 
			
		||||
      "
 | 
			
		||||
    />
 | 
			
		||||
    <div
 | 
			
		||||
      :class="[
 | 
			
		||||
 | 
			
		||||
@ -11,11 +11,7 @@ export const routerArrays: Array<RouteConfigs> = [
 | 
			
		||||
  }
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
export type RouteConfigs = {
 | 
			
		||||
  path?: string;
 | 
			
		||||
  parentPath?: string;
 | 
			
		||||
  query?: object;
 | 
			
		||||
  meta?: {
 | 
			
		||||
export type routeMetaType = {
 | 
			
		||||
  title?: string;
 | 
			
		||||
  i18n?: boolean;
 | 
			
		||||
  icon?: string;
 | 
			
		||||
@ -23,6 +19,12 @@ export type RouteConfigs = {
 | 
			
		||||
  savedPosition?: boolean;
 | 
			
		||||
  authority?: Array<string>;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type RouteConfigs = {
 | 
			
		||||
  path?: string;
 | 
			
		||||
  parentPath?: string;
 | 
			
		||||
  query?: object;
 | 
			
		||||
  meta?: routeMetaType;
 | 
			
		||||
  children?: RouteConfigs[];
 | 
			
		||||
  name?: string;
 | 
			
		||||
};
 | 
			
		||||
@ -71,6 +73,8 @@ export type childrenType = {
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
  showTooltip?: boolean;
 | 
			
		||||
  parentId?: number;
 | 
			
		||||
  pathList?: number[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type themeColorsType = {
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,10 @@ import { storageLocal } from "/@/utils/storage";
 | 
			
		||||
 * @param isI18n  如果true,获取对应的消息,否则返回本身
 | 
			
		||||
 * @returns message
 | 
			
		||||
 */
 | 
			
		||||
export function transformI18n(message: string | object = "", isI18n = false) {
 | 
			
		||||
export function transformI18n(
 | 
			
		||||
  message: string | unknown | object = "",
 | 
			
		||||
  isI18n: boolean | unknown = false
 | 
			
		||||
) {
 | 
			
		||||
  if (!message) {
 | 
			
		||||
    return "";
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@ import { split, findIndex } from "lodash-es";
 | 
			
		||||
import { transformI18n } from "/@/plugins/i18n";
 | 
			
		||||
import remainingRouter from "./modules/remaining";
 | 
			
		||||
import { storageSession } from "/@/utils/storage";
 | 
			
		||||
import { Title } from "../../public/serverConfig.json";
 | 
			
		||||
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
 | 
			
		||||
import { usePermissionStoreHook } from "/@/store/modules/permission";
 | 
			
		||||
import { Router, RouteMeta, createRouter, RouteRecordName } from "vue-router";
 | 
			
		||||
@ -54,12 +55,13 @@ router.beforeEach((to: toRouteType, _from, next) => {
 | 
			
		||||
  const externalLink = to?.redirectedFrom?.fullPath;
 | 
			
		||||
  if (!externalLink)
 | 
			
		||||
    to.matched.some(item => {
 | 
			
		||||
      item.meta.title
 | 
			
		||||
        ? (document.title = transformI18n(
 | 
			
		||||
            item.meta.title as string,
 | 
			
		||||
            item.meta?.i18n as boolean
 | 
			
		||||
          ))
 | 
			
		||||
        : "";
 | 
			
		||||
      if (!item.meta.title) return "";
 | 
			
		||||
      if (Title)
 | 
			
		||||
        document.title = `${transformI18n(
 | 
			
		||||
          item.meta.title,
 | 
			
		||||
          item.meta?.i18n
 | 
			
		||||
        )} | ${Title}`;
 | 
			
		||||
      else document.title = transformI18n(item.meta.title, item.meta?.i18n);
 | 
			
		||||
    });
 | 
			
		||||
  if (name) {
 | 
			
		||||
    if (_from?.name) {
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@ import {
 | 
			
		||||
  formatTwoStageRoutes,
 | 
			
		||||
  formatFlatteningRoutes
 | 
			
		||||
} from "../utils";
 | 
			
		||||
import { buildHierarchyTree } from "/@/utils/tree";
 | 
			
		||||
 | 
			
		||||
// 原始静态路由(未做任何处理)
 | 
			
		||||
const routes = [
 | 
			
		||||
@ -32,7 +33,7 @@ const routes = [
 | 
			
		||||
 | 
			
		||||
// 导出处理后的静态路由(三级及以上的路由全部拍成二级)
 | 
			
		||||
export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
 | 
			
		||||
  formatFlatteningRoutes(ascending(routes))
 | 
			
		||||
  formatFlatteningRoutes(buildHierarchyTree(ascending(routes)))
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// 用于渲染菜单,保持原始层级
 | 
			
		||||
 | 
			
		||||
@ -8,16 +8,29 @@ export const useEpThemeStore = defineStore({
 | 
			
		||||
  state: () => ({
 | 
			
		||||
    epThemeColor:
 | 
			
		||||
      storageLocal.getItem("responsive-layout")?.epThemeColor ??
 | 
			
		||||
      getConfig().EpThemeColor
 | 
			
		||||
      getConfig().EpThemeColor,
 | 
			
		||||
    epTheme:
 | 
			
		||||
      storageLocal.getItem("responsive-layout")?.theme ?? getConfig().Theme
 | 
			
		||||
  }),
 | 
			
		||||
  getters: {
 | 
			
		||||
    getEpThemeColor() {
 | 
			
		||||
      return this.epThemeColor;
 | 
			
		||||
    },
 | 
			
		||||
    // 用于mix导航模式下hamburger-svg的fill属性
 | 
			
		||||
    fill() {
 | 
			
		||||
      if (this.epTheme === "light") {
 | 
			
		||||
        return "#409eff";
 | 
			
		||||
      } else if (this.epTheme === "yellow") {
 | 
			
		||||
        return "#d25f00";
 | 
			
		||||
      } else {
 | 
			
		||||
        return "#fff";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  },
 | 
			
		||||
  actions: {
 | 
			
		||||
    setEpThemeColor(newColor) {
 | 
			
		||||
      const layout = storageLocal.getItem("responsive-layout");
 | 
			
		||||
      this.epTheme = layout?.theme;
 | 
			
		||||
      this.epThemeColor = newColor;
 | 
			
		||||
      layout.epThemeColor = newColor;
 | 
			
		||||
      storageLocal.setItem("responsive-layout", layout);
 | 
			
		||||
 | 
			
		||||
@ -638,3 +638,64 @@ body[layout="horizontal"] {
 | 
			
		||||
    transition: none !important;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
body[layout="mix"] {
 | 
			
		||||
  $sideBarWidth: 210px;
 | 
			
		||||
  @include merge-style($sideBarWidth);
 | 
			
		||||
 | 
			
		||||
  .el-menu {
 | 
			
		||||
    --el-menu-hover-bg-color: transparent !important;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .hideSidebar {
 | 
			
		||||
    .fixed-header {
 | 
			
		||||
      width: calc(100% - 54px);
 | 
			
		||||
      transition: width 0.28s;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .sidebar-container {
 | 
			
		||||
      width: 54px !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .main-container {
 | 
			
		||||
      margin-left: 54px;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .submenu-title-noDropdown {
 | 
			
		||||
      padding: 0 !important;
 | 
			
		||||
      position: relative;
 | 
			
		||||
 | 
			
		||||
      .el-tooltip {
 | 
			
		||||
        padding: 0 !important;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* 菜单折叠 */
 | 
			
		||||
    .el-menu--collapse {
 | 
			
		||||
      .el-sub-menu {
 | 
			
		||||
        & > .el-sub-menu__title {
 | 
			
		||||
          & > span {
 | 
			
		||||
            height: 0;
 | 
			
		||||
            width: 0;
 | 
			
		||||
            overflow: hidden;
 | 
			
		||||
            visibility: hidden;
 | 
			
		||||
            display: inline-block;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      /* 无子菜单 */
 | 
			
		||||
      .el-menu-item [class^="el-icon"] {
 | 
			
		||||
        right: 5px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .el-sub-menu__title [class^="el-icon"] {
 | 
			
		||||
        right: 2px;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      .submenu-title-noDropdown {
 | 
			
		||||
        background: transparent !important;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -46,3 +46,22 @@ export function deleteChildren(menuTree, pathList = []) {
 | 
			
		||||
  }
 | 
			
		||||
  return menuTree;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 创建层级关系
 | 
			
		||||
export function buildHierarchyTree(menuTree, pathList = []) {
 | 
			
		||||
  if (!Array.isArray(menuTree)) {
 | 
			
		||||
    console.warn("menuTree must be an array");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (!menuTree || menuTree.length === 0) return;
 | 
			
		||||
  for (const [key, node] of menuTree.entries()) {
 | 
			
		||||
    node.id = key;
 | 
			
		||||
    node.parentId = pathList.length ? pathList[pathList.length - 1] : null;
 | 
			
		||||
    node.pathList = [...pathList, node.id];
 | 
			
		||||
    const hasChildren = node.children && node.children.length > 0;
 | 
			
		||||
    if (hasChildren) {
 | 
			
		||||
      buildHierarchyTree(node.children, node.pathList);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return menuTree;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -140,6 +140,7 @@ onMounted(() => {
 | 
			
		||||
.item-cut {
 | 
			
		||||
  font-size: 1.5em;
 | 
			
		||||
  height: 77px;
 | 
			
		||||
  line-height: 77px;
 | 
			
		||||
  text-align: center;
 | 
			
		||||
  border: 1px solid #e5e4e9;
 | 
			
		||||
  cursor: move;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user