Browse Source

release: update 2.8.0

i18n
xiaoxian521 3 years ago
parent
commit
3418f44be4
  1. 11
      .vscode/extensions.json
  2. 27
      .vscode/settings.json
  3. 0
      .vscode/vue3.0.code-snippets.json
  4. 17
      .vscode/vue3.2+.setup-snippets.json
  5. 20
      .vscode/vue3.2.setup-snippets
  6. 104
      index.html
  7. 12
      mock/asyncRoutes.ts
  8. 6
      package.json
  9. 27
      pnpm-lock.yaml
  10. 6
      public/serverConfig.json
  11. 20
      src/layout/components/appMain.vue
  12. 35
      src/layout/components/navbar.vue
  13. 4
      src/layout/components/screenfull/index.vue
  14. 95
      src/layout/components/setting/index.vue
  15. 2
      src/layout/components/sidebar/breadCrumb.vue
  16. 34
      src/layout/components/sidebar/horizontal.vue
  17. 84
      src/layout/components/sidebar/sidebarItem.vue
  18. 6
      src/layout/components/sidebar/vertical.vue
  19. 1
      src/layout/components/tag/index.scss
  20. 50
      src/layout/components/tag/index.vue
  21. 18
      src/layout/index.vue
  22. 4
      src/layout/types.ts
  23. 22
      src/plugins/element-plus/index.ts
  24. 98
      src/plugins/i18n/config.ts
  25. 21
      src/plugins/i18n/en/buttons.ts
  26. 38
      src/plugins/i18n/en/menus.ts
  27. 55
      src/plugins/i18n/index.ts
  28. 21
      src/plugins/i18n/zh-CN/buttons.ts
  29. 38
      src/plugins/i18n/zh-CN/menus.ts
  30. 3
      src/router/index.ts
  31. 7
      src/router/modules/error.ts
  32. 5
      src/router/modules/externalLink.ts
  33. 4
      src/router/modules/home.ts
  34. 5
      src/router/modules/remaining.ts
  35. 6
      src/router/utils.ts
  36. 18
      src/store/modules/app.ts
  37. 9
      src/store/modules/epTheme.ts
  38. 8
      src/store/modules/multiTags.ts
  39. 4
      src/store/modules/user.ts
  40. 12
      src/utils/storage/responsive.ts
  41. 4
      types/global.d.ts

11
.vscode/extensions.json

@ -0,0 +1,11 @@
{
"recommendations": [
"stylelint.vscode-stylelint",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"johnsoncodehk.volar",
"lokalise.i18n-ally",
"mikestead.dotenv",
"antfu.iconify"
]
}

27
.vscode/settings.json

@ -1,11 +1,14 @@
{
// You should install these plugins:
// ESLint
// Prettier - Code formatter
// stylelint
// vscode-icons
// TypeScript Vue Plugin (Volar)
// Vue Language Features (Volar)
/** 便
* ESLint
* Prettier - Code formatter
* stylelint
* vscode-icons
* i18n Ally
* Iconify IntelliSense
* TypeScript Vue Plugin (Volar)
* Vue Language Features (Volar)
*/
"terminal.integrated.rendererType": "dom",
"editor.formatOnType": true,
"editor.formatOnSave": true,
@ -45,5 +48,13 @@
},
"volar.tsPlugin": true,
"typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": ["src/plugins/i18n"]
"i18n-ally.localesPaths": ["src/plugins/i18n"],
"i18n-ally.keystyle": "nested",
"i18n-ally.sortKeys": true,
"i18n-ally.namespace": true,
"i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
"i18n-ally.enabledParsers": ["ts"],
"i18n-ally.sourceLanguage": "en",
"i18n-ally.displayLanguage": "zh-CN",
"i18n-ally.enabledFrameworks": ["vue", "react"]
}

0
.vscode/vue3.0.code-snippets → .vscode/vue3.0.code-snippets.json

17
.vscode/vue3.2+.setup-snippets.json

@ -0,0 +1,17 @@
{
"Vue3.2+快速生成模板": {
"prefix": "Vue3.2+",
"body": [
"<script setup lang='ts'>",
"</script>\n",
"<template>",
"\t<div>\n",
"\t</div>",
"</template>\n",
"<style scoped>\n",
"</style>",
"$2"
],
"description": "Vue3.2+"
}
}

20
.vscode/vue3.2.setup-snippets

@ -1,20 +0,0 @@
{
"Vue3.2快速生成模板": {
"prefix": "Vue3.2",
"body": [
"<!-- $1 -->",
"<script setup lang='ts'>",
"\t$2",
"</script>\n",
"<template>",
"\t<div>",
"\t\t$3",
"\t</div>",
"</template>\n",
"<style scoped>",
"\t$4",
"</style>"
],
"description": "Vue3.2"
}
}

104
index.html

