Compare commits

..

19 Commits

Author SHA1 Message Date
xiaoxian521
248fbf26ed release: update 2.9.0 2022-02-05 14:51:00 +08:00
xiaoxian521
d943550e10 chore: 重构图标 2022-02-05 14:45:20 +08:00
xiaoxian521
3a7832b7fe chore: update 2022-01-24 15:15:46 +08:00
xiaoxian521
ad3f964c32 fix: vite@2.7.0-beta.8 build incompatible template 2022-01-21 18:26:52 +08:00
xiaoxian521
5ee4add259 release: update 2.8.5 2022-01-21 16:48:20 +08:00
xiaoxian521
433ea6fa44 chore: update typescript 2022-01-21 16:29:23 +08:00
xiaoxian521
11514da24b chore: add @vueuse/shared 2022-01-21 14:50:23 +08:00
xiaoxian521
45025f668a perf: router 2022-01-21 14:03:00 +08:00
xiaoxian521
64eef1fc66 refactor: use @iconify-icons/ep 2022-01-21 11:32:58 +08:00
xiaoxian521
d7ea59194f chore: update 2022-01-20 00:00:25 +08:00
xiaoxian521
cb71468277 fix: update vscode extensions 2022-01-19 21:39:53 +08:00
xiaoxian521
a06bdad2b8 fix: update lintstagedrc 2022-01-18 17:09:07 +08:00
xiaoxian521
32af28fb9f fix: vscode settings.json 2022-01-18 17:00:30 +08:00
xiaoxian521
b8ada350a9 fix: vscode code-snippets 2022-01-18 16:59:35 +08:00
xiaoxian521
c472772c54 fix: add vite-plugin-live-reload 2022-01-09 12:14:17 +08:00
xiaoxian521
6a8c654936 chore: update vite-plugin-remove-console 2022-01-07 17:52:35 +08:00
xiaoxian521
1f07597320 feat: 添加WindiCSS支持 2022-01-07 17:26:05 +08:00
xiaoxian521
2d7debadff feat: 添加线上环境删console插件vite-plugin-remove-console 2022-01-07 15:29:14 +08:00
xiaoxian521
0bdaf55ff1 perf: 同步完整版分支代码 2022-01-07 15:08:21 +08:00
69 changed files with 1752 additions and 1800 deletions

View File

@@ -1,10 +1,8 @@
module.exports = { module.exports = {
"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"], "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
"{!(package)*.json,./vscode/*.code-snippets,.!(browserslist)*rc}": [ "{!(package)*.json,.!(browserslist)*rc}": ["prettier --write--parser json"],
"prettier --write--parser json"
],
"package.json": ["prettier --write"], "package.json": ["prettier --write"],
"*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"], "*.vue": ["eslint --fix", "prettier --write", "stylelint --fix"],
"*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"], "*.{vue,css,scss,postcss,less}": ["stylelint --fix", "prettier --write"],
"*.md": ["prettier --write"] "*.md": ["prettier --write"]
}; };

View File

@@ -1,11 +1,16 @@
{ {
"recommendations": [ "recommendations": [
"johnsoncodehk.vscode-typescript-vue-plugin",
"voorjaar.windicss-intellisense",
"vscode-icons-team.vscode-icons",
"davidanson.vscode-markdownlint",
"stylelint.vscode-stylelint", "stylelint.vscode-stylelint",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"johnsoncodehk.volar", "johnsoncodehk.volar",
"lokalise.i18n-ally", "lokalise.i18n-ally",
"mikestead.dotenv", "mikestead.dotenv",
"eamodio.gitlens",
"antfu.iconify" "antfu.iconify"
] ]
} }

17
.vscode/settings.json vendored
View File

@@ -1,15 +1,4 @@
{ {
/** 便
* 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.formatOnType": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"javascript.updateImportsOnFileMove.enabled": "always", "javascript.updateImportsOnFileMove.enabled": "always",
@@ -27,26 +16,20 @@
"editor.suggestSelection": "first", "editor.suggestSelection": "first",
"editor.acceptSuggestionOnCommitCharacter": false, "editor.acceptSuggestionOnCommitCharacter": false,
"css.lint.propertyIgnoredDueToDisplay": "ignore", "css.lint.propertyIgnoredDueToDisplay": "ignore",
// Prevent inline styles from being automatically formatted to all lowercase
"editor.quickSuggestions": { "editor.quickSuggestions": {
"other": true, "other": true,
"comments": true, "comments": true,
"strings": true "strings": true
}, },
// Automatically fix some syntax errors of ts
"tslint.autoFixOnSave": true,
"files.associations": { "files.associations": {
// Specifies the location of snippets in the suggestion widget
"editor.snippetSuggestions": "top" "editor.snippetSuggestions": "top"
}, },
"[css]": { "[css]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" "editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"cSpell.userWords": ["sourcemap", "vite"],
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": true "source.fixAll.eslint": true
}, },
"volar.tsPlugin": true,
"typescript.tsdk": "node_modules/typescript/lib", "typescript.tsdk": "node_modules/typescript/lib",
"i18n-ally.localesPaths": ["src/plugins/i18n"], "i18n-ally.localesPaths": ["src/plugins/i18n"],
"i18n-ally.keystyle": "nested", "i18n-ally.keystyle": "nested",

View File

@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2021 啝裳 Copyright (c) 2022 啝裳
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@@ -6,11 +6,16 @@
## introduce ## introduce
The lite version is based on the shelf extracted from https://github.com/xiaoxian521/vue-pure-admin, which contains the main functions and is more suitable for actual project development The lite version is based on the shelf extracted from [vue-pure-admin](https://github.com/xiaoxian521/vue-pure-admin), which contains the main functions and is more suitable for actual project development, the packaged size is only more than `2MB`
## Supporting Video
- [Click Watch Tutorial](https://www.bilibili.com/video/BV1534y1S7HV)
- [Click Watch UI Design](https://www.bilibili.com/video/BV17g411T7rq)
## Docs ## Docs
<https://pure-admin-doc.vercel.app/> - [Click Watch Docs](https://pure-admin-doc.vercel.app)
## Usage ## Usage

View File

@@ -6,16 +6,16 @@
## 介绍 ## 介绍
精简版是基于 https://github.com/xiaoxian521/vue-pure-admin 提炼出的架子,包含主体功能,更适合实际项目开发 精简版是基于[vue-pure-admin](https://github.com/xiaoxian521/vue-pure-admin)提炼出的架子,包含主体功能,更适合实际项目开发,打包后的大小仅 `2MB`
## 配套视频 ## 配套视频
教程:<https://www.bilibili.com/video/BV1534y1S7HV/> - [点我查看教程](https://www.bilibili.com/video/BV1534y1S7HV)
UI 设计:<https://www.bilibili.com/video/BV17g411T7rq/> - [点我查看 UI 设计](https://www.bilibili.com/video/BV17g411T7rq)
## 配套文档 ## 配套文档
<https://pure-admin-doc.vercel.app/> - [点我查看文档](https://pure-admin-doc.vercel.app)
## 维护者 ## 维护者

View File

@@ -1,6 +0,0 @@
const productPlugins = [];
process.env.NODE_ENV === "production" &&
productPlugins.push("transform-remove-console");
module.exports = {
plugins: [...productPlugins]
};

102
build/plugins.ts Normal file
View File

@@ -0,0 +1,102 @@
import vue from "@vitejs/plugin-vue";
import svgLoader from "vite-svg-loader";
import legacy from "@vitejs/plugin-legacy";
import vueJsx from "@vitejs/plugin-vue-jsx";
import WindiCSS from "vite-plugin-windicss";
import { viteMockServe } from "vite-plugin-mock";
import liveReload from "vite-plugin-live-reload";
import ElementPlus from "unplugin-element-plus/vite";
import removeConsole from "vite-plugin-remove-console";
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor";
export function getPluginsList(command, VITE_LEGACY) {
const prodMock = true;
return [
vue(),
// jsx、tsx语法支持
vueJsx(),
WindiCSS(),
// 线上环境删除console
removeConsole(),
// 修改layout文件夹下的文件时自动重载浏览器 解决 https://github.com/xiaoxian521/vue-pure-admin/issues/170
liveReload(["src/layout/**/*", "src/router/**/*"]),
// 自定义主题
themePreprocessorPlugin({
scss: {
multipleScopeVars: [
{
scopeName: "layout-theme-default",
path: "src/layout/theme/default-vars.scss"
},
{
scopeName: "layout-theme-light",
path: "src/layout/theme/light-vars.scss"
},
{
scopeName: "layout-theme-dusk",
path: "src/layout/theme/dusk-vars.scss"
},
{
scopeName: "layout-theme-volcano",
path: "src/layout/theme/volcano-vars.scss"
},
{
scopeName: "layout-theme-yellow",
path: "src/layout/theme/yellow-vars.scss"
},
{
scopeName: "layout-theme-mingQing",
path: "src/layout/theme/mingQing-vars.scss"
},
{
scopeName: "layout-theme-auroraGreen",
path: "src/layout/theme/auroraGreen-vars.scss"
},
{
scopeName: "layout-theme-pink",
path: "src/layout/theme/pink-vars.scss"
},
{
scopeName: "layout-theme-saucePurple",
path: "src/layout/theme/saucePurple-vars.scss"
}
],
// 默认取 multipleScopeVars[0].scopeName
defaultScopeName: "",
// 在生产模式是否抽取独立的主题css文件extract为true以下属性有效
extract: true,
// 独立主题css文件的输出路径默认取 viteConfig.build.assetsDir 相对于 (viteConfig.build.outDir)
outputDir: "",
// 会选取defaultScopeName对应的主题css文件在html添加link
themeLinkTagId: "head",
// "head"||"head-prepend" || "body" ||"body-prepend"
themeLinkTagInjectTo: "head",
// 是否对抽取的css文件内对应scopeName的权重类名移除
removeCssScopeName: false,
// 可以自定义css文件名称的函数
customThemeCssFileName: scopeName => scopeName
}
}),
// svg组件化支持
svgLoader(),
ElementPlus({}),
// mock支持
viteMockServe({
mockPath: "mock",
localEnabled: command === "serve",
prodEnabled: command !== "serve" && prodMock,
injectCode: `
import { setupProdMockServer } from './mockProdServer';
setupProdMockServer();
`,
logger: true
}),
// 是否为打包后的文件提供传统浏览器兼容性支持
VITE_LEGACY
? legacy({
targets: ["ie >= 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"]
})
: null
];
}

View File