@ -5,7 +5,7 @@
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/iconfont.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>vue-pure-admin</title>
<title>pure-admin-thin</title>
<script>
window.process = {};
</script>
@ -14,106 +14,36 @@
<body>
<div id="app">
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background: #000;
overflow: hidden;
font-family: "Reggae One", cursive;
}
p {
font-size: 8vw;
overflow: hidden;
-webkit-text-stroke: 3px #7272a5;
}
span {
display: block;
font-size: 20px;
overflow: hidden;
color: green;
text-align: center;
}
p::before {
content: " ";
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
background-image: linear-gradient(45deg, #ff269b, #2ab5f5, #ffbf00);
mix-blend-mode: multiply;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 26px;
}
p::after {
content: "";
background: radial-gradient(circle, #fff, #000 50%);
background-size: 25% 25%;
position: absolute;
top: -100%;
left: -100%;
right: 0;
top: 0%;
bottom: 0;
mix-blend-mode: color-dodge;
animation: mix 2s linear infinite;
animation: dot 1s infinite steps(1, start);
}
@keyframes mix {
to {
transform: translate(50%, 50%);
@keyframes dot {
33.33% {
content: ".";
}
66.67% {
content: "..";
}
100% {
content: "...";
}
}
</style>
<div class="g-container">
<p>Pure-Admin</p>
<span class="_develop"></span>
</div>
<p>Loading</p>
</div>
<script>
// 此代码仅用于开发环境的友好提示,项目打包前请去掉这段js代码 This code is only used as a friendly reminder of the development environment, please remove this js code before packaging the project
window.onload = function () {
(function () {
const ua = navigator.userAgent.toLowerCase();
const re = /(msie|firefox|chrome|opera|version).*?([\d.]+)/;
const m = ua.match(re);
const Sys = {
browser: m[1].replace(/version/, "'safari"),
version: m[2]
};
const browser = Array.of("chrome", "firefox").includes(Sys.browser);
const version = parseFloat(Sys.version);
const el = document.querySelector("._develop");
if (el) {
if (browser && version >= 90) {
let success =
document.createTextNode("当前浏览器版本很适合开发!!! 😃");
el.appendChild(success);
} else {
let warn = document.createTextNode(
"当前浏览器版本不适合开发,建议使用最新版本的谷歌或者火狐浏览器!!!😯"
);
el.appendChild(warn);
el.style.color = "red";
}
}
return Sys;
})();
};
</script>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

12
mock/asyncRoutes.ts

@ -4,9 +4,9 @@ import { MockMethod } from "vite-plugin-mock";
const permissionRouter = {
path: "/permission",
name: "permission",
redirect: "/permission/page",
redirect: "/permission/page/index",
meta: {
title: "message.permission",
title: "menus.permission",
icon: "Lollipop",
i18n: true,
showLink: true,
@ -14,19 +14,19 @@ const permissionRouter = {
},
children: [
{
path: "/permission/page",
path: "/permission/page/index",
name: "permissionPage",
meta: {
title: "message.permissionPage",
title: "menus.permissionPage",
i18n: true,
showLink: true
}
},
{
path: "/permission/button",
path: "/permission/button/index",
name: "permissionButton",
meta: {
title: "message.permissionButton",
title: "menus.permissionButton",
i18n: true,
showLink: true,
authority: []

6
package.json

@ -1,6 +1,6 @@
{
"name": "vue-pure-admin",
"version": "2.6.0",
"name": "pure-admin-thin",
"version": "2.8.0",
"private": true,
"engines": {
"node": ">= 16",
@ -40,7 +40,7 @@
"axios": "^0.21.1",
"css-color-function": "^1.3.3",
"dayjs": "^1.10.7",
"element-plus": "1.2.0-beta.6",
"element-plus": "1.3.0-beta.1",
"element-resize-detector": "^1.2.3",
"font-awesome": "^4.7.0",
"js-cookie": "^3.0.1",

27
pnpm-lock.yaml

@ -32,7 +32,7 @@ specifiers:
cross-env: 7.0.3
css-color-function: ^1.3.3
dayjs: ^1.10.7
element-plus: 1.2.0-beta.6
element-plus: 1.3.0-beta.1
element-resize-detector: ^1.2.3
eslint: 7.30.0
eslint-plugin-prettier: 3.4.0
@ -88,7 +88,7 @@ dependencies:
axios: 0.21.4
css-color-function: 1.3.3
dayjs: 1.10.7
element-plus: 1.2.0-beta.6_vue@3.2.24
element-plus: 1.3.0-beta.1_vue@3.2.24
element-resize-detector: 1.2.3
font-awesome: 4.7.0
js-cookie: 3.0.1
@ -938,10 +938,10 @@ packages:
fastq: 1.13.0
dev: true
/@popperjs/core/2.11.0:
/@popperjs/core/2.11.2:
resolution:
{
integrity: sha512-zrsUxjLOKAzdewIDRWy9nsV1GQsKBCWaGwsZQlCgr6/q+vjyZhFgqedLfFBuI9anTPEUT4APq9Mu0SZBTzIcGQ==
integrity: sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==
}
dev: false
@ -1514,10 +1514,10 @@ packages:
vue-demi: 0.12.1_vue@3.2.24
dev: false
/@vueuse/core/7.2.2_vue@3.2.24:
/@vueuse/core/7.5.1_vue@3.2.24:
resolution:
{
integrity: sha512-T9oksrPflNhsgG/Y/7IeCSmITPZ0VKDnTpK8y7SQl4ZIdLIL8L7fJyhJEgSMWyo497j/XK3RKFkOTh4GFTVeIQ==
integrity: sha512-GczfdTWqH483zkUHdDYiLm0Tn51OtsQXYc+eBKKIeolh0mgn682KbSYmkrjNytaF7qGc74YxMDAYjkPBW6V2Pg==
}
peerDependencies:
"@vue/composition-api": ^1.1.0
@ -1528,7 +1528,7 @@ packages:
vue:
optional: true
dependencies:
"@vueuse/shared": 7.2.2_vue@3.2.24
"@vueuse/shared": 7.5.1_vue@3.2.24
vue: 3.2.24
vue-demi: 0.12.1_vue@3.2.24
dev: false
@ -1569,10 +1569,10 @@ packages:
vue-demi: 0.12.1_vue@3.2.24
dev: false
/@vueuse/shared/7.2.2_vue@3.2.24:
/@vueuse/shared/7.5.1_vue@3.2.24:
resolution:
{
integrity: sha512-9vevEvvQgx4snSrDfZ5BFd7FmlIl9rwTtr8ySzPZhZQslx6lbcsXK3Q97I06Fv8S2TedR//X9fn2QbNtbFmdog==
integrity: sha512-zMQEuYJyTmr5Hj2rYgSbb4H/cSI8mdaa9dUuw20j6rPV+xLV11y7vCyIkxo31uODDr0p77FMlProKzNDiK9rAA==
}
peerDependencies:
"@vue/composition-api": ^1.1.0
@ -2831,17 +2831,18 @@ packages:
}
dev: true
/element-plus/1.2.0-beta.6_vue@3.2.24:
/element-plus/1.3.0-beta.1_vue@3.2.24:
resolution:
{
integrity: sha512-8EdSIR/5/FHcSB8w1diAh+gJMHgxIvxuZoayY99k6taAR1QyEFHuPTgFccZLopJ1+iP4UEsZFz49l57qS08Utw==
integrity: sha512-q3vMaKElPpuSTeIF7kuDmMOE+N1YVCCIG3fshXpz6qgjnxPbgZumVM0qGfhr8DTu9JxRbBoDok49dqtX/BWn3w==
}
peerDependencies:
vue: ^3.2.0
dependencies:
"@ctrl/tinycolor": 3.4.0
"@element-plus/icons-vue": 0.2.4_vue@3.2.24
"@popperjs/core": 2.11.0
"@vueuse/core": 7.2.2_vue@3.2.24
"@popperjs/core": 2.11.2
"@vueuse/core": 7.5.1_vue@3.2.24
async-validator: 4.0.7
dayjs: 1.10.7
lodash: 4.17.21

6
public/serverConfig.json

@ -1,5 +1,5 @@
{
"Version": "2.6.0",
"Version": "2.8.0",
"Title": "PureAdmin",
"FixedHeader": true,
"HiddenSideBar": false,
@ -12,6 +12,10 @@
"Grey": false,
"Weak": false,
"HideTabs": false,
"SidebarStatus": true,
"EpThemeColor": "#409EFF",
"ShowLogo": true,
"ShowModel": "smart",
"MapConfigure": {
"amapKey": "97b3248d1553172e81f168cf94ea667e",
"options": {

20
src/layout/components/appMain.vue

@ -27,12 +27,23 @@ const transitions = computed(() => {
});
const hideTabs = computed(() => {
return instance?.sets.hideTabs;
return instance?.configure.hideTabs;
});
const layout = computed(() => {
return instance?.layout.layout === "vertical";
});
const getSectionStyle = computed(() => {
return [
hideTabs.value && layout ? "padding-top: 48px;" : "",
!hideTabs.value && layout ? "padding-top: 85px;" : "",
hideTabs.value && !layout.value ? "padding-top: 48px" : "",
!hideTabs.value && !layout.value ? "padding-top: 85px;" : "",
props.fixedHeader ? "" : "padding-top: 0;"
];
});
const transitionMain = defineComponent({
render() {
return h(
@ -71,12 +82,7 @@ const transitionMain = defineComponent({
<template>
<section
:class="[props.fixedHeader ? 'app-main' : 'app-main-nofixed-header']"
:style="[
hideTabs && layout ? 'padding-top: 48px;' : '',
!hideTabs && layout ? 'padding-top: 85px;' : '',
hideTabs && !layout ? 'padding-top: 48px' : '',
!hideTabs && !layout ? 'padding-top: 85px;' : ''
]"
:style="getSectionStyle"
>
<router-view>
<template #default="{ Component, route }">

35
src/layout/components/navbar.vue

@ -1,4 +1,5 @@
<script setup lang="ts">
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { emitter } from "/@/utils/mitt";
import Notice from "./notice/index.vue";
@ -22,6 +23,15 @@ const route = useRoute();
let usename = storageSession.getItem("info")?.username;
const { locale } = useI18n();
const getDropdownItemStyle = computed(() => {
return t => {
return {
background: locale.value === t ? "#1b2a47" : "",
color: locale.value === t ? "#f4f4f5" : "#000"
};
};
});
watch(
() => locale.value,
() => {
@ -73,29 +83,23 @@ function translationEn() {
<div class="vertical-header-right">
<!-- 通知 -->
<Notice />
<Notice id="header-notice" />
<!-- 全屏 -->
<screenfull v-show="!deviceDetection()" />
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 -->
<el-dropdown trigger="click">
<el-dropdown id="header-translation" trigger="click">
<globalization />
<template #dropdown>
<el-dropdown-menu class="translation">
<el-dropdown-item
:style="{
background: locale === 'zh' ? '#1b2a47' : '',
color: locale === 'zh' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('zh')"
@click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'"
><check /></el-icon
>简体中文</el-dropdown-item
>
<el-dropdown-item
:style="{
background: locale === 'en' ? '#1b2a47' : '',
color: locale === 'en' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('en')"
@click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon
@ -114,14 +118,14 @@ function translationEn() {
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">
<i class="ri-logout-circle-r-line"></i
>{{ $t("message.hsLoginOut") }}</el-dropdown-item
>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-icon
class="el-icon-setting"
:title="$t('message.hssystemSet')"
:title="$t('buttons.hssystemSet')"
@click="onPanel"
>
<Setting />
@ -230,7 +234,7 @@ function translationEn() {
.translation {
.el-dropdown-menu__item {
padding: 0 40px !important;
padding: 5px 40px !important;
}
.el-dropdown-menu__item:focus,
@ -242,12 +246,10 @@ function translationEn() {
.check-zh {
position: absolute;
left: 20px;
top: 13px;
}
.check-en {
position: absolute;
bottom: 13px;
left: 20px;
}
}
@ -259,7 +261,6 @@ function translationEn() {
min-width: 100%;
display: inline-flex;
flex-wrap: wrap;
padding: 0 18px !important;
}
.el-dropdown-menu__item:focus,

4
src/layout/components/screenfull/index.vue

@ -8,8 +8,8 @@ const { isFullscreen, toggle } = useFullscreen();
<i
:title="
isFullscreen
? $t('message.hsexitfullscreen')
: $t('message.hsfullscreen')
? $t('buttons.hsexitfullscreen')
: $t('buttons.hsfullscreen')
"
:class="
isFullscreen

95
src/layout/components/setting/index.vue

@ -11,9 +11,9 @@ import {
} from "vue";
import rgbHex from "rgb-hex";
import { find } from "lodash-es";
import panel from "../panel/index.vue";
import { getConfig } from "/@/config";
import { useRouter } from "vue-router";
import panel from "../panel/index.vue";
import { emitter } from "/@/utils/mitt";
import { templateRef } from "@vueuse/core";
import dayIcon from "/@/assets/svg/day.svg";
@ -79,19 +79,33 @@ if (unref(layoutTheme)) {
}
//
const markValue = ref(storageLocal.getItem("showModel") || "smart");
const markValue = ref(instance.configure?.showModel ?? "smart");
const logoVal = ref(storageLocal.getItem("logoVal") || "1");
const logoVal = ref(instance.configure?.showLogo ?? true);
const epThemeColor = ref(useEpThemeStoreHook().getEpThemeColor);
const settings = reactive({
greyVal: instance.sets.grey,
weakVal: instance.sets.weak,
tabsVal: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
greyVal: instance.configure.grey,
weakVal: instance.configure.weak,
tabsVal: instance.configure.hideTabs,
showLogo: instance.configure.showLogo,
showModel: instance.configure.showModel,
multiTagsCache: instance.configure.multiTagsCache
});
const getThemeColorStyle = computed(() => {
return rgb => {
return { background: `rgb(${rgb})` };
};
});
function changeStorageConfigure(key, val) {
const storageConfigure = instance.configure;
storageConfigure[key] = val;
instance.configure = storageConfigure;
}
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
const targetEl = target || document.body;
let { className } = targetEl;
@ -102,12 +116,7 @@ function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
//
const greyChange = (value): void => {
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
instance.sets = {
grey: value,
weak: instance.sets.weak,
hideTabs: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("grey", value);
};
//
@ -117,33 +126,18 @@ const weekChange = (value): void => {
"html-weakness",
document.querySelector("html")
);
instance.sets = {
grey: instance.sets.grey,
weak: value,
hideTabs: instance.sets.hideTabs,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("weak", value);
};
const tagsChange = () => {
let showVal = settings.tabsVal;
instance.sets = {
grey: instance.sets.grey,
weak: instance.sets.weak,
hideTabs: showVal,
multiTagsCache: instance.sets.multiTagsCache
};
changeStorageConfigure("hideTabs", showVal);
emitter.emit("tagViewsChange", showVal);
};
const multiTagsCacheChange = () => {
let multiTagsCache = settings.multiTagsCache;
instance.sets = {
grey: instance.sets.grey,
weak: instance.sets.weak,
hideTabs: instance.sets.hideTabs,
multiTagsCache: multiTagsCache
};
changeStorageConfigure("multiTagsCache", multiTagsCache);
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
};
@ -160,30 +154,30 @@ function onReset() {
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}
}
]);
useMultiTagsStoreHook().multiTagsCacheChange(getConfig().MultiTagsCache);
useEpThemeStoreHook().setEpThemeColor("#409EFF");
useEpThemeStoreHook().setEpThemeColor(getConfig().EpThemeColor);
storageLocal.clear();
storageSession.clear();
router.push("/login");
}
function onChange(label) {
storageLocal.setItem("showModel", label);
changeStorageConfigure("showModel", label);
emitter.emit("tagViewsShowModel", label);
}
// Logo
function logoChange() {
unref(logoVal) === "1"
? storageLocal.setItem("logoVal", "1")
: storageLocal.setItem("logoVal", "-1");
unref(logoVal)
? changeStorageConfigure("showLogo", true)
: changeStorageConfigure("showLogo", false);
emitter.emit("logoChange", unref(logoVal));
}
@ -232,13 +226,19 @@ function setLayoutModel(layout: string) {
instance.layout = {
layout,
theme: layoutTheme.value.theme,
darkMode: instance.layout.darkMode
darkMode: instance.layout.darkMode,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
useAppStoreHook().setLayout(layout);
}
//
let tempLayoutThemeColor;
//
function setLayoutThemeColor(theme: string) {
tempLayoutThemeColor = instance.layout.theme;
layoutTheme.value.theme = theme;
toggleTheme({
scopeName: `layout-theme-${theme}`
@ -246,11 +246,13 @@ function setLayoutThemeColor(theme: string) {
instance.layout = {
layout: useAppStoreHook().layout,
theme,
darkMode: dataTheme.value
darkMode: dataTheme.value,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
if (theme === "default" || theme === "light") {
setEpThemeColor("#409EFF");
setEpThemeColor(getConfig().EpThemeColor);
} else {
const colors = find(themeColors.value, { themeColor: theme });
const color = "#" + rgbHex(colors.rgb);
@ -274,10 +276,13 @@ function dataThemeChange() {
setLayoutThemeColor("light");
} else {
body.setAttribute("data-theme", "");
tempLayoutThemeColor && setLayoutThemeColor(tempLayoutThemeColor);
instance.layout = {
layout: useAppStoreHook().layout,
theme: instance.layout.theme,
darkMode: dataTheme.value
darkMode: dataTheme.value,
sidebarStatus: instance.layout.sidebarStatus,
epThemeColor: instance.layout.epThemeColor
};
}
}
@ -338,7 +343,7 @@ nextTick(() => {
<li
v-for="(item, index) in themeColors"
:key="index"
:style="{ background: `rgb(${item.rgb})` }"
:style="getThemeColorStyle(item.rgb)"
@click="setLayoutThemeColor(item.themeColor)"
>
<el-icon
@ -394,8 +399,8 @@ nextTick(() => {
<el-switch
v-model="logoVal"
inline-prompt
active-value="1"
inactive-value="-1"
:active-value="true"
:inactive-value="false"
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"

2
src/layout/components/sidebar/breadCrumb.vue

@ -65,7 +65,7 @@ const getBreadcrumb = (): void => {
{
path: "/welcome",
parentPath: "/",
meta: { title: "message.hshome", i18n: true }
meta: { title: "menus.hshome", i18n: true }
} as unknown as RouteLocationMatched
].concat(matched);
}

34
src/layout/components/sidebar/horizontal.vue

@ -35,6 +35,15 @@ const routers = useRouter().options.routes;
let usename = storageSession.getItem("info")?.username;
const { locale, t } = useI18n();
const getDropdownItemStyle = computed(() => {
return t => {
return {
background: locale.value === t ? "#1b2a47" : "",
color: locale.value === t ? "#f4f4f5" : "#000"
};
};
});
watch(
() => locale.value,
() => {
@ -140,29 +149,23 @@ onMounted(() => {
</el-menu>
<div class="horizontal-header-right">
<!-- 通知 -->
<Notice />
<Notice id="header-notice" />
<!-- 全屏 -->
<screenfull v-show="!deviceDetection()" />
<screenfull id="header-screenfull" v-show="!deviceDetection()" />
<!-- 国际化 -->
<el-dropdown trigger="click">
<el-dropdown id="header-translation" trigger="click">
<globalization />
<template #dropdown>
<el-dropdown-menu class="translation">
<el-dropdown-item
:style="{
background: locale === 'zh' ? '#1b2a47' : '',
color: locale === 'zh' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('zh')"
@click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'"
><check /></el-icon
>简体中文</el-dropdown-item
>
<el-dropdown-item
:style="{
background: locale === 'en' ? '#1b2a47' : '',
color: locale === 'en' ? '#f4f4f5' : '#000'
}"
:style="getDropdownItemStyle('en')"
@click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon
@ -181,14 +184,14 @@ onMounted(() => {
<el-dropdown-menu class="logout">
<el-dropdown-item @click="logout">
<i class="ri-logout-circle-r-line"></i
>{{ $t("message.hsLoginOut") }}</el-dropdown-item
>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-icon
class="el-icon-setting"
:title="$t('message.hssystemSet')"
:title="$t('buttons.hssystemSet')"
@click="onPanel"
>
<Setting />
@ -200,7 +203,7 @@ onMounted(() => {
<style lang="scss" scoped>
.translation {
.el-dropdown-menu__item {
padding: 0 40px !important;
padding: 5px 40px !important;
}
.el-dropdown-menu__item:focus,
@ -212,12 +215,10 @@ onMounted(() => {
.check-zh {
position: absolute;
left: 20px;
top: 13px;
}
.check-en {
position: absolute;
bottom: 13px;
left: 20px;
}
}
@ -229,7 +230,6 @@ onMounted(() => {
min-width: 100%;
display: inline-flex;
flex-wrap: wrap;
padding: 0 18px !important;
}
.el-dropdown-menu__item:focus,

84
src/layout/components/sidebar/sidebarItem.vue

@ -1,11 +1,19 @@
<script setup lang="ts">
import {
ref,
PropType,
nextTick,
computed,
CSSProperties,
getCurrentInstance
} from "vue";
import path from "path";
import { PropType, ref, nextTick, getCurrentInstance } from "vue";
import { childrenType } from "../../types";
import { useAppStoreHook } from "/@/store/modules/app";
import Icon from "/@/components/ReIcon/src/Icon.vue";
import { transformI18n } from "/@/plugins/i18n";
import { findIconReg } from "/@/components/ReIcon";
import Icon from "/@/components/ReIcon/src/Icon.vue";
import { useAppStoreHook } from "/@/store/modules/app";
const instance = getCurrentInstance().appContext.app.config.globalProperties;
const menuMode = instance.$storage.layout?.layout === "vertical";
const pureApp = useAppStoreHook();
@ -24,6 +32,48 @@ const props = defineProps({
}
});
const getNoDropdownStyle = computed((): CSSProperties => {
return {
display: "flex",
alignItems: "center"
};
});
const getDivStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "" : "100%",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
overflow: "hidden"
};
});
const getMenuTextStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "125px" : "",
overflow: "hidden",
textOverflow: "ellipsis",
outline: "none"
};
});
const getSubTextStyle = computed((): CSSProperties => {
return {
width: pureApp.sidebar.opened ? "125px" : "",
display: "inline-block",
overflow: "hidden",
textOverflow: "ellipsis"
};
});
const getSpanStyle = computed((): CSSProperties => {
return {
overflow: "hidden",
textOverflow: "ellipsis"
};
});
const onlyOneChild: childrenType = ref(null);
// showTooltip
const hoverMenuMap = new WeakMap();
@ -88,7 +138,7 @@ function resolvePath(routePath) {
<el-menu-item
:index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"
style="display: flex; align-items: center"
:style="getNoDropdownStyle"
>
<el-icon v-show="props.item.meta.icon">
<component
@ -101,15 +151,7 @@ function resolvePath(routePath) {
></component>
</el-icon>
<template #title>
<div
:style="{
width: pureApp.sidebar.opened ? '' : '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
overflow: 'hidden'
}"
>
<div :style="getDivStyle">
<span v-if="!menuMode">{{
transformI18n(onlyOneChild.meta.title, onlyOneChild.meta.i18n)
}}</span>
@ -126,12 +168,7 @@ function resolvePath(routePath) {
</template>
<span
ref="menuTextRef"
:style="{
width: pureApp.sidebar.opened ? '125px' : '',
overflow: 'hidden',
textOverflow: 'ellipsis',
outline: 'none'
}"
:style="getMenuTextStyle"
@mouseover="hoverMenu(onlyOneChild)"
>
{{
@ -175,15 +212,10 @@ function resolvePath(routePath) {
</template>
<div
ref="menuTextRef"
:style="{
width: pureApp.sidebar.opened ? '125px' : '',
display: 'inline-block',
overflow: 'hidden',
textOverflow: 'ellipsis'
}"
:style="getSubTextStyle"
@mouseover="hoverMenu(props.item)"
>
<span style="overflow: hidden; text-overflow: ellipsis">
<span :style="getSpanStyle">
{{ transformI18n(props.item.meta.title, props.item.meta.i18n) }}
</span>
</div>

6
src/layout/components/sidebar/vertical.vue

@ -12,7 +12,9 @@ import { usePermissionStoreHook } from "/@/store/modules/permission";
const route = useRoute();
const pureApp = useAppStoreHook();
const router = useRouter().options.routes;
const showLogo = ref(storageLocal.getItem("logoVal") || "1");
const showLogo = ref(
storageLocal.getItem("responsive-configure")?.showLogo ?? true
);
const isCollapse = computed(() => {
return !pureApp.getSidebarStatus;
});
@ -58,7 +60,7 @@ onBeforeMount(() => {
<template>
<div :class="['sidebar-container', showLogo ? 'has-logo' : '']">
<Logo v-if="showLogo === '1'" :collapse="isCollapse" />
<Logo v-if="showLogo" :collapse="isCollapse" />
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"

1
src/layout/components/tag/index.scss

@ -207,7 +207,6 @@
li {
width: 100%;
margin: 0;
padding: 0 12px;
cursor: pointer;
display: flex;
align-items: center;

50
src/layout/components/tag/index.vue

@ -2,12 +2,13 @@
import {
ref,
watch,
onBeforeMount,
unref,
nextTick,
computed,
getCurrentInstance,
ComputedRef
ComputedRef,
CSSProperties,
onBeforeMount,
getCurrentInstance
} from "vue";
import close from "/@/assets/svg/close.svg";
@ -17,6 +18,7 @@ import closeLeft from "/@/assets/svg/close_left.svg";
import closeOther from "/@/assets/svg/close_other.svg";
import closeRight from "/@/assets/svg/close_right.svg";
import { $t as t } from "/@/plugins/i18n";
import { emitter } from "/@/utils/mitt";
import { isEqual, isEmpty } from "lodash-es";
import { transformI18n } from "/@/plugins/i18n";
@ -187,42 +189,42 @@ const handleScroll = (offset: number): void => {
const tagsViews = ref<Array<tagsViewsType>>([
{
icon: refresh,
text: "message.hsreload",
text: t("buttons.hsreload"),
divided: false,
disabled: false,
show: true
},
{
icon: close,
text: "message.hscloseCurrentTab",
text: t("buttons.hscloseCurrentTab"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeLeft,
text: "message.hscloseLeftTabs",
text: t("buttons.hscloseLeftTabs"),
divided: true,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeRight,
text: "message.hscloseRightTabs",
text: t("buttons.hscloseRightTabs"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
},
{
icon: closeOther,
text: "message.hscloseOtherTabs",
text: t("buttons.hscloseOtherTabs"),
divided: true,
disabled: multiTags.value.length > 2 ? false : true,
show: true
},
{
icon: closeAll,
text: "message.hscloseAllTabs",
text: t("buttons.hscloseAllTabs"),
divided: false,
disabled: multiTags.value.length > 1 ? false : true,
show: true
@ -230,9 +232,13 @@ const tagsViews = ref<Array<tagsViewsType>>([
]);
//
const showModel = ref(storageLocal.getItem("showModel") || "smart");
const showModel = ref(
storageLocal.getItem("responsive-configure")?.showModel || "smart"
);
if (!showModel.value) {
storageLocal.setItem("showModel", "card");
const configure = storageLocal.getItem("responsive-configure");
configure.showModel = "card";
storageLocal.setItem("responsive-configure", configure);
}
let visible = ref(false);
@ -306,7 +312,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "el-icon-s-home",
showLink: true
@ -609,17 +615,23 @@ onBeforeMount(() => {
});
});
});
const getTabStyle = computed((): CSSProperties => {
return {
transform: `translateX(${translateX.value}px)`
};
});
const getContextMenuStyle = computed((): CSSProperties => {
return { left: buttonLeft.value + "px", top: buttonTop.value + "px" };
});
</script>
<template>
<div ref="containerDom" class="tags-view" v-if="!showTags">
<i class="ri-arrow-left-s-line" @click="handleScroll(200)"></i>
<div ref="scrollbarDom" class="scroll-container">
<div
class="tab"
ref="tabDom"
:style="{ transform: `translateX(${translateX}px)` }"
>
<div class="tab" ref="tabDom" :style="getTabStyle">
<div
:ref="'dynamic' + index"
v-for="(item, index) in multiTags"
@ -663,7 +675,7 @@ onBeforeMount(() => {
<ul
v-show="visible"
:key="Math.random()"
:style="{ left: buttonLeft + 'px', top: buttonTop + 'px' }"
:style="getContextMenuStyle"
class="contextmenu"
>
<div
@ -682,7 +694,7 @@ onBeforeMount(() => {
<ul class="right-button">
<li>
<el-icon
:title="$t('message.hsrefreshRoute')"
:title="$t('buttons.hsrefreshRoute')"
class="el-icon-refresh-right rotate"
@click="onFresh"
>

18
src/layout/index.vue

@ -16,8 +16,8 @@ import { useAppStoreHook } from "/@/store/modules/app";
import fullScreen from "/@/assets/svg/full_screen.svg";
import exitScreen from "/@/assets/svg/exit_screen.svg";
import { deviceDetection } from "/@/utils/deviceDetection";
import { useSettingStoreHook } from "/@/store/modules/settings";
import { useMultiTagsStore } from "/@/store/modules/multiTags";
import { useSettingStoreHook } from "/@/store/modules/settings";
import navbar from "./components/navbar.vue";
import tag from "./components/tag/index.vue";
@ -52,16 +52,20 @@ const layout = computed(() => {
instance.$storage.layout = {
layout: instance.$config?.Layout ?? "vertical",
theme: instance.$config?.Theme ?? "default",
darkMode: instance.$config?.DarkMode ?? false
darkMode: instance.$config?.DarkMode ?? false,
sidebarStatus: instance.$config?.SidebarStatus ?? true,
epThemeColor: instance.$config?.EpThemeColor ?? "#409EFF"
};
}
//
if (!instance.$storage.sets) {
if (!instance.$storage.configure) {
// eslint-disable-next-line
instance.$storage.sets = {
instance.$storage.configure = {
grey: instance.$config?.Grey ?? false,
weak: instance.$config?.Weak ?? false,
hideTabs: instance.$config?.HideTabs ?? false,
showLogo: instance.$config?.ShowLogo ?? true,
showModel: instance.$config?.ShowModel ?? "smart",
multiTagsCache: instance.$config?.MultiTagsCache ?? false
};
}
@ -91,7 +95,7 @@ const set: setType = reactive({
}),
hideTabs: computed(() => {
return instance.$storage?.sets.hideTabs;
return instance.$storage?.configure.hideTabs;
})
});
@ -100,7 +104,9 @@ function setTheme(layoutModel: string) {
instance.$storage.layout = {
layout: `${layoutModel}`,
theme: instance.$storage.layout?.theme,
darkMode: instance.$storage.layout?.darkMode
darkMode: instance.$storage.layout?.darkMode,
sidebarStatus: instance.$storage.layout?.sidebarStatus,
epThemeColor: instance.$storage.layout?.epThemeColor
};
}

4
src/layout/types.ts

@ -3,9 +3,9 @@ export const routerArrays: Array<RouteConfigs> = [
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "el-icon-s-home",
icon: "HomeFilled",
showLink: true
}
}

22
src/plugins/element-plus/index.ts

@ -43,6 +43,7 @@ import {
ElEmpty,
ElCollapse,
ElCollapseItem,
ElTreeV2,
// 指令
ElLoading,
ElInfiniteScroll
@ -94,7 +95,8 @@ const components = [
ElAvatar,
ElEmpty,
ElCollapse,
ElCollapseItem
ElCollapseItem,
ElTreeV2
];
// https://element-plus.org/zh-CN/component/icon.html
@ -113,7 +115,14 @@ import {
ArrowDown,
Close,
CloseBold,
Bell
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
} from "@element-plus/icons-vue";
// Icon
@ -132,7 +141,14 @@ export const iconComponents = [
ArrowDown,
Close,
CloseBold,
Bell
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
];
export function useElementPlus(app: App) {

98
src/plugins/i18n/config.ts

@ -1,104 +1,20 @@
import { siphonI18n } from "./index";
// element-plus国际化
import enLocale from "element-plus/lib/locale/lang/en";
import zhLocale from "element-plus/lib/locale/lang/zh-cn";
// 导航菜单配置
export const menusConfig = {
zh: {
message: {
hshome: "首页",
hserror: "错误页面",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
permission: "权限管理",
permissionPage: "页面权限",
permissionButton: "按钮权限",
externalLink: "外链"
}
},
en: {
message: {
hshome: "Home",
hserror: "Error Page",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
permission: "Permission Manage",
permissionPage: "Page Permission",
permissionButton: "Button Permission",
externalLink: "External Link"
}
}
};
// 按钮配置
export const buttonConfig = {
zh: {
message: {
hsLoginOut: "退出系统",
hsfullscreen: "全屏",
hsexitfullscreen: "退出全屏",
hsrefreshRoute: "刷新路由",
hslogin: "登陆",
hsadd: "新增",
hsmark: "标记/取消",
hssave: "保存",
hssearch: "搜索",
hsexpendAll: "全部展开",
hscollapseAll: "全部折叠",
hssystemSet: "打开项目配置",
hsdelete: "删除",
hsreload: "重新加载",
hscloseCurrentTab: "关闭当前标签页",
hscloseLeftTabs: "关闭左侧标签页",
hscloseRightTabs: "关闭右侧标签页",
hscloseOtherTabs: "关闭其他标签页",
hscloseAllTabs: "关闭全部标签页"
}
},
en: {
message: {
hsLoginOut: "loginOut",
hsfullscreen: "fullScreen",
hsexitfullscreen: "exitFullscreen",
hsrefreshRoute: "refreshRoute",
hslogin: "login",
hsadd: "Add",
hsmark: "Mark/Cancel",
hssave: "Save",
hssearch: "Search",
hsexpendAll: "Expand All",
hscollapseAll: "Collapse All",
hssystemSet: "Open ProjectConfig",
hsdelete: "Delete",
hsreload: "Reload",
hscloseCurrentTab: "Close CurrentTab",
hscloseLeftTabs: "Close LeftTabs",
hscloseRightTabs: "Close RightTabs",
hscloseOtherTabs: "Close OtherTabs",
hscloseAllTabs: "Close AllTabs"
}
}
};
// 配置
// export const xxxx = {
// zh: {
// message: {},
// },
// en: {
// message: {},
// },
// };
const localesList = [menusConfig, buttonConfig];
// 项目内自定义国际化
const zhModules = import.meta.globEager("./zh-CN/**/*.ts");
const enModules = import.meta.globEager("./en/**/*.ts");
export const localesConfigs = {
zh: {
message: Object.assign({}, ...localesList.map(v => v.zh.message)),
...siphonI18n(zhModules, "zh-CN"),
...zhLocale
},
en: {
message: Object.assign({}, ...localesList.map(v => v.en.message)),
...siphonI18n(enModules, "en"),
...enLocale
}
};

21
src/plugins/i18n/en/buttons.ts

@ -0,0 +1,21 @@
export default {
hsLoginOut: "LoginOut",
hsfullscreen: "FullScreen",
hsexitfullscreen: "ExitFullscreen",
hsrefreshRoute: "RefreshRoute",
hslogin: "Login",
hsadd: "Add",
hsmark: "Mark/Cancel",
hssave: "Save",
hssearch: "Search",
hsexpendAll: "Expand All",
hscollapseAll: "Collapse All",
hssystemSet: "Open ProjectConfig",
hsdelete: "Delete",
hsreload: "Reload",
hscloseCurrentTab: "Close CurrentTab",
hscloseLeftTabs: "Close LeftTabs",
hscloseRightTabs: "Close RightTabs",
hscloseOtherTabs: "Close OtherTabs",
hscloseAllTabs: "Close AllTabs"
};

38
src/plugins/i18n/en/menus.ts

@ -0,0 +1,38 @@
export default {
hshome: "Home",
hslogin: "Login",
hssysManagement: "System Manage",
hsBaseinfo: "Base Info",
hsDict: "Dict Manage",
hseditor: "Editor",
hserror: "Error Page",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
hscomponents: "Components",
hsvideo: "Video Components",
hsmap: "Map Components",
hsdraggable: "Draggable Components",
hssplitPane: "Split Pane",
hsbutton: "Button Components",
hscropping: "Picture Cropping",
hscountTo: "Digital Animation",
hsselector: "Selector Components",
hsflowChart: "Flow Chart",
hsseamless: "Seamless Scroll",
hscontextmenu: "Context Menu",
hsmenus: "MultiLevel Menu",
hsmenu1: "Menu1",
"hsmenu1-1": "Menu1-1",
"hsmenu1-2": "Menu1-2",
"hsmenu1-2-1": "Menu1-2-1",
"hsmenu1-2-2": "Menu1-2-2",
"hsmenu1-3": "Menu1-3",
hsmenu2: "Menu2",
permission: "Permission Manage",
permissionPage: "Page Permission",
permissionButton: "Button Permission",
hstabs: "Tabs Operate",
hsMenuTree: "Menu Tree",
hsguide: "Guide",
externalLink: "External Link"
};

55
src/plugins/i18n/index.ts

@ -1,19 +1,10 @@
// 多组件库的国际化和本地项目国际化兼容
import { App } from "vue";
import { set } from "lodash-es";
import { createI18n } from "vue-i18n";
import { localesConfigs } from "./config";
import { storageLocal } from "/@/utils/storage";
export const i18n = createI18n({
locale: storageLocal.getItem("responsive-locale")?.locale ?? "zh",
fallbackLocale: "en",
messages: localesConfigs
});
export function usI18n(app: App) {
app.use(i18n);
}
/**
*
* @param message message
@ -37,3 +28,47 @@ export function transformI18n(message: string | object = "", isI18n = false) {
return message;
}
}
/**
*
* @param langs
* @param prefix zh-CN
* @returns obj {.**}
*/
export function siphonI18n(
langs: Record<string, Record<string, any>>,
prefix = "zh-CN"
) {
const langsObj: Recordable = {};
Object.keys(langs).forEach((key: string) => {
let fileName = key.replace(`./${prefix}/`, "").replace(/^\.\//, "");
fileName = fileName.substring(0, fileName.lastIndexOf("."));
const keyList = fileName.split("/");
const moduleName = keyList.shift();
const objKey = keyList.join(".");
const langFileModule = langs[key].default;
if (moduleName) {
if (objKey) {
set(langsObj, moduleName, langsObj[moduleName] || {});
set(langsObj[moduleName], objKey, langFileModule);
} else {
set(langsObj, moduleName, langFileModule || {});
}
}
});
return langsObj;
}
// 此函数只是配合i18n Ally插件来进行国际化智能提示,并无实际意义(只对提示起作用),如果不需要国际化可删除
export const $t = (key: string) => key;
export const i18n = createI18n({
locale: storageLocal.getItem("responsive-locale")?.locale ?? "zh",
fallbackLocale: "en",
messages: localesConfigs
});
export function usI18n(app: App) {
app.use(i18n);
}

21
src/plugins/i18n/zh-CN/buttons.ts

@ -0,0 +1,21 @@
export default {
hsLoginOut: "退出系统",
hsfullscreen: "全屏",
hsexitfullscreen: "退出全屏",
hsrefreshRoute: "刷新路由",
hslogin: "登陆",
hsadd: "新增",
hsmark: "标记/取消",
hssave: "保存",
hssearch: "搜索",
hsexpendAll: "全部展开",
hscollapseAll: "全部折叠",
hssystemSet: "打开项目配置",
hsdelete: "删除",
hsreload: "重新加载",
hscloseCurrentTab: "关闭当前标签页",
hscloseLeftTabs: "关闭左侧标签页",
hscloseRightTabs: "关闭右侧标签页",
hscloseOtherTabs: "关闭其他标签页",
hscloseAllTabs: "关闭全部标签页"
};

38
src/plugins/i18n/zh-CN/menus.ts

@ -0,0 +1,38 @@
export default {
hshome: "首页",
hslogin: "登陆",
hssysManagement: "系统管理",
hsBaseinfo: "基础信息",
hsDict: "字典管理",
hseditor: "编辑器",
hserror: "错误页面",
hsfourZeroFour: "404",
hsfourZeroOne: "401",
hscomponents: "组件",
hsvideo: "视频组件",
hsmap: "地图组件",
hsdraggable: "拖拽组件",
hssplitPane: "切割面板",
hsbutton: "按钮组件",
hscropping: "图片裁剪",
hscountTo: "数字动画",
hsselector: "选择器组件",
hsflowChart: "流程图",
hsseamless: "无缝滚动",
hscontextmenu: "右键菜单",
hsmenus: "多级菜单",
hsmenu1: "菜单1",
"hsmenu1-1": "菜单1-1",
"hsmenu1-2": "菜单1-2",
"hsmenu1-2-1": "菜单1-2-1",
"hsmenu1-2-2": "菜单1-2-2",
"hsmenu1-3": "菜单1-3",
hsmenu2: "菜单2",
permission: "权限管理",
permissionPage: "页面权限",
permissionButton: "按钮权限",
hstabs: "标签页操作",
hsMenuTree: "菜单树结构",
hsguide: "引导页",
externalLink: "外链"
};

3
src/router/index.ts

@ -117,8 +117,9 @@ router.beforeEach((to: toRouteType, _from, next) => {
route?.meta?.rank !== 0 &&
routePartent.length === 0
) {
if (!route?.meta?.refreshRedirect) return;
const { name, meta } = findRouteByPath(
route?.meta?.refreshRedirect,
route.meta.refreshRedirect,
routes
);
handTag(

7
src/router/modules/error.ts

@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const errorRouter = {
@ -7,7 +8,7 @@ const errorRouter = {
redirect: "/error/401",
meta: {
icon: "Position",
title: "message.hserror",
title: $t("menus.hserror"),
showLink: true,
i18n: true,
rank: 7
@ -18,7 +19,7 @@ const errorRouter = {
name: "401",
component: () => import("/@/views/error/401.vue"),
meta: {
title: "message.hsfourZeroOne",
title: $t("menus.hsfourZeroOne"),
i18n: true,
showLink: true
}
@ -28,7 +29,7 @@ const errorRouter = {
name: "404",
component: () => import("/@/views/error/404.vue"),
meta: {
title: "message.hsfourZeroFour",
title: $t("menus.hsfourZeroFour"),
i18n: true,
showLink: true
}

5
src/router/modules/externalLink.ts

@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const externalLink = {
@ -6,7 +7,7 @@ const externalLink = {
component: Layout,
meta: {
icon: "Link",
title: "message.externalLink",
title: $t("menus.externalLink"),
showLink: true,
i18n: true,
rank: 190
@ -15,7 +16,7 @@ const externalLink = {
{
path: "https://github.com/xiaoxian521/vue-pure-admin",
meta: {
title: "message.externalLink",
title: $t("menus.externalLink"),
showLink: true,
i18n: true,
rank: 191

4
src/router/modules/home.ts

@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const homeRouter = {
@ -7,6 +8,7 @@ const homeRouter = {
redirect: "/welcome",
meta: {
icon: "HomeFilled",
title: $t("menus.hshome"),
showLink: true,
i18n: true,
rank: 0
@ -17,7 +19,7 @@ const homeRouter = {
name: "welcome",
component: () => import("/@/views/welcome.vue"),
meta: {
title: "message.hshome",
title: $t("menus.hshome"),
i18n: true,
showLink: true
}

5
src/router/modules/remaining.ts

@ -1,3 +1,4 @@
import { $t } from "/@/plugins/i18n";
import Layout from "/@/layout/index.vue";
const remainingRouter = [
@ -6,7 +7,7 @@ const remainingRouter = [
name: "login",
component: () => import("/@/views/login.vue"),
meta: {
title: "message.hslogin",
title: $t("menus.hslogin"),
showLink: false,
i18n: true,
rank: 101
@ -18,7 +19,7 @@ const remainingRouter = [
component: Layout,
meta: {
icon: "HomeFilled",
title: "message.hshome",
title: $t("menus.hshome"),
i18n: true,
showLink: false,
rank: 104

6
src/router/utils.ts

@ -13,7 +13,7 @@ import { useTimeoutFn } from "@vueuse/core";
import { RouteConfigs } from "/@/layout/types";
import { usePermissionStoreHook } from "/@/store/modules/permission";
// https://cn.vitejs.dev/guide/features.html#glob-import
const modulesRoutes = import.meta.glob("/src/views/*/*/*.vue");
const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
// 动态路由
import { getAsyncRoutes } from "/@/api/routes";
@ -212,11 +212,13 @@ const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => {
// 过滤后端传来的动态路由 重新生成规范路由
const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => {
if (!arrRoutes || !arrRoutes.length) return;
const modulesRoutesKeys = Object.keys(modulesRoutes);
arrRoutes.forEach((v: RouteRecordRaw) => {
if (v.redirect) {
v.component = Layout;
} else {
v.component = modulesRoutes[`/src/views${v.path}/index.vue`];
const index = modulesRoutesKeys.findIndex(ev => ev.includes(v.path));
v.component = modulesRoutes[modulesRoutesKeys[index]];
}
if (v.children) {
addAsyncRoutes(v.children);

18
src/store/modules/app.ts

@ -1,17 +1,17 @@
import { storageLocal } from "/@/utils/storage";
import { deviceDetection } from "/@/utils/deviceDetection";
import { store } from "/@/store";
import { appType } from "./types";
import { defineStore } from "pinia";
import { getConfig } from "/@/config";
import { storageLocal } from "/@/utils/storage";
import { deviceDetection } from "/@/utils/deviceDetection";
export const useAppStore = defineStore({
id: "pure-app",
state: (): appType => ({
sidebar: {
opened: storageLocal.getItem("sidebarStatus")
? !!+storageLocal.getItem("sidebarStatus")
: true,
opened:
storageLocal.getItem("responsive-layout")?.sidebarStatus ??
getConfig().SidebarStatus,
withoutAnimation: false,
isClickHamburger: false
},
@ -30,20 +30,22 @@ export const useAppStore = defineStore({
},
actions: {
TOGGLE_SIDEBAR(opened?: boolean, resize?: string) {
const layout = storageLocal.getItem("responsive-layout");
if (opened && resize) {
this.sidebar.withoutAnimation = true;
this.sidebar.opened = true;
storageLocal.setItem("sidebarStatus", true);
layout.sidebarStatus = true;
} else if (!opened && resize) {
this.sidebar.withoutAnimation = true;
this.sidebar.opened = false;
storageLocal.setItem("sidebarStatus", false);
layout.sidebarStatus = false;
} else if (!opened && !resize) {
this.sidebar.withoutAnimation = false;
this.sidebar.opened = !this.sidebar.opened;
this.sidebar.isClickHamburger = !this.sidebar.opened;
storageLocal.setItem("sidebarStatus", this.sidebar.opened);
layout.sidebarStatus = this.sidebar.opened;
}
storageLocal.setItem("responsive-layout", layout);
},
TOGGLE_DEVICE(device: string) {
this.device = device;

9
src/store/modules/epTheme.ts

@ -1,11 +1,14 @@
import { store } from "/@/store";
import { defineStore } from "pinia";
import { getConfig } from "/@/config";
import { storageLocal } from "/@/utils/storage";
export const useEpThemeStore = defineStore({
id: "pure-epTheme",
state: () => ({
epThemeColor: storageLocal.getItem("epThemeColor") || "#409EFF"
epThemeColor:
storageLocal.getItem("responsive-layout")?.epThemeColor ??
getConfig().EpThemeColor
}),
getters: {
getEpThemeColor() {
@ -14,8 +17,10 @@ export const useEpThemeStore = defineStore({
},
actions: {
setEpThemeColor(newColor) {
const layout = storageLocal.getItem("responsive-layout");
this.epThemeColor = newColor;
storageLocal.setItem("epThemeColor", newColor);
layout.epThemeColor = newColor;
storageLocal.setItem("responsive-layout", layout);
}
}
});

8
src/store/modules/multiTags.ts

@ -8,21 +8,21 @@ export const useMultiTagsStore = defineStore({
id: "pure-multiTags",
state: () => ({
// 存储标签页信息(路由信息)
multiTags: storageLocal.getItem("responsive-sets").multiTagsCache
multiTags: storageLocal.getItem("responsive-configure").multiTagsCache
? storageLocal.getItem("responsive-tags")
: [
{
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}
}
],
multiTagsCache: storageLocal.getItem("responsive-sets").multiTagsCache
multiTagsCache: storageLocal.getItem("responsive-configure").multiTagsCache
}),
getters: {
getMultiTagsCache() {

4
src/store/modules/user.ts

@ -58,8 +58,8 @@ export const useUserStore = defineStore({
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
icon: "el-icon-s-home",
title: "menus.hshome",
icon: "HomeFilled",
i18n: true,
showLink: true
}

12
src/utils/storage/responsive.ts

@ -18,15 +18,19 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
default: Storage.getData(undefined, "layout") ?? {
layout: config.Layout ?? "vertical",
theme: config.Theme ?? "default",
darkMode: config.DarkMode ?? false
darkMode: config.DarkMode ?? false,
sidebarStatus: config.SidebarStatus ?? true,
epThemeColor: config.EpThemeColor ?? "409EFF"
}
},
sets: {
configure: {
type: Object,
default: Storage.getData(undefined, "sets") ?? {
default: Storage.getData(undefined, "configure") ?? {
grey: config.Grey ?? false,
weak: config.Weak ?? false,
hideTabs: config.HideTabs ?? false,
showLogo: config.ShowLogo ?? true,
showModel: config.ShowModel ?? "smart",
multiTagsCache: config.MultiTagsCache ?? false
}
}
@ -41,7 +45,7 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
path: "/welcome",
parentPath: "/",
meta: {
title: "message.hshome",
title: "menus.hshome",
i18n: true,
icon: "HomeFilled",
showLink: true

4
types/global.d.ts

@ -86,6 +86,10 @@ declare global {
Grey?: boolean;
Weak?: boolean;
HideTabs?: boolean;
SidebarStatus?: boolean;
EpThemeColor?: string;
ShowLogo?: boolean;
ShowModel?: string;
MapConfigure?: {
amapKey?: string;
options: {

Loading…
Cancel
Save