@@ -3,7 +3,6 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="/iconfont.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>pure-admin-thin</title> <title>pure-admin-thin</title>
<script> <script>
@@ -14,35 +13,89 @@
<body> <body>
<div id="app"> <div id="app">
<style> <style>
p { * {
position: absolute; margin: 0;
top: 50%; padding: 0;
left: 50%;
transform: translate(-50%, -50%);
font-size: 26px;
} }
p::after { html,
body {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
font-family: "Reggae One", cursive;
}
.loader,
.loader:before,
.loader:after {
border-radius: 50%;
width: 2.5em;
height: 2.5em;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
-webkit-animation: loadAnimation 1.8s infinite ease-in-out;
animation: loadAnimation 1.8s infinite ease-in-out;
}
.loader {
color: #406eeb;
font-size: 10px;
margin: 80px auto;
position: relative;
text-indent: -9999em;
-webkit-transform: translateZ(0);
-ms-transform: translateZ(0);
transform: translateZ(0);
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
.loader:before,
.loader:after {
content: ""; content: "";
position: absolute; position: absolute;
top: 0%; top: 0;
bottom: 0;
animation: dot 1s infinite steps(1, start);
} }
@keyframes dot { .loader:before {
33.33% { left: -3.5em;
content: "."; -webkit-animation-delay: -0.32s;
} animation-delay: -0.32s;
66.67% { }
content: "..";
} .loader:after {
left: 3.5em;
}
@-webkit-keyframes loadAnimation {
0%,
80%,
100% { 100% {
content: "..."; box-shadow: 0 2.5em 0 -1.3em;
}
40% {
box-shadow: 0 2.5em 0 0;
}
}
@keyframes loadAnimation {
0%,
80%,
100% {
box-shadow: 0 2.5em 0 -1.3em;
}
40% {
box-shadow: 0 2.5em 0 0;
} }
} }
</style> </style>
<p>Loading</p> <div class="loader">Loading...</div>
</div> </div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>

View File

@@ -7,9 +7,8 @@ const permissionRouter = {
redirect: "/permission/page/index", redirect: "/permission/page/index",
meta: { meta: {
title: "menus.permission", title: "menus.permission",
icon: "Lollipop", icon: "lollipop",
i18n: true, i18n: true,
showLink: true,
rank: 3 rank: 3
}, },
children: [ children: [
@@ -18,8 +17,7 @@ const permissionRouter = {
name: "permissionPage", name: "permissionPage",
meta: { meta: {
title: "menus.permissionPage", title: "menus.permissionPage",
i18n: true, i18n: true
showLink: true
} }
}, },
{ {
@@ -28,7 +26,6 @@ const permissionRouter = {
meta: { meta: {
title: "menus.permissionButton", title: "menus.permissionButton",
i18n: true, i18n: true,
showLink: true,
authority: [] authority: []
} }
} }

View File

@@ -1,6 +1,6 @@
{ {
"name": "pure-admin-thin", "name": "pure-admin-thin",
"version": "2.8.0", "version": "2.9.0",
"private": true, "private": true,
"engines": { "engines": {
"node": ">= 16", "node": ">= 16",
@@ -18,7 +18,7 @@
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/", "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js", "lint:lint-staged": "lint-staged -c ./.husky/lintstagedrc.js",
"lint:pretty": "pretty-quick --staged", "lint:pretty": "pretty-quick --staged",
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint && pnpm lint:pretty", "lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
"prepare": "husky install", "prepare": "husky install",
"preinstall": "npx only-allow pnpm" "preinstall": "npx only-allow pnpm"
}, },
@@ -29,40 +29,38 @@
], ],
"dependencies": { "dependencies": {
"@ctrl/tinycolor": "^3.4.0", "@ctrl/tinycolor": "^3.4.0",
"@element-plus/icons-vue": "^0.2.4", "@vueuse/core": "^7.5.5",
"@fortawesome/fontawesome-svg-core": "^1.2.36", "@vueuse/motion": "^2.0.0-beta.9",
"@fortawesome/free-solid-svg-icons": "^5.15.4", "@vueuse/shared": "^7.5.5",
"@fortawesome/vue-fontawesome": "^3.0.0-5",
"@vue/compiler-sfc": "^3.2.24",
"@vueuse/core": "^6.7.1",
"@vueuse/motion": "^2.0.0-beta.4",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^0.21.1", "axios": "^0.25.0",
"css-color-function": "^1.3.3", "css-color-function": "^1.3.3",
"dayjs": "^1.10.7",
"element-plus": "1.3.0-beta.1", "element-plus": "1.3.0-beta.1",
"element-resize-detector": "^1.2.3", "element-resize-detector": "^1.2.3",
"font-awesome": "^4.7.0",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mitt": "^3.0.0", "mitt": "^3.0.0",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path": "^0.12.7", "path": "^0.12.7",
"pinia": "^2.0.0-rc.14", "pinia": "^2.0.11",
"qs": "^6.10.2", "qs": "^6.10.2",
"remixicon": "^2.5.0",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"responsive-storage": "^1.0.11", "responsive-storage": "^1.0.11",
"rgb-hex": "^4.0.0", "rgb-hex": "^4.0.0",
"vue": "^3.2.24", "vue": "^3.2.29",
"vue-i18n": "^9.2.0-beta.3", "vue-i18n": "^9.2.0-beta.30",
"vue-router": "^4.0.12", "vue-router": "^4.0.12",
"vue-types": "^4.1.0" "vue-types": "^4.1.1"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "13.1.0", "@commitlint/cli": "13.1.0",
"@commitlint/config-conventional": "13.1.0", "@commitlint/config-conventional": "13.1.0",
"@iconify-icons/ep": "^1.1.3",
"@iconify-icons/fa": "^1.1.1",
"@iconify-icons/fa-solid": "^1.1.2",
"@iconify-icons/ri": "^1.1.1",
"@iconify/vue": "^3.1.3",
"@types/element-resize-detector": "1.1.3", "@types/element-resize-detector": "1.1.3",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.1",
"@types/mockjs": "1.0.3", "@types/mockjs": "1.0.3",
@@ -72,13 +70,12 @@
"@typescript-eslint/eslint-plugin": "4.31.0", "@typescript-eslint/eslint-plugin": "4.31.0",
"@typescript-eslint/parser": "4.31.0", "@typescript-eslint/parser": "4.31.0",
"@vitejs/plugin-legacy": "^1.6.4", "@vitejs/plugin-legacy": "^1.6.4",
"@vitejs/plugin-vue": "^1.10.2", "@vitejs/plugin-vue": "^2.1.0",
"@vitejs/plugin-vue-jsx": "^1.3.1", "@vitejs/plugin-vue-jsx": "^1.3.3",
"@vue/eslint-config-prettier": "6.0.0", "@vue/eslint-config-prettier": "6.0.0",
"@vue/eslint-config-typescript": "7.0.0", "@vue/eslint-config-typescript": "7.0.0",
"@zougt/vite-plugin-theme-preprocessor": "^1.4.0", "@zougt/vite-plugin-theme-preprocessor": "^1.4.4",
"autoprefixer": "10.2.4", "autoprefixer": "^10.4.2",
"babel-plugin-transform-remove-console": "6.9.4",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint": "7.30.0", "eslint": "7.30.0",
"eslint-plugin-prettier": "3.4.0", "eslint-plugin-prettier": "3.4.0",
@@ -90,19 +87,23 @@
"prettier": "2.3.2", "prettier": "2.3.2",
"pretty-quick": "3.1.1", "pretty-quick": "3.1.1",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"sass": "^1.45.0", "sass": "^1.49.0",
"sass-loader": "^12.3.0", "sass-loader": "^12.4.0",
"stylelint": "13.13.1", "stylelint": "13.13.1",
"stylelint-config-prettier": "8.0.2", "stylelint-config-prettier": "8.0.2",
"stylelint-config-standard": "22.0.0", "stylelint-config-standard": "22.0.0",
"stylelint-order": "4.1.0", "stylelint-order": "4.1.0",
"typescript": "4.4.2", "typescript": "^4.5.5",
"unplugin-element-plus": "^0.1.3", "unplugin-element-plus": "^0.2.0",
"vite": "2.6.14", "vite": "^2.7.13",
"vite-plugin-live-reload": "^2.1.0",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
"vite-plugin-style-import": "^1.2.1", "vite-plugin-remove-console": "^0.0.6",
"vite-svg-loader": "^2.2.0", "vite-plugin-style-import": "^1.4.1",
"vue-eslint-parser": "7.10.0" "vite-plugin-windicss": "^1.6.3",
"vite-svg-loader": "2.2.0",
"vue-eslint-parser": "7.10.0",
"windicss": "^3.4.3"
}, },
"repository": "git@github.com:xiaoxian521/vue-pure-admin.git", "repository": "git@github.com:xiaoxian521/vue-pure-admin.git",
"author": "xiaoxian521", "author": "xiaoxian521",

1915
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,18 +0,0 @@
@font-face {
font-family: "iconfont"; /* project id 1098500 */
src: url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.eot");
src: url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.eot?#iefix")
format("embedded-opentype"),
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.woff2") format("woff2"),
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.woff") format("woff"),
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.ttf") format("truetype"),
url("//at.alicdn.com/t/font_1098500_3d6un9zwltz.svg#iconfont") format("svg");
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -1,5 +1,5 @@
{ {
"Version": "2.8.0", "Version": "2.9.0",
"Title": "PureAdmin", "Title": "PureAdmin",
"FixedHeader": true, "FixedHeader": true,
"HiddenSideBar": false, "HiddenSideBar": false,
@@ -15,13 +15,5 @@
"SidebarStatus": true, "SidebarStatus": true,
"EpThemeColor": "#409EFF", "EpThemeColor": "#409EFF",
"ShowLogo": true, "ShowLogo": true,
"ShowModel": "smart", "ShowModel": "smart"
"MapConfigure": {
"amapKey": "97b3248d1553172e81f168cf94ea667e",
"options": {
"resizeEnable": true,
"center": [113.6401, 34.72468],
"zoom": 12
}
}
} }

View File

@@ -5,10 +5,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from "vue";
import { ElConfigProvider } from "element-plus"; import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn"; import zhCn from "element-plus/lib/locale/lang/zh-cn";
import en from "element-plus/lib/locale/lang/en"; import en from "element-plus/lib/locale/lang/en";
export default { export default defineComponent({
name: "app", name: "app",
components: { components: {
[ElConfigProvider.name]: ElConfigProvider [ElConfigProvider.name]: ElConfigProvider
@@ -18,5 +19,5 @@ export default {
return this.$storage.locale?.locale === "zh" ? zhCn : en; return this.$storage.locale?.locale === "zh" ? zhCn : en;
} }
} }
}; });
</script> </script>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 20 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 17 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 29 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,98 +1,13 @@
import { App, defineComponent } from "vue"; import iconifyIconOffline from "./src/iconifyIconOffline";
import icon from "./src/Icon.vue"; import iconifyIconOnline from "./src/iconifyIconOnline";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import fontIcon from "./src/iconfont";
import { iconComponents } from "/@/plugins/element-plus";
/** export const IconifyIconOffline = iconifyIconOffline;
* find icon component export const IconifyIconOnline = iconifyIconOnline;
* @param icon icon图标 export const FontIcon = fontIcon;
* @returns component
*/
export function findIconReg(icon: string) {
// fontawesome4
const fa4Reg = /^fa-/;
// fontawesome5+
const fa5Reg = /^FA-/;
// iconfont
const iFReg = /^IF-/;
// remixicon
const riReg = /^RI-/;
// typeof icon === "function" 属于SVG
if (fa5Reg.test(icon)) {
const text = icon.split(fa5Reg)[1];
return findIcon(
text.slice(0, text.indexOf(" ") == -1 ? text.length : text.indexOf(" ")),
"FA",
text.slice(text.indexOf(" ") + 1, text.length)
);
} else if (fa4Reg.test(icon)) {
return findIcon(icon.split(fa4Reg)[1], "fa");
} else if (iFReg.test(icon)) {
return findIcon(icon.split(iFReg)[1], "IF");
} else if (typeof icon === "function") {
return findIcon(icon, "SVG");
} else if (riReg.test(icon)) {
return findIcon(icon.split(riReg)[1], "RI");
} else {
return findIcon(icon, "EL");
}
}
// 支持fontawesome、iconfont、remixicon、element-plus/icons、自定义svg
export function findIcon(icon: String, type = "EL", property?: string) {
if (type === "FA") {
return defineComponent({
name: "FaIcon",
setup() {
return { icon, property };
},
components: { FontAwesomeIcon },
template: `<font-awesome-icon :icon="icon" v-bind:[property]="true" />`
});
} else if (type === "fa") {
return defineComponent({
name: "faIcon",
data() {
return { icon: `fa ${icon}` };
},
template: `<i :class="icon" />`
});
} else if (type === "IF") {
return defineComponent({
name: "IfIcon",
data() {
return { icon: `iconfont ${icon}` };
},
template: `<i :class="icon" />`
});
} else if (type === "RI") {
return defineComponent({
name: "RIIcon",
data() {
return { icon: `ri-${icon}` };
},
template: `<i :class="icon" />`
});
} else if (type === "EL") {
const components = iconComponents.filter(
component => component.name === icon
);
if (components.length > 0) {
return components[0];
} else {
return null;
}
} else if (type === "SVG") {
return icon;
}
}
export const Icon = Object.assign(icon, {
install(app: App) {
app.component(icon.name, icon);
}
});
export default { export default {
Icon IconifyIconOffline,
IconifyIconOnline,
FontIcon
}; };

View File

@@ -1,97 +0,0 @@
<script lang="ts">
export default {
name: "Icon"
};
</script>
<script setup lang="ts">
import { ref, computed } from "vue";
const props = defineProps({
content: {
type: String,
default: ""
},
size: {
type: Number,
default: 18
},
width: {
type: Number,
default: 20
},
height: {
type: Number,
default: 20
},
color: {
type: String,
default: ""
},
svg: {
type: Boolean,
default: false
}
});
const emit = defineEmits<{
(e: "click"): void;
}>();
let text = ref("");
let className = computed(() => {
if (props.content.indexOf("fa-") > -1) {
return props.content.indexOf("fa ") === 0
? props.content
: ["fa", props.content];
} else if (props.content.indexOf("el-icon-") > -1) {
return props.content;
} else if (props.content.indexOf("#") > -1) {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
text.value = props.content;
return "iconfont";
} else {
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
text.value = props.content;
return "";
}
});
let iconStyle = computed(() => {
return (
"font-size: " +
props.size +
"px; color: " +
props.color +
"; width: " +
props.width +
"px; height: " +
props.height +
"px; font-style: normal;"
);
});
const clickHandle = () => {
emit("click");
};
</script>
<template>
<i
v-if="!props.svg"
:class="className"
:style="iconStyle"
v-html="text"
@click="clickHandle"
></i>
<svg
class="icon-svg"
v-if="props.svg"
aria-hidden="true"
:style="iconStyle"
@click="clickHandle"
>
<use :xlink:href="`#${props.content}`" />
</svg>
</template>

View File

@@ -0,0 +1,39 @@
import { h, defineComponent, Component } from "vue";
import { IconifyIconOffline, FontIcon } from "../index";
// 支持fontawesome4、5+、iconfont、remixicon、element-plus的icons、自定义svg
export function useRenderIcon(icon: string): Component {
// iconfont
const ifReg = /^IF-/;
// typeof icon === "function" 属于SVG
if (ifReg.test(icon)) {
// iconfont
const name = icon.split(ifReg)[1];
const iconName = name.slice(
0,
name.indexOf(" ") == -1 ? name.length : name.indexOf(" ")
);
const iconType = name.slice(name.indexOf(" ") + 1, name.length);
return defineComponent({
name: "FontIcon",
render() {
return h(FontIcon, {
icon: iconName,
iconType
});
}
});
} else if (typeof icon === "function") {
// svg
return icon;
} else {
return defineComponent({
name: "Icon",
render() {
return h(IconifyIconOffline, {
icon: icon
});
}
});
}
}

View File

@@ -0,0 +1,48 @@
import { h, defineComponent } from "vue";
// 封装iconfont组件默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code
export default defineComponent({
name: "fontIcon",
props: {
icon: {
type: String,
default: ""
}
},
render() {
const attrs = this.$attrs;
if (Object.keys(attrs).includes("uni") || attrs?.iconType === "uni") {
return h(
"i",
{
class: "iconfont",
...attrs
},
this.icon
);
} else if (
Object.keys(attrs).includes("svg") ||
attrs?.iconType === "svg"
) {
return h(
"svg",
{
class: "icon-svg",
"aria-hidden": true
},
{
default: () => [
h("use", {
"xlink:href": `#${this.icon}`
})
]
}
);
} else {
return h("i", {
class: `iconfont ${this.icon}`,
...attrs
});
}
}
});

View File

@@ -0,0 +1,89 @@
import { h, defineComponent } from "vue";
import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline";
// element-plus icon
import Check from "@iconify-icons/ep/check";
import Menu from "@iconify-icons/ep/menu";
import HomeFilled from "@iconify-icons/ep/home-filled";
import SetUp from "@iconify-icons/ep/set-up";
import Edit from "@iconify-icons/ep/edit";
import Setting from "@iconify-icons/ep/setting";
import Lollipop from "@iconify-icons/ep/lollipop";
import Link from "@iconify-icons/ep/link";
import Position from "@iconify-icons/ep/position";
import Histogram from "@iconify-icons/ep/histogram";
import RefreshRight from "@iconify-icons/ep/refresh-right";
import ArrowDown from "@iconify-icons/ep/arrow-down";
import Close from "@iconify-icons/ep/close";
import CloseBold from "@iconify-icons/ep/close-bold";
import Bell from "@iconify-icons/ep/bell";
import Guide from "@iconify-icons/ep/guide";
import User from "@iconify-icons/ep/user";
import Iphone from "@iconify-icons/ep/iphone";
import Location from "@iconify-icons/ep/location";
import Tickets from "@iconify-icons/ep/tickets";
import OfficeBuilding from "@iconify-icons/ep/office-building";
import Notebook from "@iconify-icons/ep/notebook";
addIcon("check", Check);
addIcon("menu", Menu);
addIcon("home-filled", HomeFilled);
addIcon("set-up", SetUp);
addIcon("edit", Edit);
addIcon("setting", Setting);
addIcon("lollipop", Lollipop);
addIcon("link", Link);
addIcon("position", Position);
addIcon("histogram", Histogram);
addIcon("refresh-right", RefreshRight);
addIcon("arrow-down", ArrowDown);
addIcon("close", Close);
addIcon("close-bold", CloseBold);
addIcon("bell", Bell);
addIcon("guide", Guide);
addIcon("user", User);
addIcon("iphone", Iphone);
addIcon("location", Location);
addIcon("tickets", Tickets);
addIcon("office-building", OfficeBuilding);
addIcon("notebook", Notebook);
// remixicon
import arrowRightSLine from "@iconify-icons/ri/arrow-right-s-line";
import arrowLeftSLine from "@iconify-icons/ri/arrow-left-s-line";
import logoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line";
addIcon("arrow-right-s-line", arrowRightSLine);
addIcon("arrow-left-s-line", arrowLeftSLine);
addIcon("logout-circle-r-line", logoutCircleRLine);
// Font Awesome 4
import faUser from "@iconify-icons/fa/user";
import faLock from "@iconify-icons/fa/lock";
import faSignOut from "@iconify-icons/fa/sign-out";
addIcon("fa-user", faUser);
addIcon("fa-lock", faLock);
addIcon("fa-sign-out", faSignOut);
// Iconify Icon在Vue里离线使用用于内网环境https://docs.iconify.design/icon-components/vue/offline.html
export default defineComponent({
name: "IconifyIcon",
components: { IconifyIcon },
props: {
icon: {
type: String,
default: ""
}
},
render() {
const attrs = this.$attrs;
return h(
IconifyIcon,
{
icon: `${this.icon}`,
...attrs
},
{
default: () => []
}
);
}
});

View File

@@ -0,0 +1,32 @@
import { h, defineComponent } from "vue";
import { Icon as IconifyIcon } from "@iconify/vue";
// Iconify Icon在Vue里在线使用用于外网环境 https://docs.iconify.design/icon-components/vue/offline.html
export default defineComponent({
name: "IconifyIcon",
components: { IconifyIcon },
props: {
icon: {
type: String,
default: ""
},
// default element plus icon
type: {
type: String,
default: "ep:"
}
},
render() {
const attrs = this.$attrs;
return h(
IconifyIcon,
{
icon: `${this.type}${this.icon}`,
...attrs
},
{
default: () => []
}
);
}
});

View File

@@ -8,7 +8,7 @@ import {
getCurrentInstance getCurrentInstance
} from "vue"; } from "vue";
import { RouterView } from "vue-router"; import { RouterView } from "vue-router";
import backTop from "/@/assets/svg/back_top.svg"; import backTop from "/@/assets/svg/back_top.svg?component";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
const props = defineProps({ const props = defineProps({

View File

@@ -13,7 +13,8 @@ import { useAppStoreHook } from "/@/store/modules/app";
import { unref, watch, getCurrentInstance } from "vue"; import { unref, watch, getCurrentInstance } from "vue";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
import screenfull from "../components/screenfull/index.vue"; import screenfull from "../components/screenfull/index.vue";
import globalization from "/@/assets/svg/globalization.svg"; import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
import globalization from "/@/assets/svg/globalization.svg?component";
const instance = const instance =
getCurrentInstance().appContext.config.globalProperties.$storage; getCurrentInstance().appContext.config.globalProperties.$storage;
@@ -26,7 +27,7 @@ const { locale } = useI18n();
const getDropdownItemStyle = computed(() => { const getDropdownItemStyle = computed(() => {
return t => { return t => {
return { return {
background: locale.value === t ? "#1b2a47" : "", background: locale.value === t ? useEpThemeStoreHook().epThemeColor : "",
color: locale.value === t ? "#f4f4f5" : "#000" color: locale.value === t ? "#f4f4f5" : "#000"
}; };
}; };
@@ -94,15 +95,17 @@ function translationEn() {
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle('zh')" :style="getDropdownItemStyle('zh')"
@click="translationCh" @click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'" ><IconifyIconOffline
><check /></el-icon class="check-zh"
>简体中文</el-dropdown-item v-show="locale === 'zh'"
icon="check"
/>简体中文</el-dropdown-item
> >
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle('en')" :style="getDropdownItemStyle('en')"
@click="translationEn" @click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'" ><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon ><IconifyIconOffline icon="check" /></el-icon
>English</el-dropdown-item >English</el-dropdown-item
> >
</el-dropdown-menu> </el-dropdown-menu>
@@ -117,8 +120,10 @@ function translationEn() {
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">
<el-dropdown-item @click="logout"> <el-dropdown-item @click="logout">
<i class="ri-logout-circle-r-line"></i <IconifyIconOffline
>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item icon="logout-circle-r-line"
style="margin: 5px"
/>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
> >
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@@ -128,7 +133,7 @@ function translationEn() {
:title="$t('buttons.hssystemSet')" :title="$t('buttons.hssystemSet')"
@click="onPanel" @click="onPanel"
> >
<Setting /> <IconifyIconOffline icon="setting" />
</el-icon> </el-icon>
</div> </div>
</div> </div>
@@ -149,10 +154,6 @@ function translationEn() {
cursor: pointer; cursor: pointer;
transition: background 0.3s; transition: background 0.3s;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
&:hover {
background: rgba(0, 0, 0, 0.025);
}
} }
.vertical-header-right { .vertical-header-right {

View File

@@ -108,7 +108,7 @@ export const noticesData: TabItem[] = [
{ {
avatar: "", avatar: "",
title: "任务名称", title: "任务名称",
description: "任务需要在 2021-11-16 20:00 前启动", description: "任务需要在 2022-11-16 20:00 前启动",
datetime: "", datetime: "",
extra: "未开始", extra: "未开始",
status: "info", status: "info",
@@ -118,7 +118,7 @@ export const noticesData: TabItem[] = [
avatar: "", avatar: "",
title: "第三方紧急代码变更", title: "第三方紧急代码变更",
description: description:
"一拳提交于 2021-11-16需在 2021-11-18 前完成代码变更任务", "一拳提交于 2022-11-16需在 2022-11-18 前完成代码变更任务",
datetime: "", datetime: "",
extra: "马上到期", extra: "马上到期",
status: "danger", status: "danger",
@@ -127,7 +127,7 @@ export const noticesData: TabItem[] = [
{ {
avatar: "", avatar: "",
title: "信息安全考试", title: "信息安全考试",
description: "指派小仙于 2021-12-12 前完成更新并发布", description: "指派小仙于 2022-12-12 前完成更新并发布",
datetime: "", datetime: "",
extra: "已耗时 8 天", extra: "已耗时 8 天",
status: "warning", status: "warning",

View File

@@ -16,7 +16,9 @@ notices.value.forEach(notice => {
<el-dropdown trigger="click" placement="bottom-end"> <el-dropdown trigger="click" placement="bottom-end">
<span class="dropdown-badge"> <span class="dropdown-badge">
<el-badge :value="noticesNum" :max="99"> <el-badge :value="noticesNum" :max="99">
<el-icon class="header-notice-icon"><bell /></el-icon> <el-icon class="header-notice-icon"
><IconifyIconOffline icon="bell"
/></el-icon>
</el-badge> </el-badge>
</span> </span>
<template #dropdown> <template #dropdown>

View File

@@ -23,7 +23,7 @@ emitter.on("openPanel", () => {
<div class="project-configuration"> <div class="project-configuration">
<h3>项目配置</h3> <h3>项目配置</h3>
<el-icon title="关闭配置" class="el-icon-close" @click="show = !show"> <el-icon title="关闭配置" class="el-icon-close" @click="show = !show">
<Close /> <IconifyIconOffline icon="close" />
</el-icon> </el-icon>
</div> </div>
<div style="border-bottom: 1px solid #dcdfe6"></div> <div style="border-bottom: 1px solid #dcdfe6"></div>

View File

@@ -5,18 +5,14 @@ const { isFullscreen, toggle } = useFullscreen();
<template> <template>
<div class="screen-full" @click="toggle"> <div class="screen-full" @click="toggle">
<i <FontIcon
:title=" :title="
isFullscreen isFullscreen
? $t('buttons.hsexitfullscreen') ? $t('buttons.hsexitfullscreen')
: $t('buttons.hsfullscreen') : $t('buttons.hsfullscreen')
" "
:class=" :icon="isFullscreen ? 'team-iconexit-fullscreen' : 'team-iconfullscreen'"
isFullscreen />
? 'iconfont team-iconexit-fullscreen'
: 'iconfont team-iconfullscreen'
"
></i>
</div> </div>
</template> </template>

View File

@@ -16,9 +16,7 @@ import { useRouter } from "vue-router";
import panel from "../panel/index.vue"; import panel from "../panel/index.vue";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import { templateRef } from "@vueuse/core"; import { templateRef } from "@vueuse/core";
import dayIcon from "/@/assets/svg/day.svg";
import { debounce } from "/@/utils/debounce"; import { debounce } from "/@/utils/debounce";
import darkIcon from "/@/assets/svg/dark.svg";
import { themeColorsType } from "../../types"; import { themeColorsType } from "../../types";
import { useAppStoreHook } from "/@/store/modules/app"; import { useAppStoreHook } from "/@/store/modules/app";
import { shadeBgColor } from "../../theme/element-plus"; import { shadeBgColor } from "../../theme/element-plus";
@@ -28,6 +26,9 @@ import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
import { createNewStyle, writeNewStyle } from "../../theme/element-plus"; import { createNewStyle, writeNewStyle } from "../../theme/element-plus";
import { toggleTheme } from "@zougt/vite-plugin-theme-preprocessor/dist/browser-utils"; import { toggleTheme } from "@zougt/vite-plugin-theme-preprocessor/dist/browser-utils";
import dayIcon from "/@/assets/svg/day.svg?component";
import darkIcon from "/@/assets/svg/dark.svg?component";
const router = useRouter(); const router = useRouter();
const { isSelect } = useCssModule(); const { isSelect } = useCssModule();
const body = document.documentElement as HTMLElement; const body = document.documentElement as HTMLElement;
@@ -155,9 +156,8 @@ function onReset() {
parentPath: "/", parentPath: "/",
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
icon: "HomeFilled", icon: "home-filled",
i18n: true, i18n: true
showLink: true
} }
} }
]); ]);
@@ -351,7 +351,7 @@ nextTick(() => {
:size="17" :size="17"
:color="getThemeColor(item.themeColor)" :color="getThemeColor(item.themeColor)"
> >
<Check /> <IconifyIconOffline icon="check" />
</el-icon> </el-icon>
</li> </li>
</ul> </ul>
@@ -436,7 +436,12 @@ nextTick(() => {
style="width: 90%; margin: 24px 15px" style="width: 90%; margin: 24px 15px"
@click="onReset" @click="onReset"
> >
<i class="fa fa-sign-out"></i> <IconifyIconOffline
icon="fa-sign-out"
width="15"
height="15"
style="margin-right: 4px"
/>
清空缓存并返回登录页</el-button 清空缓存并返回登录页</el-button
> >
</panel> </panel>
@@ -444,7 +449,7 @@ nextTick(() => {
<style scoped module> <style scoped module>
.isSelect { .isSelect {
border: 2px solid #0960bd; border: 2px solid var(--el-color-primary);
} }
</style> </style>

View File

@@ -1,4 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from "vue";
import { useEpThemeStoreHook } from "/@/store/modules/epTheme";
export interface Props { export interface Props {
isActive: boolean; isActive: boolean;
} }
@@ -7,6 +9,8 @@ const props = withDefaults(defineProps<Props>(), {
isActive: false isActive: false
}); });
const fillColor = ref<string>("");
const emit = defineEmits<{ const emit = defineEmits<{
(e: "toggleClick"): void; (e: "toggleClick"): void;
}>(); }>();
@@ -21,8 +25,11 @@ const toggleClick = () => {
:class="classes.container" :class="classes.container"
:title="props.isActive ? '点击折叠' : '点击展开'" :title="props.isActive ? '点击折叠' : '点击展开'"
@click="toggleClick" @click="toggleClick"
@mouseenter="fillColor = useEpThemeStoreHook().epThemeColor"
@mouseleave="fillColor = ''"
> >
<svg <svg
:fill="fillColor"
:class="['hamburger', props.isActive ? 'is-active' : '']" :class="['hamburger', props.isActive ? 'is-active' : '']"
viewBox="0 0 1024 1024" viewBox="0 0 1024 1024"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -13,14 +13,12 @@ import Notice from "../notice/index.vue";
import { templateRef } from "@vueuse/core"; import { templateRef } from "@vueuse/core";
import SidebarItem from "./sidebarItem.vue"; import SidebarItem from "./sidebarItem.vue";
import avatars from "/@/assets/avatars.jpg"; import avatars from "/@/assets/avatars.jpg";
import { algorithm } from "/@/utils/algorithm";
import screenfull from "../screenfull/index.vue"; import screenfull from "../screenfull/index.vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { storageSession } from "/@/utils/storage"; import { storageSession } from "/@/utils/storage";
import Icon from "/@/components/ReIcon/src/Icon.vue";
import { deviceDetection } from "/@/utils/deviceDetection"; import { deviceDetection } from "/@/utils/deviceDetection";
import globalization from "/@/assets/svg/globalization.svg";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
import globalization from "/@/assets/svg/globalization.svg?component";
const instance = const instance =
getCurrentInstance().appContext.config.globalProperties.$storage; getCurrentInstance().appContext.config.globalProperties.$storage;
@@ -92,7 +90,7 @@ const menuSelect = (indexPath: string): void => {
} }
}); });
} }
findCurrentRoute(algorithm.increaseIndexes(routers)); findCurrentRoute(routers);
}; };
function backHome() { function backHome() {
@@ -128,7 +126,11 @@ onMounted(() => {
<template> <template>
<div class="horizontal-header"> <div class="horizontal-header">
<div class="horizontal-header-left" @click="backHome"> <div class="horizontal-header-left" @click="backHome">
<Icon svg :width="35" :height="35" content="team-iconlogo" /> <FontIcon
icon="team-iconlogo"
svg
style="width: 35px; height: 35px"
></FontIcon>
<h4>{{ title }}</h4> <h4>{{ title }}</h4>
</div> </div>
<el-menu <el-menu
@@ -161,14 +163,14 @@ onMounted(() => {
:style="getDropdownItemStyle('zh')" :style="getDropdownItemStyle('zh')"
@click="translationCh" @click="translationCh"
><el-icon class="check-zh" v-show="locale === 'zh'" ><el-icon class="check-zh" v-show="locale === 'zh'"
><check /></el-icon ><IconifyIconOffline icon="check" /></el-icon
>简体中文</el-dropdown-item >简体中文</el-dropdown-item
> >
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle('en')" :style="getDropdownItemStyle('en')"
@click="translationEn" @click="translationEn"
><el-icon class="check-en" v-show="locale === 'en'" ><el-icon class="check-en" v-show="locale === 'en'"
><check /></el-icon ><IconifyIconOffline icon="check" /></el-icon
>English</el-dropdown-item >English</el-dropdown-item
> >
</el-dropdown-menu> </el-dropdown-menu>
@@ -183,8 +185,11 @@ onMounted(() => {
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="logout"> <el-dropdown-menu class="logout">
<el-dropdown-item @click="logout"> <el-dropdown-item @click="logout">
<i class="ri-logout-circle-r-line"></i <IconifyIconOffline
>{{ $t("buttons.hsLoginOut") }}</el-dropdown-item icon="logout-circle-r-line"
style="margin: 5px"
/>
{{ $t("buttons.hsLoginOut") }}</el-dropdown-item
> >
</el-dropdown-menu> </el-dropdown-menu>
</template> </template>
@@ -194,7 +199,7 @@ onMounted(() => {
:title="$t('buttons.hssystemSet')" :title="$t('buttons.hssystemSet')"
@click="onPanel" @click="onPanel"
> >
<Setting /> <IconifyIconOffline icon="setting" />
</el-icon> </el-icon>
</div> </div>
</div> </div>

View File

@@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { getCurrentInstance } from "vue"; import { getCurrentInstance } from "vue";
import Icon from "/@/components/ReIcon/src/Icon.vue";
const props = defineProps({ const props = defineProps({
collapse: Boolean collapse: Boolean
}); });
@@ -19,7 +18,11 @@ const title =
class="sidebar-logo-link" class="sidebar-logo-link"
to="/" to="/"
> >
<Icon svg :width="35" :height="35" content="team-iconlogo" /> <FontIcon
icon="team-iconlogo"
svg
style="width: 35px; height: 35px"
></FontIcon>
<span class="sidebar-title">{{ title }}</span> <span class="sidebar-title">{{ title }}</span>
</router-link> </router-link>
<router-link <router-link
@@ -29,7 +32,11 @@ const title =
class="sidebar-logo-link" class="sidebar-logo-link"
to="/" to="/"
> >
<Icon svg :width="35" :height="35" content="team-iconlogo" /> <FontIcon
icon="team-iconlogo"
svg
style="width: 35px; height: 35px"
></FontIcon>
<span class="sidebar-title">{{ title }}</span> <span class="sidebar-title">{{ title }}</span>
</router-link> </router-link>
</transition> </transition>

View File

@@ -10,9 +10,8 @@ import {
import path from "path"; import path from "path";
import { childrenType } from "../../types"; import { childrenType } from "../../types";
import { transformI18n } from "/@/plugins/i18n"; import { transformI18n } from "/@/plugins/i18n";
import { findIconReg } from "/@/components/ReIcon";
import Icon from "/@/components/ReIcon/src/Icon.vue";
import { useAppStoreHook } from "/@/store/modules/app"; import { useAppStoreHook } from "/@/store/modules/app";
import { useRenderIcon } from "/@/components/ReIcon/src/hooks";
const instance = getCurrentInstance().appContext.app.config.globalProperties; const instance = getCurrentInstance().appContext.app.config.globalProperties;
const menuMode = instance.$storage.layout?.layout === "vertical"; const menuMode = instance.$storage.layout?.layout === "vertical";
@@ -143,7 +142,7 @@ function resolvePath(routePath) {
<el-icon v-show="props.item.meta.icon"> <el-icon v-show="props.item.meta.icon">
<component <component
:is=" :is="
findIconReg( useRenderIcon(
onlyOneChild.meta.icon || onlyOneChild.meta.icon ||
(props.item.meta && props.item.meta.icon) (props.item.meta && props.item.meta.icon)
) )
@@ -176,11 +175,11 @@ function resolvePath(routePath) {
}} }}
</span> </span>
</el-tooltip> </el-tooltip>
<Icon <FontIcon
v-if="onlyOneChild.meta.extraIcon" v-if="onlyOneChild.meta.extraIcon"
:icon="onlyOneChild.meta.extraIcon.name"
:svg="onlyOneChild.meta.extraIcon.svg ? true : false" :svg="onlyOneChild.meta.extraIcon.svg ? true : false"
:content="`${onlyOneChild.meta.extraIcon.name}`" ></FontIcon>
/>
</div> </div>
</template> </template>
</el-menu-item> </el-menu-item>
@@ -195,7 +194,7 @@ function resolvePath(routePath) {
<template #title> <template #title>
<el-icon v-show="props.item.meta.icon" :class="props.item.meta.icon"> <el-icon v-show="props.item.meta.icon" :class="props.item.meta.icon">
<component <component
:is="findIconReg(props.item.meta && props.item.meta.icon)" :is="useRenderIcon(props.item.meta && props.item.meta.icon)"
></component> ></component>
</el-icon> </el-icon>
<span v-if="!menuMode">{{ <span v-if="!menuMode">{{
@@ -220,11 +219,11 @@ function resolvePath(routePath) {
</span> </span>
</div> </div>
</el-tooltip> </el-tooltip>
<Icon <FontIcon
v-if="props.item.meta.extraIcon" v-if="props.item.meta.extraIcon"
:icon="props.item.meta.extraIcon.name"
:svg="props.item.meta.extraIcon.svg ? true : false" :svg="props.item.meta.extraIcon.svg ? true : false"
:content="`${props.item.meta.extraIcon.name}`" ></FontIcon>
/>
</template> </template>
<sidebar-item <sidebar-item
v-for="child in props.item.children" v-for="child in props.item.children"

View File

@@ -2,7 +2,6 @@
import Logo from "./logo.vue"; import Logo from "./logo.vue";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import SidebarItem from "./sidebarItem.vue"; import SidebarItem from "./sidebarItem.vue";
import { algorithm } from "/@/utils/algorithm";
import { storageLocal } from "/@/utils/storage"; import { storageLocal } from "/@/utils/storage";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { computed, ref, onBeforeMount } from "vue"; import { computed, ref, onBeforeMount } from "vue";
@@ -48,7 +47,7 @@ const menuSelect = (indexPath: string): void => {
} }
}); });
} }
findCurrentRoute(algorithm.increaseIndexes(router)); findCurrentRoute(router);
}; };
onBeforeMount(() => { onBeforeMount(() => {

View File

@@ -253,13 +253,23 @@
} }
} }
.ri-arrow-left-s-line { .arrow-left,
.arrow-right {
width: 40px; width: 40px;
height: 38px; height: 38px;
line-height: 38px;
text-align: center;
font-size: 20px;
color: #00000073; color: #00000073;
position: relative;
svg {
width: 20px;
height: 20px;
position: absolute;
left: 50%;
transform: translate(-50%, 50%);
}
}
.arrow-left {
box-shadow: 5px 0 5px -6px #ccc; box-shadow: 5px 0 5px -6px #ccc;
&:hover { &:hover {
@@ -267,15 +277,9 @@
} }
} }
.ri-arrow-right-s-line { .arrow-right {
width: 40px;
height: 38px;
line-height: 38px;
text-align: center;
font-size: 20px;
border-right: 1px solid #ccc;
color: #00000073;
box-shadow: -5px 0 5px -6px #ccc; box-shadow: -5px 0 5px -6px #ccc;
border-right: 1px solid #ccc;
&:hover { &:hover {
cursor: e-resize; cursor: e-resize;

View File

@@ -3,6 +3,7 @@ import {
ref, ref,
watch, watch,
unref, unref,
reactive,
nextTick, nextTick,
computed, computed,
ComputedRef, ComputedRef,
@@ -11,15 +12,15 @@ import {
getCurrentInstance getCurrentInstance
} from "vue"; } from "vue";
import close from "/@/assets/svg/close.svg"; import close from "/@/assets/svg/close.svg?component";
import refresh from "/@/assets/svg/refresh.svg"; import refresh from "/@/assets/svg/refresh.svg?component";
import closeAll from "/@/assets/svg/close_all.svg"; import closeAll from "/@/assets/svg/close_all.svg?component";
import closeLeft from "/@/assets/svg/close_left.svg"; import closeLeft from "/@/assets/svg/close_left.svg?component";
import closeOther from "/@/assets/svg/close_other.svg"; import closeOther from "/@/assets/svg/close_other.svg?component";
import closeRight from "/@/assets/svg/close_right.svg"; import closeRight from "/@/assets/svg/close_right.svg?component";
import { $t as t } from "/@/plugins/i18n";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import { $t as t } from "/@/plugins/i18n";
import { isEqual, isEmpty } from "lodash-es"; import { isEqual, isEmpty } from "lodash-es";
import { transformI18n } from "/@/plugins/i18n"; import { transformI18n } from "/@/plugins/i18n";
import { storageLocal } from "/@/utils/storage"; import { storageLocal } from "/@/utils/storage";
@@ -30,7 +31,6 @@ import { handleAliveRoute, delAliveRoutes } from "/@/router/utils";
import { useMultiTagsStoreHook } from "/@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "/@/store/modules/multiTags";
import { usePermissionStoreHook } from "/@/store/modules/permission"; import { usePermissionStoreHook } from "/@/store/modules/permission";
import { toggleClass, removeClass, hasClass } from "/@/utils/operate"; import { toggleClass, removeClass, hasClass } from "/@/utils/operate";
import { templateRef, useResizeObserver, useDebounceFn } from "@vueuse/core"; import { templateRef, useResizeObserver, useDebounceFn } from "@vueuse/core";
const route = useRoute(); const route = useRoute();
@@ -40,11 +40,11 @@ const activeIndex = ref<number>(-1);
let refreshButton = "refresh-button"; let refreshButton = "refresh-button";
const instance = getCurrentInstance(); const instance = getCurrentInstance();
const pureSetting = useSettingStoreHook(); const pureSetting = useSettingStoreHook();
const showTags = ref(storageLocal.getItem("tagsVal") || false);
const tabDom = templateRef<HTMLElement | null>("tabDom", null); const tabDom = templateRef<HTMLElement | null>("tabDom", null);
const containerDom = templateRef<HTMLElement | null>("containerDom", null); const containerDom = templateRef<HTMLElement | null>("containerDom", null);
const scrollbarDom = templateRef<HTMLElement | null>("scrollbarDom", null); const scrollbarDom = templateRef<HTMLElement | null>("scrollbarDom", null);
const showTags =
ref(storageLocal.getItem("responsive-configure").hideTabs) ?? "false";
let multiTags: ComputedRef<Array<RouteConfigs>> = computed(() => { let multiTags: ComputedRef<Array<RouteConfigs>> = computed(() => {
return useMultiTagsStoreHook()?.multiTags; return useMultiTagsStoreHook()?.multiTags;
}); });
@@ -128,15 +128,15 @@ const moveToView = (index: number): void => {
if (!instance.refs["dynamic" + index]) { if (!instance.refs["dynamic" + index]) {
return; return;
} }
const tabItemEl = instance.refs["dynamic" + index]; const tabItemEl = instance.refs["dynamic" + index][0];
const tabItemElOffsetLeft = (tabItemEl as HTMLElement).offsetLeft; const tabItemElOffsetLeft = (tabItemEl as HTMLElement)?.offsetLeft;
const tabItemOffsetWidth = (tabItemEl as HTMLElement).offsetWidth; const tabItemOffsetWidth = (tabItemEl as HTMLElement)?.offsetWidth;
// 标签页导航栏可视长度(不包含溢出部分) // 标签页导航栏可视长度(不包含溢出部分)
const scrollbarDomWidth = scrollbarDom.value const scrollbarDomWidth = scrollbarDom.value
? scrollbarDom.value.offsetWidth ? scrollbarDom.value?.offsetWidth
: 0; : 0;
// 已有标签页总长度(包含溢出部分) // 已有标签页总长度(包含溢出部分)
const tabDomWidth = tabDom.value ? tabDom.value.offsetWidth : 0; const tabDomWidth = tabDom.value ? tabDom.value?.offsetWidth : 0;
if (tabDomWidth < scrollbarDomWidth || tabItemElOffsetLeft === 0) { if (tabDomWidth < scrollbarDomWidth || tabItemElOffsetLeft === 0) {
translateX.value = 0; translateX.value = 0;
@@ -186,7 +186,7 @@ const handleScroll = (offset: number): void => {
} }
}; };
const tagsViews = ref<Array<tagsViewsType>>([ const tagsViews = reactive<Array<tagsViewsType>>([
{ {
icon: refresh, icon: refresh,
text: t("buttons.hsreload"), text: t("buttons.hsreload"),
@@ -314,8 +314,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
i18n: true, i18n: true,
icon: "el-icon-s-home", icon: "home-filled"
showLink: true
} }
}, },
obj obj
@@ -435,13 +434,13 @@ function closeMenu() {
function showMenus(value: boolean) { function showMenus(value: boolean) {
Array.of(1, 2, 3, 4, 5).forEach(v => { Array.of(1, 2, 3, 4, 5).forEach(v => {
tagsViews.value[v].show = value; tagsViews[v].show = value;
}); });
} }
function disabledMenus(value: boolean) { function disabledMenus(value: boolean) {
Array.of(1, 2, 3, 4, 5).forEach(v => { Array.of(1, 2, 3, 4, 5).forEach(v => {
tagsViews.value[v].disabled = value; tagsViews[v].disabled = value;
}); });
} }
@@ -463,35 +462,34 @@ function showMenuModel(
showMenus(true); showMenus(true);
if (refresh) { if (refresh) {
tagsViews.value[0].show = true; tagsViews[0].show = true;
} }
/** /**
* currentIndex为1时左侧的菜单是首页则不显示关闭左侧标签页 * currentIndex为1时左侧的菜单是首页则不显示关闭左侧标签页
* 如果currentIndex等于routeLength-1右侧没有菜单则不显示关闭右侧标签页 * 如果currentIndex等于routeLength-1右侧没有菜单则不显示关闭右侧标签页
*/ */
if (currentIndex === 1 && routeLength !== 2) { if (currentIndex === 1 && routeLength !== 2) {
// 左侧的菜单是首页,右侧存在别的菜单 // 左侧的菜单是首页,右侧存在别的菜单
tagsViews.value[2].show = false; tagsViews[2].show = false;
Array.of(1, 3, 4, 5).forEach(v => { Array.of(1, 3, 4, 5).forEach(v => {
tagsViews.value[v].disabled = false; tagsViews[v].disabled = false;
}); });
tagsViews.value[2].disabled = true; tagsViews[2].disabled = true;
} else if (currentIndex === 1 && routeLength === 2) { } else if (currentIndex === 1 && routeLength === 2) {
disabledMenus(false); disabledMenus(false);
// 左侧的菜单是首页,右侧不存在别的菜单 // 左侧的菜单是首页,右侧不存在别的菜单
Array.of(2, 3, 4).forEach(v => { Array.of(2, 3, 4).forEach(v => {
tagsViews.value[v].show = false; tagsViews[v].show = false;
tagsViews.value[v].disabled = true; tagsViews[v].disabled = true;
}); });
} else if (routeLength - 1 === currentIndex && currentIndex !== 0) { } else if (routeLength - 1 === currentIndex && currentIndex !== 0) {
// 当前路由是所有路由中的最后一个 // 当前路由是所有路由中的最后一个
tagsViews.value[3].show = false; tagsViews[3].show = false;
Array.of(1, 2, 4, 5).forEach(v => { Array.of(1, 2, 4, 5).forEach(v => {
tagsViews.value[v].disabled = false; tagsViews[v].disabled = false;
}); });
tagsViews.value[3].disabled = true; tagsViews[3].disabled = true;
} else if (currentIndex === 0 || currentPath === "/redirect/welcome") { } else if (currentIndex === 0 || currentPath === "/redirect/welcome") {
// 当前路由为首页 // 当前路由为首页
disabledMenus(true); disabledMenus(true);
@@ -505,10 +503,10 @@ function openMenu(tag, e) {
if (tag.path === "/welcome") { if (tag.path === "/welcome") {
// 右键菜单为首页,只显示刷新 // 右键菜单为首页,只显示刷新
showMenus(false); showMenus(false);
tagsViews.value[0].show = true; tagsViews[0].show = true;
} else if (route.path !== tag.path) { } else if (route.path !== tag.path) {
// 右键菜单不匹配当前路由,隐藏刷新 // 右键菜单不匹配当前路由,隐藏刷新
tagsViews.value[0].show = false; tagsViews[0].show = false;
showMenuModel(tag.path, tag.query); showMenuModel(tag.path, tag.query);
} else if ( } else if (
// eslint-disable-next-line no-dupe-else-if // eslint-disable-next-line no-dupe-else-if
@@ -517,7 +515,7 @@ function openMenu(tag, e) {
) { ) {
showMenus(true); showMenus(true);
// 只有两个标签时不显示关闭其他标签页 // 只有两个标签时不显示关闭其他标签页
tagsViews.value[4].show = false; tagsViews[4].show = false;
} else if (route.path === tag.path) { } else if (route.path === tag.path) {
// 右键当前激活的菜单 // 右键当前激活的菜单
showMenuModel(tag.path, tag.query, true); showMenuModel(tag.path, tag.query, true);
@@ -552,30 +550,32 @@ function tagOnClick(item) {
} }
// 鼠标移入 // 鼠标移入
function onMouseenter(item, index) { function onMouseenter(index) {
if (index) activeIndex.value = index; if (index) activeIndex.value = index;
if (unref(showModel) === "smart") { if (unref(showModel) === "smart") {
if (hasClass(instance.refs["schedule" + index], "schedule-active")) return; if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
toggleClass(true, "schedule-in", instance.refs["schedule" + index]); return;
toggleClass(false, "schedule-out", instance.refs["schedule" + index]); toggleClass(true, "schedule-in", instance.refs["schedule" + index][0]);
toggleClass(false, "schedule-out", instance.refs["schedule" + index][0]);
} else { } else {
if (hasClass(instance.refs["dynamic" + index], "card-active")) return; if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
toggleClass(true, "card-in", instance.refs["dynamic" + index]); toggleClass(true, "card-in", instance.refs["dynamic" + index][0]);
toggleClass(false, "card-out", instance.refs["dynamic" + index]); toggleClass(false, "card-out", instance.refs["dynamic" + index][0]);
} }
} }
// 鼠标移出 // 鼠标移出
function onMouseleave(item, index) { function onMouseleave(index) {
activeIndex.value = -1; activeIndex.value = -1;
if (unref(showModel) === "smart") { if (unref(showModel) === "smart") {
if (hasClass(instance.refs["schedule" + index], "schedule-active")) return; if (hasClass(instance.refs["schedule" + index][0], "schedule-active"))
toggleClass(false, "schedule-in", instance.refs["schedule" + index]); return;
toggleClass(true, "schedule-out", instance.refs["schedule" + index]); toggleClass(false, "schedule-in", instance.refs["schedule" + index][0]);
toggleClass(true, "schedule-out", instance.refs["schedule" + index][0]);
} else { } else {
if (hasClass(instance.refs["dynamic" + index], "card-active")) return; if (hasClass(instance.refs["dynamic" + index][0], "card-active")) return;
toggleClass(false, "card-in", instance.refs["dynamic" + index]); toggleClass(false, "card-in", instance.refs["dynamic" + index][0]);
toggleClass(true, "card-out", instance.refs["dynamic" + index]); toggleClass(true, "card-out", instance.refs["dynamic" + index][0]);
} }
} }
@@ -629,7 +629,9 @@ const getContextMenuStyle = computed((): CSSProperties => {
<template> <template>
<div ref="containerDom" class="tags-view" v-if="!showTags"> <div ref="containerDom" class="tags-view" v-if="!showTags">
<i class="ri-arrow-left-s-line" @click="handleScroll(200)"></i> <div class="arrow-left">
<IconifyIconOffline icon="arrow-left-s-line" @click="handleScroll(200)" />
</div>
<div ref="scrollbarDom" class="scroll-container"> <div ref="scrollbarDom" class="scroll-container">
<div class="tab" ref="tabDom" :style="getTabStyle"> <div class="tab" ref="tabDom" :style="getTabStyle">
<div <div
@@ -644,8 +646,8 @@ const getContextMenuStyle = computed((): CSSProperties => {
: '' : ''
]" ]"
@contextmenu.prevent="openMenu(item, $event)" @contextmenu.prevent="openMenu(item, $event)"
@mouseenter.prevent="onMouseenter(item, index)" @mouseenter.prevent="onMouseenter(index)"
@mouseleave.prevent="onMouseleave(item, index)" @mouseleave.prevent="onMouseleave(index)"
@click="tagOnClick(item)" @click="tagOnClick(item)"
> >
<router-link :to="item.path" <router-link :to="item.path"
@@ -659,7 +661,7 @@ const getContextMenuStyle = computed((): CSSProperties => {
class="el-icon-close" class="el-icon-close"
@click.stop="deleteMenu(item)" @click.stop="deleteMenu(item)"
> >
<CloseBold /> <IconifyIconOffline icon="close-bold" />
</el-icon> </el-icon>
<div <div
:ref="'schedule' + index" :ref="'schedule' + index"
@@ -669,7 +671,12 @@ const getContextMenuStyle = computed((): CSSProperties => {
</div> </div>
</div> </div>
</div> </div>
<i class="ri-arrow-right-s-line" @click="handleScroll(-200)"></i> <span class="arrow-right">
<IconifyIconOffline
icon="arrow-right-s-line"
@click="handleScroll(-200)"
/>
</span>
<!-- 右键菜单按钮 --> <!-- 右键菜单按钮 -->
<transition name="el-zoom-in-top"> <transition name="el-zoom-in-top">
<ul <ul
@@ -698,13 +705,13 @@ const getContextMenuStyle = computed((): CSSProperties => {
class="el-icon-refresh-right rotate" class="el-icon-refresh-right rotate"
@click="onFresh" @click="onFresh"
> >
<RefreshRight /> <IconifyIconOffline icon="refresh-right" />
</el-icon> </el-icon>
</li> </li>
<li> <li>
<el-dropdown trigger="click" placement="bottom-end"> <el-dropdown trigger="click" placement="bottom-end">
<el-icon> <el-icon>
<ArrowDown /> <IconifyIconOffline icon="arrow-down" />
</el-icon> </el-icon>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>

View File

@@ -11,14 +11,15 @@ import { setType } from "./types";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { routerArrays } from "./types"; import { routerArrays } from "./types";
import { emitter } from "/@/utils/mitt"; import { emitter } from "/@/utils/mitt";
import backTop from "/@/assets/svg/back_top.svg";
import { useAppStoreHook } from "/@/store/modules/app"; 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 { deviceDetection } from "/@/utils/deviceDetection";
import { useMultiTagsStore } from "/@/store/modules/multiTags"; import { useMultiTagsStore } from "/@/store/modules/multiTags";
import { useSettingStoreHook } from "/@/store/modules/settings"; import { useSettingStoreHook } from "/@/store/modules/settings";
import backTop from "/@/assets/svg/back_top.svg?component";
import fullScreen from "/@/assets/svg/full_screen.svg?component";
import exitScreen from "/@/assets/svg/exit_screen.svg?component";
import navbar from "./components/navbar.vue"; import navbar from "./components/navbar.vue";
import tag from "./components/tag/index.vue"; import tag from "./components/tag/index.vue";
import appMain from "./components/appMain.vue"; import appMain from "./components/appMain.vue";

View File

@@ -1,6 +1,6 @@
/* 动态改变element-plus主题色 */ /* 动态改变element-plus主题色 */
import rgbHex from "rgb-hex"; import rgbHex from "rgb-hex";
import color from "css-color-function"; import { convert } from "css-color-function";
import { TinyColor } from "@ctrl/tinycolor"; import { TinyColor } from "@ctrl/tinycolor";
import epCss from "element-plus/dist/index.css"; import epCss from "element-plus/dist/index.css";
@@ -48,7 +48,7 @@ export const createColors = (primary: string) => {
}; };
Object.keys(formula).forEach(key => { Object.keys(formula).forEach(key => {
const value = formula[key].replace(/primary/, primary); const value = formula[key].replace(/primary/, primary);
colors[key] = "#" + rgbHex(color.convert(value)); colors[key] = "#" + rgbHex(convert(value));
}); });
return colors; return colors;
}; };

View File

@@ -1,3 +1,4 @@
import { Component } from "vue";
export const routerArrays: Array<RouteConfigs> = [ export const routerArrays: Array<RouteConfigs> = [
{ {
path: "/welcome", path: "/welcome",
@@ -5,8 +6,7 @@ export const routerArrays: Array<RouteConfigs> = [
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
i18n: true, i18n: true,
icon: "HomeFilled", icon: "home-filled"
showLink: true
} }
} }
]; ];
@@ -32,7 +32,7 @@ export type multiTagsType = {
}; };
export type tagsViewsType = { export type tagsViewsType = {
icon: string; icon: Component;
text: string; text: string;
divided: boolean; divided: boolean;
disabled: boolean; disabled: boolean;

View File

@@ -5,11 +5,11 @@ import { getServerConfig } from "./config";
import { createApp, Directive } from "vue"; import { createApp, Directive } from "vue";
import { usI18n } from "../src/plugins/i18n"; import { usI18n } from "../src/plugins/i18n";
import { MotionPlugin } from "@vueuse/motion"; import { MotionPlugin } from "@vueuse/motion";
import { useFontawesome } from "../src/plugins/fontawesome";
import { useElementPlus } from "../src/plugins/element-plus"; import { useElementPlus } from "../src/plugins/element-plus";
import { injectResponsiveStorage } from "/@/utils/storage/responsive"; import { injectResponsiveStorage } from "/@/utils/storage/responsive";
import "animate.css"; import "animate.css";
import "virtual:windi.css";
// 导入公共样式 // 导入公共样式
import "./style/index.scss"; import "./style/index.scss";
// 导入字体图标 // 导入字体图标
@@ -24,15 +24,20 @@ Object.keys(directives).forEach(key => {
app.directive(key, (directives as { [key: string]: Directive })[key]); app.directive(key, (directives as { [key: string]: Directive })[key]);
}); });
// 全局注册`@iconify/vue`图标库
import {
IconifyIconOffline,
IconifyIconOnline,
FontIcon
} from "./components/ReIcon";
app.component("IconifyIconOffline", IconifyIconOffline);
app.component("IconifyIconOnline", IconifyIconOnline);
app.component("FontIcon", FontIcon);
getServerConfig(app).then(async config => { getServerConfig(app).then(async config => {
injectResponsiveStorage(app, config); injectResponsiveStorage(app, config);
setupStore(app); setupStore(app);
app app.use(router).use(MotionPlugin).use(useElementPlus).use(usI18n);
.use(router)
.use(MotionPlugin)
.use(useElementPlus)
.use(usI18n)
.use(useFontawesome);
await router.isReady(); await router.isReady();
app.mount("#app"); app.mount("#app");
}); });

View File

@@ -1,8 +1,6 @@
import { App, Component } from "vue"; import { App, Component } from "vue";
import { import {
ElTag, ElTag,
ElAffix,
ElSkeleton,
ElBreadcrumb, ElBreadcrumb,
ElBreadcrumbItem, ElBreadcrumbItem,
ElScrollbar, ElScrollbar,
@@ -10,30 +8,21 @@ import {
ElButton, ElButton,
ElCol, ElCol,
ElRow, ElRow,
ElSpace,
ElDivider, ElDivider,
ElCard,
ElDropdown, ElDropdown,
ElDialog,
ElMenu, ElMenu,
ElMenuItem, ElMenuItem,
ElDropdownItem, ElDropdownItem,
ElDropdownMenu, ElDropdownMenu,
ElIcon, ElIcon,
ElInput, ElInput,
ElForm,
ElFormItem,
ElPopover, ElPopover,
ElPopper, ElPopper,
ElTooltip, ElTooltip,
ElDrawer, ElDrawer,
ElPagination,
ElAlert,
ElRadio, ElRadio,
ElRadioButton, ElRadioButton,
ElRadioGroup, ElRadioGroup,
ElDescriptions,
ElDescriptionsItem,
ElBacktop, ElBacktop,
ElSwitch, ElSwitch,
ElBadge, ElBadge,
@@ -43,7 +32,6 @@ import {
ElEmpty, ElEmpty,
ElCollapse, ElCollapse,
ElCollapseItem, ElCollapseItem,
ElTreeV2,
// 指令 // 指令
ElLoading, ElLoading,
ElInfiniteScroll ElInfiniteScroll
@@ -54,8 +42,6 @@ const plugins = [ElLoading, ElInfiniteScroll];
const components = [ const components = [
ElTag, ElTag,
ElAffix,
ElSkeleton,
ElBreadcrumb, ElBreadcrumb,
ElBreadcrumbItem, ElBreadcrumbItem,
ElScrollbar, ElScrollbar,
@@ -63,30 +49,21 @@ const components = [
ElButton, ElButton,
ElCol, ElCol,
ElRow, ElRow,
ElSpace,
ElDivider, ElDivider,
ElCard,
ElDropdown, ElDropdown,
ElDialog,
ElMenu, ElMenu,
ElMenuItem, ElMenuItem,
ElDropdownItem, ElDropdownItem,
ElDropdownMenu, ElDropdownMenu,
ElIcon, ElIcon,
ElInput, ElInput,
ElForm,
ElFormItem,
ElPopover, ElPopover,
ElPopper, ElPopper,
ElTooltip, ElTooltip,
ElDrawer, ElDrawer,
ElPagination,
ElAlert,
ElRadio, ElRadio,
ElRadioButton, ElRadioButton,
ElRadioGroup, ElRadioGroup,
ElDescriptions,
ElDescriptionsItem,
ElBacktop, ElBacktop,
ElSwitch, ElSwitch,
ElBadge, ElBadge,
@@ -95,60 +72,7 @@ const components = [
ElAvatar, ElAvatar,
ElEmpty, ElEmpty,
ElCollapse, ElCollapse,
ElCollapseItem, ElCollapseItem
ElTreeV2
];
// https://element-plus.org/zh-CN/component/icon.html
import {
Check,
Menu,
HomeFilled,
SetUp,
Edit,
Setting,
Lollipop,
Link,
Position,
Histogram,
RefreshRight,
ArrowDown,
Close,
CloseBold,
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
} from "@element-plus/icons-vue";
// Icon
export const iconComponents = [
Check,
Menu,
HomeFilled,
SetUp,
Edit,
Setting,
Lollipop,
Link,
Position,
Histogram,
RefreshRight,
ArrowDown,
Close,
CloseBold,
Bell,
Guide,
User,
Iphone,
Location,
Tickets,
OfficeBuilding,
Notebook
]; ];
export function useElementPlus(app: App) { export function useElementPlus(app: App) {
@@ -160,8 +84,4 @@ export function useElementPlus(app: App) {
plugins.forEach(plugin => { plugins.forEach(plugin => {
app.use(plugin); app.use(plugin);
}); });
// 注册图标
iconComponents.forEach((component: Component) => {
app.component(component.name, component);
});
} }

View File

@@ -1,21 +0,0 @@
/** 兼容fontawesome4和5版本
* 4版本: www.fontawesome.com.cn/faicons/
* 5版本https://fontawesome.com/v5.15/icons?d=gallery&p=2&m=free
* https://github.com/FortAwesome/vue-fontawesome
*/
import { App } from "vue";
import "font-awesome/css/font-awesome.css";
import { library } from "@fortawesome/fontawesome-svg-core";
import {
faUserSecret,
faCoffee,
faSpinner
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
// github.com/Remix-Design/RemixIcon/blob/master/README_CN.md#%E5%AE%89%E8%A3%85%E5%BC%95%E5%85%A5
import "remixicon/fonts/remixicon.css";
export function useFontawesome(app: App) {
library.add(faUserSecret, faCoffee, faSpinner);
app.component("font-awesome-icon", FontAwesomeIcon);
}

View File

@@ -3,23 +3,9 @@ export default {
hslogin: "Login", hslogin: "Login",
hssysManagement: "System Manage", hssysManagement: "System Manage",
hsBaseinfo: "Base Info", hsBaseinfo: "Base Info",
hsDict: "Dict Manage",
hseditor: "Editor",
hserror: "Error Page", hserror: "Error Page",
hsfourZeroFour: "404", hsfourZeroFour: "404",
hsfourZeroOne: "401", 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", hsmenus: "MultiLevel Menu",
hsmenu1: "Menu1", hsmenu1: "Menu1",
"hsmenu1-1": "Menu1-1", "hsmenu1-1": "Menu1-1",
@@ -31,8 +17,5 @@ export default {
permission: "Permission Manage", permission: "Permission Manage",
permissionPage: "Page Permission", permissionPage: "Page Permission",
permissionButton: "Button Permission", permissionButton: "Button Permission",
hstabs: "Tabs Operate",
hsMenuTree: "Menu Tree",
hsguide: "Guide",
externalLink: "External Link" externalLink: "External Link"
}; };

View File

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

View File

@@ -7,9 +7,8 @@ const errorRouter = {
component: Layout, component: Layout,
redirect: "/error/401", redirect: "/error/401",
meta: { meta: {
icon: "Position", icon: "position",
title: $t("menus.hserror"), title: $t("menus.hserror"),
showLink: true,
i18n: true, i18n: true,
rank: 7 rank: 7
}, },
@@ -20,8 +19,7 @@ const errorRouter = {
component: () => import("/@/views/error/401.vue"), component: () => import("/@/views/error/401.vue"),
meta: { meta: {
title: $t("menus.hsfourZeroOne"), title: $t("menus.hsfourZeroOne"),
i18n: true, i18n: true
showLink: true
} }
}, },
{ {
@@ -30,8 +28,7 @@ const errorRouter = {
component: () => import("/@/views/error/404.vue"), component: () => import("/@/views/error/404.vue"),
meta: { meta: {
title: $t("menus.hsfourZeroFour"), title: $t("menus.hsfourZeroFour"),
i18n: true, i18n: true
showLink: true
} }
} }
] ]

View File

@@ -6,9 +6,8 @@ const externalLink = {
name: "external", name: "external",
component: Layout, component: Layout,
meta: { meta: {
icon: "Link", icon: "link",
title: $t("menus.externalLink"), title: $t("menus.externalLink"),
showLink: true,
i18n: true, i18n: true,
rank: 190 rank: 190
}, },
@@ -17,7 +16,6 @@ const externalLink = {
path: "https://github.com/xiaoxian521/vue-pure-admin", path: "https://github.com/xiaoxian521/vue-pure-admin",
meta: { meta: {
title: $t("menus.externalLink"), title: $t("menus.externalLink"),
showLink: true,
i18n: true, i18n: true,
rank: 191 rank: 191
} }

View File

@@ -7,9 +7,8 @@ const homeRouter = {
component: Layout, component: Layout,
redirect: "/welcome", redirect: "/welcome",
meta: { meta: {
icon: "HomeFilled", icon: "home-filled",
title: $t("menus.hshome"), title: $t("menus.hshome"),
showLink: true,
i18n: true, i18n: true,
rank: 0 rank: 0
}, },
@@ -20,8 +19,7 @@ const homeRouter = {
component: () => import("/@/views/welcome.vue"), component: () => import("/@/views/welcome.vue"),
meta: { meta: {
title: $t("menus.hshome"), title: $t("menus.hshome"),
i18n: true, i18n: true
showLink: true
} }
} }
] ]

View File

@@ -18,7 +18,7 @@ const remainingRouter = [
name: "redirect", name: "redirect",
component: Layout, component: Layout,
meta: { meta: {
icon: "HomeFilled", icon: "home-filled",
title: $t("menus.hshome"), title: $t("menus.hshome"),
i18n: true, i18n: true,
showLink: false, showLink: false,

View File

@@ -19,37 +19,37 @@ const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}");
import { getAsyncRoutes } from "/@/api/routes"; import { getAsyncRoutes } from "/@/api/routes";
// 按照路由中meta下的rank等级升序来排序路由 // 按照路由中meta下的rank等级升序来排序路由
const ascending = (arr: any[]) => { function ascending(arr: any[]) {
return arr.sort( return arr.sort(
(a: { meta: { rank: number } }, b: { meta: { rank: number } }) => { (a: { meta: { rank: number } }, b: { meta: { rank: number } }) => {
return a?.meta?.rank - b?.meta?.rank; return a?.meta?.rank - b?.meta?.rank;
} }
); );
}; }
// 过滤meta中showLink为false的路由 // 过滤meta中showLink为false的路由
const filterTree = (data: RouteComponent[]) => { function filterTree(data: RouteComponent[]) {
const newTree = data.filter( const newTree = data.filter(
(v: { meta: { showLink: boolean } }) => v.meta.showLink (v: { meta: { showLink: boolean } }) => v.meta?.showLink !== false
); );
newTree.forEach( newTree.forEach(
(v: { children }) => v.children && (v.children = filterTree(v.children)) (v: { children }) => v.children && (v.children = filterTree(v.children))
); );
return newTree; return newTree;
}; }
// 批量删除缓存路由(keepalive) // 批量删除缓存路由(keepalive)
const delAliveRoutes = (delAliveRouteList: Array<RouteConfigs>) => { function delAliveRoutes(delAliveRouteList: Array<RouteConfigs>) {
delAliveRouteList.forEach(route => { delAliveRouteList.forEach(route => {
usePermissionStoreHook().cacheOperate({ usePermissionStoreHook().cacheOperate({
mode: "delete", mode: "delete",
name: route?.name name: route?.name
}); });
}); });
}; }
// 通过path获取父级路径 // 通过path获取父级路径
const getParentPaths = (path: string, routes: RouteRecordRaw[]) => { function getParentPaths(path: string, routes: RouteRecordRaw[]) {
// 深度遍历查找 // 深度遍历查找
function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) { function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) {
for (let i = 0; i < routes.length; i++) { for (let i = 0; i < routes.length; i++) {
@@ -70,10 +70,10 @@ const getParentPaths = (path: string, routes: RouteRecordRaw[]) => {
} }
return dfs(routes, path, []); return dfs(routes, path, []);
}; }
// 查找对应path的路由信息 // 查找对应path的路由信息
const findRouteByPath = (path: string, routes: RouteRecordRaw[]) => { function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
let res = routes.find((item: { path: string }) => item.path == path); let res = routes.find((item: { path: string }) => item.path == path);
if (res) { if (res) {
return res; return res;
@@ -91,20 +91,20 @@ const findRouteByPath = (path: string, routes: RouteRecordRaw[]) => {
} }
return null; return null;
} }
}; }
// 重置路由 // 重置路由
const resetRouter = (): void => { function resetRouter(): void {
router.getRoutes().forEach(route => { router.getRoutes().forEach(route => {
const { name } = route; const { name } = route;
if (name) { if (name) {
router.hasRoute(name) && router.removeRoute(name); router.hasRoute(name) && router.removeRoute(name);
} }
}); });
}; }
// 初始化路由 // 初始化路由
const initRouter = (name: string) => { function initRouter(name: string) {
return new Promise(resolve => { return new Promise(resolve => {
getAsyncRoutes({ name }).then(({ info }) => { getAsyncRoutes({ name }).then(({ info }) => {
if (info.length === 0) { if (info.length === 0) {
@@ -137,15 +137,15 @@ const initRouter = (name: string) => {
}); });
}); });
}); });
}; }
/** /**
* 将多级嵌套路由处理成一维数组 * 将多级嵌套路由处理成一维数组
* @param routesList 传入路由 * @param routesList 传入路由
* @returns 返回处理后的一维路由 * @returns 返回处理后的一维路由
*/ */
const formatFlatteningRoutes = (routesList: RouteRecordRaw[]) => { function formatFlatteningRoutes(routesList: RouteRecordRaw[]) {
if (routesList.length <= 0) return routesList; if (routesList.length === 0) return routesList;
for (let i = 0; i < routesList.length; i++) { for (let i = 0; i < routesList.length; i++) {
if (routesList[i].children) { if (routesList[i].children) {
routesList = routesList routesList = routesList
@@ -154,7 +154,7 @@ const formatFlatteningRoutes = (routesList: RouteRecordRaw[]) => {
} }
} }
return routesList; return routesList;
}; }
/** /**
* 一维数组处理成多级嵌套数组三级及以上的路由全部拍成二级keep-alive 只支持到二级缓存) * 一维数组处理成多级嵌套数组三级及以上的路由全部拍成二级keep-alive 只支持到二级缓存)
@@ -162,8 +162,8 @@ const formatFlatteningRoutes = (routesList: RouteRecordRaw[]) => {
* @param routesList 处理后的一维路由菜单数组 * @param routesList 处理后的一维路由菜单数组
* @returns 返回将一维数组重新处理成规定路由的格式 * @returns 返回将一维数组重新处理成规定路由的格式
*/ */
const formatTwoStageRoutes = (routesList: RouteRecordRaw[]) => { function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
if (routesList.length <= 0) return routesList; if (routesList.length === 0) return routesList;
const newRoutesList: RouteRecordRaw[] = []; const newRoutesList: RouteRecordRaw[] = [];
routesList.forEach((v: RouteRecordRaw) => { routesList.forEach((v: RouteRecordRaw) => {
if (v.path === "/") { if (v.path === "/") {
@@ -180,10 +180,10 @@ const formatTwoStageRoutes = (routesList: RouteRecordRaw[]) => {
} }
}); });
return newRoutesList; return newRoutesList;
}; }
// 处理缓存路由(添加、删除、刷新) // 处理缓存路由(添加、删除、刷新)
const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => { function handleAliveRoute(matched: RouteRecordNormalized[], mode?: string) {
switch (mode) { switch (mode) {
case "add": case "add":
matched.forEach(v => { matched.forEach(v => {
@@ -207,10 +207,10 @@ const handleAliveRoute = (matched: RouteRecordNormalized[], mode?: string) => {
}); });
}, 100); }, 100);
} }
}; }
// 过滤后端传来的动态路由 重新生成规范路由 // 过滤后端传来的动态路由 重新生成规范路由
const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => { function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
if (!arrRoutes || !arrRoutes.length) return; if (!arrRoutes || !arrRoutes.length) return;
const modulesRoutesKeys = Object.keys(modulesRoutes); const modulesRoutesKeys = Object.keys(modulesRoutes);
arrRoutes.forEach((v: RouteRecordRaw) => { arrRoutes.forEach((v: RouteRecordRaw) => {
@@ -225,10 +225,10 @@ const addAsyncRoutes = (arrRoutes: Array<RouteRecordRaw>) => {
} }
}); });
return arrRoutes; return arrRoutes;
}; }
// 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html // 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html
const getHistoryMode = (): RouterHistory => { function getHistoryMode(): RouterHistory {
const routerHistory = loadEnv().VITE_ROUTER_HISTORY; const routerHistory = loadEnv().VITE_ROUTER_HISTORY;
// len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1 // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1
const historyMode = routerHistory.split(","); const historyMode = routerHistory.split(",");
@@ -249,10 +249,10 @@ const getHistoryMode = (): RouterHistory => {
return createWebHistory(rightMode); return createWebHistory(rightMode);
} }
} }
}; }
// 是否有权限 // 是否有权限
const hasPermissions = (value: Array<string>): boolean => { function hasPermissions(value: Array<string>): boolean {
if (value && value instanceof Array && value.length > 0) { if (value && value instanceof Array && value.length > 0) {
const roles = usePermissionStoreHook().buttonAuth; const roles = usePermissionStoreHook().buttonAuth;
const permissionRoles = value; const permissionRoles = value;
@@ -268,7 +268,7 @@ const hasPermissions = (value: Array<string>): boolean => {
} else { } else {
return false; return false;
} }
}; }
export { export {
ascending, ascending,

View File

@@ -16,9 +16,8 @@ export const useMultiTagsStore = defineStore({
parentPath: "/", parentPath: "/",
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
icon: "HomeFilled", icon: "home-filled",
i18n: true, i18n: true
showLink: true
} }
} }
], ],

View File

@@ -59,9 +59,8 @@ export const useUserStore = defineStore({
parentPath: "/", parentPath: "/",
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
icon: "HomeFilled", icon: "home-filled",
i18n: true, i18n: true
showLink: true
} }
} }
]); ]);

View File

@@ -85,7 +85,7 @@
align-items: center; align-items: center;
} }
.icon i { .icon svg {
color: #d9d9d9; color: #d9d9d9;
transition: 0.5s; transition: 0.5s;
} }
@@ -107,7 +107,7 @@
padding: 0; padding: 0;
} }
.input-group.focus .icon i { .input-group.focus .icon svg {
color: #5392f0; color: #5392f0;
} }

View File

@@ -1,21 +0,0 @@
interface ProxyAlgorithm {
increaseIndexes<T>(val: Array<T>): Array<T>;
}
class algorithmProxy implements ProxyAlgorithm {
constructor() {}
// 数组每一项添加索引字段
public increaseIndexes<T>(val: Array<T>): Array<T> {
return Object.keys(val)
.map(v => {
return {
...val[v],
key: v
};
})
.filter(v => v.meta && v.meta.showLink);
}
}
export const algorithm = new algorithmProxy();

View File

@@ -1,54 +0,0 @@
interface ProxyLoader {
loadCss(src: string): any;
loadScript(src: string): Promise<any>;
loadScriptConcurrent(src: Array<string>): Promise<any>;
}
class loaderProxy implements ProxyLoader {
constructor() {}
protected scriptLoaderCache: Array<string> = [];
public loadCss = (src: string): any => {
const element: HTMLLinkElement = document.createElement("link");
element.rel = "stylesheet";
element.href = src;
document.body.appendChild(element);
};
public loadScript = async (src: string): Promise<any> => {
if (this.scriptLoaderCache.includes(src)) {
return src;
} else {
const element: HTMLScriptElement = document.createElement("script");
element.src = src;
document.body.appendChild(element);
element.onload = () => {
return this.scriptLoaderCache.push(src);
};
}
};
public loadScriptConcurrent = async (
srcList: Array<string>
): Promise<any> => {
if (Array.isArray(srcList)) {
const len: number = srcList.length;
if (len > 0) {
let count = 0;
srcList.map(src => {
if (src) {
this.loadScript(src).then(() => {
count++;
if (count === len) {
return;
}
});
}
});
}
}
};
}
export const loader = new loaderProxy();

View File

@@ -1,35 +0,0 @@
import ResizeObserver from "resize-observer-polyfill";
const isServer = typeof window === "undefined";
const resizeHandler = (entries: any[]): void => {
for (const entry of entries) {
const listeners = entry.target.__resizeListeners__ || [];
if (listeners.length) {
listeners.forEach((fn: () => any) => {
fn();
});
}
}
};
export const addResizeListener = (element: any, fn: () => any): any => {
if (isServer) return;
if (!element.__resizeListeners__) {
element.__resizeListeners__ = [];
element.__ro__ = new ResizeObserver(resizeHandler);
element.__ro__.observe(element);
}
element.__resizeListeners__.push(fn);
};
export const removeResizeListener = (element: any, fn: () => any): any => {
if (!element || !element.__resizeListeners__) return;
element.__resizeListeners__.splice(
element.__resizeListeners__.indexOf(fn),
1
);
if (!element.__resizeListeners__.length) {
element.__ro__.disconnect();
}
};

View File

@@ -47,8 +47,7 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
meta: { meta: {
title: "menus.hshome", title: "menus.hshome",
i18n: true, i18n: true,
icon: "HomeFilled", icon: "home-filled"
showLink: true
} }
} }
] ]

View File

@@ -1,43 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from "vue"; import { ref } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { initRouter } from "/@/router/utils"; import { initRouter } from "/@/router/utils";
import { storageSession } from "/@/utils/storage"; import { storageSession } from "/@/utils/storage";
import { addClass, removeClass } from "/@/utils/operate"; import { addClass, removeClass } from "/@/utils/operate";
import bg from "/@/assets/login/bg.png"; import bg from "/@/assets/login/bg.png";
import avatar from "/@/assets/login/avatar.svg"; import avatar from "/@/assets/login/avatar.svg?component";
import illustration0 from "/@/assets/login/illustration0.svg"; import illustration from "/@/assets/login/illustration.svg?component";
import illustration1 from "/@/assets/login/illustration1.svg";
import illustration2 from "/@/assets/login/illustration2.svg";
import illustration3 from "/@/assets/login/illustration3.svg";
import illustration4 from "/@/assets/login/illustration4.svg";
import illustration5 from "/@/assets/login/illustration5.svg";
import illustration6 from "/@/assets/login/illustration6.svg";
const router = useRouter(); const router = useRouter();
// eslint-disable-next-line vue/return-in-computed-property
const currentWeek = computed(() => {
switch (String(new Date().getDay())) {
case "0":
return illustration0;
case "1":
return illustration1;
case "2":
return illustration2;
case "3":
return illustration3;
case "4":
return illustration4;
case "5":
return illustration5;
case "6":
return illustration6;
default:
return illustration4;
}
});
let user = ref("admin"); let user = ref("admin");
let pwd = ref("123456"); let pwd = ref("123456");
@@ -73,7 +45,7 @@ function onPwdBlur() {
<img :src="bg" class="wave" /> <img :src="bg" class="wave" />
<div class="login-container"> <div class="login-container">
<div class="img"> <div class="img">
<component :is="currentWeek"></component> <illustration />
</div> </div>
<div class="login-box"> <div class="login-box">
<div class="login-form"> <div class="login-form">
@@ -110,7 +82,7 @@ function onPwdBlur() {
}" }"
> >
<div class="icon"> <div class="icon">
<i class="fa fa-user"></i> <IconifyIconOffline icon="fa-user" width="14" height="14" />
</div> </div>
<div> <div>
<h5>用户名</h5> <h5>用户名</h5>
@@ -139,7 +111,7 @@ function onPwdBlur() {
}" }"
> >
<div class="icon"> <div class="icon">
<i class="fa fa-lock"></i> <IconifyIconOffline icon="fa-lock" width="14" height="14" />
</div> </div>
<div> <div>
<h5>密码</h5> <h5>密码</h5>

19
types/global.d.ts vendored
View File

@@ -6,16 +6,16 @@ import type {
PropType as VuePropType PropType as VuePropType
} from "vue"; } from "vue";
// GlobalComponents for Volar
declare module "vue" {
export interface GlobalComponents {
IconifyIconOffline: typeof import("../src/components/ReIcon")["IconifyIconOffline"];
IconifyIconOnline: typeof import("../src/components/ReIcon")["IconifyIconOnline"];
FontIcon: typeof import("../src/components/ReIcon")["FontIcon"];
}
}
declare global { declare global {
const __APP_INFO__: {
pkg: {
name: string;
version: string;
dependencies: Recordable<string>;
devDependencies: Recordable<string>;
};
lastBuildTime: string;
};
interface Window { interface Window {
// Global vue app instance // Global vue app instance
__APP__: App<Element>; __APP__: App<Element>;
@@ -23,7 +23,6 @@ declare global {
mozCancelAnimationFrame: (handle: number) => void; mozCancelAnimationFrame: (handle: number) => void;
oCancelAnimationFrame: (handle: number) => void; oCancelAnimationFrame: (handle: number) => void;
msCancelAnimationFrame: (handle: number) => void; msCancelAnimationFrame: (handle: number) => void;
webkitRequestAnimationFrame: (callback: FrameRequestCallback) => number; webkitRequestAnimationFrame: (callback: FrameRequestCallback) => number;
mozRequestAnimationFrame: (callback: FrameRequestCallback) => number; mozRequestAnimationFrame: (callback: FrameRequestCallback) => number;
oRequestAnimationFrame: (callback: FrameRequestCallback) => number; oRequestAnimationFrame: (callback: FrameRequestCallback) => number;

View File

@@ -1,13 +1,7 @@
import { resolve } from "path"; import { resolve } from "path";
import vue from "@vitejs/plugin-vue";
import svgLoader from "vite-svg-loader";
import legacy from "@vitejs/plugin-legacy";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { warpperEnv, regExps } from "./build"; import { warpperEnv, regExps } from "./build";
import { viteMockServe } from "vite-plugin-mock"; import { getPluginsList } from "./build/plugins";
import ElementPlus from "unplugin-element-plus/vite";
import { UserConfigExport, ConfigEnv, loadEnv } from "vite"; import { UserConfigExport, ConfigEnv, loadEnv } from "vite";
import themePreprocessorPlugin from "@zougt/vite-plugin-theme-preprocessor";
// 当前执行node命令时文件夹的地址工作目录 // 当前执行node命令时文件夹的地址工作目录
const root: string = process.cwd(); const root: string = process.cwd();
@@ -33,7 +27,6 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
VITE_PROXY_DOMAIN, VITE_PROXY_DOMAIN,
VITE_PROXY_DOMAIN_REAL VITE_PROXY_DOMAIN_REAL
} = warpperEnv(loadEnv(mode, root)); } = warpperEnv(loadEnv(mode, root));
const prodMock = true;
return { return {
base: VITE_PUBLIC_PATH, base: VITE_PUBLIC_PATH,
root, root,
@@ -77,93 +70,16 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
} }
: null : null
}, },
plugins: [ plugins: getPluginsList(command, VITE_LEGACY),
vue(),
// jsx、tsx语法支持
vueJsx(),
// 自定义主题
themePreprocessorPlugin({
scss: {
multipleScopeVars: [
{
scopeName: "layout-theme-default",
path: pathResolve("src/layout/theme/default-vars.scss")
},
{
scopeName: "layout-theme-light",
path: pathResolve("src/layout/theme/light-vars.scss")
},
{
scopeName: "layout-theme-dusk",
path: pathResolve("src/layout/theme/dusk-vars.scss")
},
{
scopeName: "layout-theme-volcano",
path: pathResolve("src/layout/theme/volcano-vars.scss")
},
{
scopeName: "layout-theme-yellow",
path: pathResolve("src/layout/theme/yellow-vars.scss")
},
{
scopeName: "layout-theme-mingQing",
path: pathResolve("src/layout/theme/mingQing-vars.scss")
},
{
scopeName: "layout-theme-auroraGreen",
path: pathResolve("src/layout/theme/auroraGreen-vars.scss")
},
{
scopeName: "layout-theme-pink",
path: pathResolve("src/layout/theme/pink-vars.scss")
},
{
scopeName: "layout-theme-saucePurple",
path: pathResolve("src/layout/theme/saucePurple-vars.scss")
}
],
// 默认取 multipleScopeVars[0].scopeName
defaultScopeName: "",
// 在生产模式是否抽取独立的主题css文件extract为true以下属性有效
extract: true,
// 独立主题css文件的输出路径默认取 viteConfig.build.assetsDir 相对于 (viteConfig.build.outDir)
outputDir: "",
// 会选取defaultScopeName对应的主题css文件在html添加link
themeLinkTagId: "head",
// "head"||"head-prepend" || "body" ||"body-prepend"
themeLinkTagInjectTo: "head",
// 是否对抽取的css文件内对应scopeName的权重类名移除
removeCssScopeName: false,
// 可以自定义css文件名称的函数
customThemeCssFileName: scopeName => scopeName
}
}),
// svg组件化支持
svgLoader(),
ElementPlus({}),
// mock支持
viteMockServe({
mockPath: "mock",
localEnabled: command === "serve",
prodEnabled: command !== "serve" && prodMock,
injectCode: `
import { setupProdMockServer } from './mockProdServer';
setupProdMockServer();
`,
logger: true
}),
// 是否为打包后的文件提供传统浏览器兼容性支持
VITE_LEGACY
? legacy({
targets: ["ie >= 11"],
additionalLegacyPolyfills: ["regenerator-runtime/runtime"]
})
: null
],
optimizeDeps: { optimizeDeps: {
include: [ include: [
"element-plus/lib/locale/lang/zh-cn", "pinia",
"element-plus/lib/locale/lang/en" "vue-i18n",
"lodash-es",
"@vueuse/core",
"@iconify/vue",
"element-plus/lib/locale/lang/en",
"element-plus/lib/locale/lang/zh-cn"
], ],
exclude: ["@zougt/vite-plugin-theme-preprocessor/dist/browser-utils"] exclude: ["@zougt/vite-plugin-theme-preprocessor/dist/browser-utils"]
}, },

50
windi.config.ts Normal file
View File

@@ -0,0 +1,50 @@
// https://cn.windicss.org/ 中文文档
import { defineConfig } from "windicss/helpers";
import colors from "windicss/colors";
import typography from "windicss/plugin/typography";
export default defineConfig({
darkMode: "class",
attributify: true,
plugins: [typography()],
theme: {
extend: {
zIndex: {
"-1": "-1"
},
screens: {
sm: "576px",
md: "768px",
lg: "992px",
xl: "1200px",
"2xl": "1600px"
},
typography: {
DEFAULT: {
css: {
maxWidth: "65ch",
color: "inherit",
a: {
color: "inherit",
opacity: 0.75,
fontWeight: "500",
textDecoration: "underline",
"&:hover": {
opacity: 1,
color: colors.teal[600]
}
},
b: { color: "inherit" },
strong: { color: "inherit" },
em: { color: "inherit" },
h1: { color: "inherit" },
h2: { color: "inherit" },
h3: { color: "inherit" },
h4: { color: "inherit" },
code: { color: "inherit" }
}
}
}
}
}
});