Merge branch 'main' of github.com:pure-admin/vue-pure-admin into gitee

This commit is contained in:
xiaoxian521 2023-05-08 20:14:32 +08:00
commit c77ec6bda3
102 changed files with 6318 additions and 2507 deletions

3
.env
View File

@ -1,2 +1,5 @@
# 平台本地运行端口号 # 平台本地运行端口号
VITE_PORT = 8848 VITE_PORT = 8848
# 是否隐藏首页 隐藏 true 不隐藏 false 勿删除VITE_HIDE_HOME只需在.env文件配置
VITE_HIDE_HOME = false

View File

@ -13,4 +13,4 @@ VITE_CDN = true
# 是否启用gzip压缩或brotli压缩分两种情况删除原始文件和不删除原始文件 # 是否启用gzip压缩或brotli压缩分两种情况删除原始文件和不删除原始文件
# 压缩时不删除原始文件的配置gzip、brotli、both同时开启 gzip 与 brotli 压缩、none不开启压缩默认 # 压缩时不删除原始文件的配置gzip、brotli、both同时开启 gzip 与 brotli 压缩、none不开启压缩默认
# 压缩时删除原始文件的配置gzip-clear、brotli-clear、both-clear同时开启 gzip 与 brotli 压缩、none不开启压缩默认 # 压缩时删除原始文件的配置gzip-clear、brotli-clear、both-clear同时开启 gzip 与 brotli 压缩、none不开启压缩默认
VITE_COMPRESSION = "both-clear" VITE_COMPRESSION = "none"

View File

@ -1,3 +1,4 @@
/dist/* /dist/*
/public/* /public/*
public/* public/*
src/style/reset.scss

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2023 pure-admin Copyright (c) 2020-present, pure-admin
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

@ -150,7 +150,7 @@ If you think this project is helpful to you, you can help the author buy a glass
In principle, no fees and copyrights are charged, and you can use it with confidence, but if you need secondary open source, please contact the author for permission! In principle, no fees and copyrights are charged, and you can use it with confidence, but if you need secondary open source, please contact the author for permission!
[MIT © xiaoxian521-2023](./LICENSE) [MIT © 2020-present, pure-admin](./LICENSE)
## Backers ## Backers

View File

@ -154,7 +154,7 @@ pnpm build
原则上不收取任何费用及版权,可以放心使用,不过如需二次开源(比如用此平台二次开发并开源)请联系作者获取许可! 原则上不收取任何费用及版权,可以放心使用,不过如需二次开源(比如用此平台二次开发并开源)请联系作者获取许可!
[MIT © xiaoxian521-2023](./LICENSE) [MIT © 2020-present, pure-admin](./LICENSE)
## 支持者 ## 支持者

View File

@ -6,6 +6,7 @@ const warpperEnv = (envConf: Recordable): ViteEnv => {
VITE_PUBLIC_PATH: "", VITE_PUBLIC_PATH: "",
VITE_ROUTER_HISTORY: "", VITE_ROUTER_HISTORY: "",
VITE_CDN: false, VITE_CDN: false,
VITE_HIDE_HOME: "false",
VITE_COMPRESSION: "none" VITE_COMPRESSION: "none"
}; };

View File

@ -1,8 +1,8 @@
import type { Plugin } from "vite"; import type { Plugin } from "vite";
import dayjs, { Dayjs } from "dayjs"; import dayjs, { Dayjs } from "dayjs";
import utils from "@pureadmin/utils";
import duration from "dayjs/plugin/duration"; import duration from "dayjs/plugin/duration";
import { green, blue, bold } from "picocolors"; import { green, blue, bold } from "picocolors";
import { getPackageSize } from "@pureadmin/utils";
dayjs.extend(duration); dayjs.extend(duration);
export function viteBuildInfo(): Plugin { export function viteBuildInfo(): Plugin {
@ -33,7 +33,7 @@ export function viteBuildInfo(): Plugin {
closeBundle() { closeBundle() {
if (config.command === "build") { if (config.command === "build") {
endTime = dayjs(new Date()); endTime = dayjs(new Date());
getPackageSize({ utils.getPackageSize({
folder: outDir, folder: outDir,
callback: (size: string) => { callback: (size: string) => {
console.log( console.log(

View File

@ -12,7 +12,6 @@ const include = [
"axios", "axios",
"pinia", "pinia",
"swiper", "swiper",
"echarts",
"intro.js", "intro.js",
"vue-i18n", "vue-i18n",
"js-cookie", "js-cookie",
@ -38,11 +37,7 @@ const include = [
"@amap/amap-jsapi-loader", "@amap/amap-jsapi-loader",
"el-table-infinite-scroll", "el-table-infinite-scroll",
"@wangeditor/editor-for-vue", "@wangeditor/editor-for-vue",
"xgplayer/dist/simple_player", "vuedraggable/src/vuedraggable"
"xgplayer/es/controls/volume",
"vuedraggable/src/vuedraggable",
"xgplayer/es/controls/screenShot",
"xgplayer/es/controls/playbackRate"
]; ];
/** /**

View File

@ -21,54 +21,54 @@
html, html,
body, body,
#app { #app {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex;
position: relative;
justify-content: center;
align-items: center;
overflow: hidden; overflow: hidden;
} }
.loader, .loader,
.loader:before, .loader::before,
.loader:after { .loader::after {
border-radius: 50%;
width: 2.5em; width: 2.5em;
height: 2.5em; height: 2.5em;
border-radius: 50%;
animation: load-animation 1.8s infinite ease-in-out;
animation-fill-mode: both; animation-fill-mode: both;
animation: loadAnimation 1.8s infinite ease-in-out;
} }
.loader { .loader {
color: #406eeb;
font-size: 10px;
margin: 80px auto;
position: relative; position: relative;
top: 0;
margin: 80px auto;
font-size: 10px;
color: #406eeb;
text-indent: -9999em; text-indent: -9999em;
transform: translateZ(0); transform: translateZ(0);
animation-delay: -0.16s;
top: 0;
transform: translate(-50%, 0); transform: translate(-50%, 0);
animation-delay: -0.16s;
} }
.loader:before, .loader::before,
.loader:after { .loader::after {
content: "";
position: absolute; position: absolute;
top: 0; top: 0;
content: "";
} }
.loader:before { .loader::before {
left: -3.5em; left: -3.5em;
animation-delay: -0.32s; animation-delay: -0.32s;
} }
.loader:after { .loader::after {
left: 3.5em; left: 3.5em;
} }
@keyframes loadAnimation { @keyframes load-animation {
0%, 0%,
80%, 80%,
100% { 100% {

View File

@ -36,6 +36,7 @@ menus:
hsfourZeroOne: "403" hsfourZeroOne: "403"
hsFive: "500" hsFive: "500"
hscomponents: Components hscomponents: Components
hsdialog: Dialog Components
hsmessage: Message Tips Components hsmessage: Message Tips Components
hsvideo: Video Components hsvideo: Video Components
hsmap: Map Components hsmap: Map Components

View File

@ -36,6 +36,7 @@ menus:
hsfourZeroOne: "403" hsfourZeroOne: "403"
hsFive: "500" hsFive: "500"
hscomponents: 组件 hscomponents: 组件
hsdialog: 函数式弹框组件
hsmessage: 消息提示组件 hsmessage: 消息提示组件
hsvideo: 视频组件 hsvideo: 视频组件
hsmap: 地图组件 hsmap: 地图组件

View File

@ -15,8 +15,8 @@
"cloc": "NODE_OPTIONS=--max-old-space-size=4096 cloc . --exclude-dir=node_modules --exclude-lang=YAML", "cloc": "NODE_OPTIONS=--max-old-space-size=4096 cloc . --exclude-dir=node_modules --exclude-lang=YAML",
"clean:cache": "rm -rf node_modules && rm -rf .eslintcache && pnpm install", "clean:cache": "rm -rf node_modules && rm -rf .eslintcache && pnpm install",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix", "lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,less,scss,vue,html,md}\"", "lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/", "lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --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", "lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
@ -29,121 +29,131 @@
], ],
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@howdyjs/mouse-menu": "^2.0.5", "@howdyjs/mouse-menu": "^2.0.7",
"@logicflow/core": "^1.1.30", "@logicflow/core": "^1.2.5",
"@logicflow/extension": "^1.1.30", "@logicflow/extension": "^1.2.5",
"@pureadmin/descriptions": "^1.1.1", "@pureadmin/descriptions": "^1.1.1",
"@pureadmin/table": "^2.0.0", "@pureadmin/table": "^2.0.0",
"@pureadmin/utils": "^1.8.5", "@pureadmin/utils": "^1.8.9",
"@vueuse/core": "^9.13.0", "@vueuse/core": "^10.1.2",
"@vueuse/motion": "2.0.0-beta.12", "@vueuse/motion": "2.0.0-beta.12",
"@wangeditor/editor": "^5.1.21", "@wangeditor/editor": "^5.1.21",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.2.2", "axios": "^1.4.0",
"china-area-data": "^5.0.1", "china-area-data": "^5.0.1",
"cropperjs": "^1.5.12", "cropperjs": "^1.5.13",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"echarts": "^5.4.2", "echarts": "^5.4.2",
"el-table-infinite-scroll": "^3.0.1", "el-table-infinite-scroll": "^3.0.1",
"element-plus": "^2.3.1", "element-plus": "^2.3.4",
"element-resize-detector": "^1.2.4", "element-resize-detector": "^1.2.4",
"intro.js": "^6.0.0", "intro.js": "^7.0.1",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.5",
"jsbarcode": "^3.11.5", "jsbarcode": "^3.11.5",
"md-editor-v3": "^2.7.2", "md-editor-v3": "^3.0.1",
"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.33", "pinia": "^2.0.35",
"qrcode": "^1.5.1", "qrcode": "^1.5.3",
"qs": "^6.11.0", "qs": "^6.11.1",
"responsive-storage": "^2.2.0", "responsive-storage": "^2.2.0",
"sortablejs": "^1.15.0", "sortablejs": "^1.15.0",
"swiper": "^9.1.0", "swiper": "^9.2.4",
"typeit": "^8.7.1", "typeit": "^8.7.1",
"v-contextmenu": "3.0.0", "v-contextmenu": "3.0.0",
"vue": "^3.2.47", "vue": "^3.2.47",
"vue-i18n": "^9.2.2", "vue-i18n": "^9.2.2",
"vue-json-pretty": "^2.2.3", "vue-json-pretty": "^2.2.4",
"vue-pdf-embed": "^1.1.5", "vue-pdf-embed": "^1.1.6",
"vue-router": "^4.1.6", "vue-router": "^4.1.6",
"vue-types": "^5.0.2", "vue-types": "^5.0.2",
"vue-virtual-scroller": "2.0.0-beta.7", "vue-virtual-scroller": "2.0.0-beta.7",
"vue3-danmaku": "^1.2.0", "vue3-danmaku": "1.4.0-beta.1",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"xgplayer": "^2.32.2", "xgplayer": "^3.0.2",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "13.1.0", "@commitlint/cli": "^17.6.3",
"@commitlint/config-conventional": "13.1.0", "@commitlint/config-conventional": "^17.6.3",
"@iconify-icons/ep": "^1.2.10", "@iconify-icons/ep": "^1.2.11",
"@iconify-icons/ri": "^1.2.4", "@iconify-icons/ri": "^1.2.7",
"@iconify/vue": "^4.0.2", "@iconify/vue": "^4.1.1",
"@intlify/unplugin-vue-i18n": "^0.10.0", "@intlify/unplugin-vue-i18n": "^0.10.0",
"@pureadmin/theme": "^3.0.0", "@pureadmin/theme": "^3.0.0",
"@types/element-resize-detector": "1.1.3", "@types/element-resize-detector": "1.1.3",
"@types/intro.js": "^5.1.0", "@types/intro.js": "^5.1.1",
"@types/js-cookie": "^3.0.1", "@types/js-cookie": "^3.0.3",
"@types/mockjs": "^1.0.7", "@types/mockjs": "^1.0.7",
"@types/node": "^18.11.9", "@types/node": "^18.15.12",
"@types/nprogress": "0.2.0", "@types/nprogress": "0.2.0",
"@types/qrcode": "^1.4.2", "@types/qrcode": "^1.5.0",
"@types/qs": "^6.9.7", "@types/qs": "^6.9.7",
"@types/sortablejs": "^1.15.0", "@types/sortablejs": "^1.15.1",
"@typescript-eslint/eslint-plugin": "^5.43.0", "@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.43.0", "@typescript-eslint/parser": "^5.59.2",
"@vitejs/plugin-vue": "^4.1.0", "@vitejs/plugin-vue": "^4.2.1",
"@vitejs/plugin-vue-jsx": "^3.0.1", "@vitejs/plugin-vue-jsx": "^3.0.1",
"@vue/eslint-config-prettier": "^7.0.0", "@vue/eslint-config-prettier": "^7.1.0",
"@vue/eslint-config-typescript": "^11.0.2", "@vue/eslint-config-typescript": "^11.0.3",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"cloc": "^2.11.0", "cloc": "^2.11.0",
"cssnano": "^6.0.0", "cssnano": "^6.0.1",
"eslint": "^8.8.0", "eslint": "^8.40.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-vue": "^9.7.0", "eslint-plugin-vue": "^9.11.1",
"husky": "^7.0.4", "husky": "^8.0.3",
"lint-staged": "11.1.2", "lint-staged": "^13.2.2",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"postcss": "^8.4.21", "postcss": "^8.4.23",
"postcss-html": "^1.5.0", "postcss-html": "^1.5.0",
"postcss-import": "^15.1.0", "postcss-import": "^15.1.0",
"postcss-scss": "^4.0.6", "postcss-scss": "^4.0.6",
"prettier": "^2.5.1", "prettier": "^2.8.7",
"pretty-quick": "3.1.1", "pretty-quick": "3.1.1",
"rimraf": "^4.4.1", "rimraf": "^5.0.0",
"rollup-plugin-visualizer": "^5.9.0", "rollup-plugin-visualizer": "^5.9.0",
"sass": "^1.60.0", "sass": "^1.62.1",
"sass-loader": "^13.2.2", "sass-loader": "^13.2.2",
"stylelint": "^14.3.0", "stylelint": "^15.6.1",
"stylelint-config-html": "^1.0.0", "stylelint-config-html": "^1.1.0",
"stylelint-config-prettier": "^9.0.3", "stylelint-config-recess-order": "^4.0.0",
"stylelint-config-recommended": "^9.0.0", "stylelint-config-recommended": "^12.0.0",
"stylelint-config-standard": "^29.0.0", "stylelint-config-recommended-scss": "^11.0.0",
"stylelint-order": "^5.0.0", "stylelint-config-recommended-vue": "^1.4.0",
"stylelint-config-standard": "^33.0.0",
"stylelint-config-standard-scss": "^9.0.0",
"stylelint-order": "^6.0.3",
"stylelint-prettier": "^3.0.0",
"stylelint-scss": "^5.0.0",
"svgo": "^3.0.2", "svgo": "^3.0.2",
"tailwindcss": "^3.2.7", "tailwindcss": "^3.3.2",
"terser": "^5.16.8", "terser": "^5.17.1",
"typescript": "^5.0.2", "typescript": "^5.0.4",
"unplugin-vue-define-options": "^1.0.0", "unplugin-vue-define-options": "1.1.6",
"vite": "^4.2.1", "vite": "^4.3.5",
"vite-plugin-cdn-import": "^0.3.5", "vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "^2.9.6", "vite-plugin-mock": "^2.9.6",
"vite-plugin-remove-console": "^2.1.0", "vite-plugin-remove-console": "^2.1.1",
"vite-svg-loader": "^4.0.0", "vite-svg-loader": "^4.0.0",
"vue-eslint-parser": "^9.1.0", "vue-eslint-parser": "^9.2.1",
"vue-tsc": "^1.2.0" "vue-tsc": "^1.2.0"
}, },
"pnpm": { "pnpm": {
"peerDependencyRules": { "peerDependencyRules": {
"ignoreMissing": [ "ignoreMissing": [
"rollup", "rollup",
"webpack" "webpack",
"core-js"
] ]
},
"allowedDeprecatedVersions": {
"sourcemap-codec": "*",
"stable": "*"
} }
}, },
"repository": "git@github.com:pure-admin/vue-pure-admin.git", "repository": "git@github.com:pure-admin/vue-pure-admin.git",

6731
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,7 @@
module.exports = { module.exports = {
plugins: { plugins: {
"postcss-import": {}, "postcss-import": {},
"tailwindcss/nesting": {},
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}) ...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {})

View File

@ -19,6 +19,7 @@
"MenuArrowIconNoTransition": true, "MenuArrowIconNoTransition": true,
"CachingAsyncRoutes": false, "CachingAsyncRoutes": false,
"TooltipEffect": "light", "TooltipEffect": "light",
"ResponsiveStorageNameSpace": "responsive-",
"MapConfigure": { "MapConfigure": {
"amapKey": "97b3248d1553172e81f168cf94ea667e", "amapKey": "97b3248d1553172e81f168cf94ea667e",
"options": { "options": {

View File

@ -1,6 +1,7 @@
<template> <template>
<el-config-provider :locale="currentLocale"> <el-config-provider :locale="currentLocale">
<router-view /> <router-view />
<ReDialog />
</el-config-provider> </el-config-provider>
</template> </template>
@ -9,10 +10,12 @@ 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";
import { ReDialog } from "@/components/ReDialog";
export default defineComponent({ export default defineComponent({
name: "app", name: "app",
components: { components: {
[ElConfigProvider.name]: ElConfigProvider [ElConfigProvider.name]: ElConfigProvider,
ReDialog
}, },
computed: { computed: {
currentLocale() { currentLocale() {

View File

@ -0,0 +1,39 @@
import { ref } from "vue";
import reDialog from "./index.vue";
import { useTimeoutFn } from "@vueuse/core";
import { withInstall } from "@pureadmin/utils";
import type {
EventType,
ArgsType,
DialogProps,
ButtonProps,
DialogOptions
} from "./type";
const dialogStore = ref<Array<DialogOptions>>([]);
const addDialog = (options: DialogOptions) => {
const open = () =>
dialogStore.value.push(Object.assign(options, { visible: true }));
if (options?.openDelay) {
useTimeoutFn(() => {
open();
}, options.openDelay);
} else {
open();
}
};
const closeDialog = (options: DialogOptions, index: number, args?: any) => {
dialogStore.value.splice(index, 1);
options.closeCallBack && options.closeCallBack({ options, index, args });
};
const closeAllDialog = () => {
dialogStore.value = [];
};
const ReDialog = withInstall(reDialog);
export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions };
export { ReDialog, dialogStore, addDialog, closeDialog, closeAllDialog };

View File

@ -0,0 +1,106 @@
<script setup lang="ts">
import { computed } from "vue";
import { isFunction } from "@pureadmin/utils";
import {
type DialogOptions,
type ButtonProps,
type EventType,
dialogStore,
closeDialog
} from "./index";
const footerButtons = computed(() => {
return (options: DialogOptions) => {
return options?.footerButtons?.length > 0
? options.footerButtons
: ([
{
label: "取消",
text: true,
bg: true,
btnClick: ({ dialog: { options, index } }) => {
closeDialog(options, index, { command: "cancel" });
}
},
{
label: "确定",
type: "primary",
text: true,
bg: true,
btnClick: ({ dialog: { options, index } }) => {
closeDialog(options, index, { command: "sure" });
}
}
] as Array<ButtonProps>);
};
});
function eventsCallBack(
event: EventType,
options: DialogOptions,
index: number
) {
if (options?.[event] && isFunction(options?.[event])) {
return options?.[event]({ options, index });
}
}
function handleClose(
options: DialogOptions,
index: number,
args = { command: "close" }
) {
closeDialog(options, index, args);
eventsCallBack("close", options, index);
}
</script>
<template>
<el-dialog
v-for="(options, index) in dialogStore"
:key="index"
v-bind="options"
v-model="options.visible"
@opened="eventsCallBack('open', options, index)"
@close="handleClose(options, index)"
@openAutoFocus="eventsCallBack('openAutoFocus', options, index)"
@closeAutoFocus="eventsCallBack('closeAutoFocus', options, index)"
>
<!-- header -->
<template
v-if="options?.headerRenderer"
#header="{ close, titleId, titleClass }"
>
<component
:is="options?.headerRenderer({ close, titleId, titleClass })"
/>
</template>
<!-- default -->
<component
v-bind="options?.props"
:is="options.contentRenderer({ options, index })"
@close="args => handleClose(options, index, args)"
/>
<!-- footer -->
<template v-if="!options?.hideFooter" #footer>
<template v-if="options?.footerRenderer">
<component :is="options?.footerRenderer({ options, index })" />
</template>
<span v-else>
<el-button
v-for="(btn, key) in footerButtons(options)"
:key="key"
v-bind="btn"
@click="
btn.btnClick({
dialog: { options, index },
button: { btn, index: key }
})
"
>
{{ btn?.label }}
</el-button>
</span>
</template>
</el-dialog>
</template>

View File

@ -0,0 +1,194 @@
import type { CSSProperties, VNode, Component } from "vue";
type DoneFn = (cancel?: boolean) => void;
type EventType = "open" | "close" | "openAutoFocus" | "closeAutoFocus";
type ArgsType = {
/** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或者空白页 */
command: "cancel" | "sure" | "close";
};
/** https://element-plus.org/zh-CN/component/dialog.html#attributes */
type DialogProps = {
/** `Dialog` 的显示与隐藏 */
visible?: boolean;
/** `Dialog` 的标题 */
title?: string;
/** `Dialog` 的宽度,默认 `50%` */
width?: string | number;
/** 是否为全屏 `Dialog`,默认 `false` */
fullscreen?: boolean;
/** `Dialog CSS` 中的 `margin-top` 值,默认 `15vh` */
top?: string;
/** 是否需要遮罩层,默认 `true` */
modal?: boolean;
/** `Dialog` 自身是否插入至 `body` 元素上。嵌套的 `Dialog` 必须指定该属性并赋值为 `true`,默认 `false` */
appendToBody?: boolean;
/** 是否在 `Dialog` 出现时将 `body` 滚动锁定,默认 `true` */
lockScroll?: boolean;
/** `Dialog` 的自定义类名 */
class?: string;
/** `Dialog` 的自定义样式 */
style?: CSSProperties;
/** `Dialog` 打开的延时时间,单位毫秒,默认 `0` */
openDelay?: number;
/** `Dialog` 关闭的延时时间,单位毫秒,默认 `0` */
closeDelay?: number;
/** 是否可以通过点击 `modal` 关闭 `Dialog`,默认 `true` */
closeOnClickModal?: boolean;
/** 是否可以通过按下 `ESC` 关闭 `Dialog`,默认 `true` */
closeOnPressEscape?: boolean;
/** 是否显示关闭按钮,默认 `true` */
showClose?: boolean;
/** 关闭前的回调,会暂停 `Dialog` 的关闭. 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
beforeClose?: (done: DoneFn) => void;
/** 为 `Dialog` 启用可拖拽功能,默认 `false` */
draggable?: boolean;
/** 是否让 `Dialog` 的 `header` 和 `footer` 部分居中排列,默认 `false` */
center?: boolean;
/** 是否水平垂直对齐对话框,默认 `false` */
alignCenter?: boolean;
/** 当关闭 `Dialog` 时,销毁其中的元素,默认 `false` */
destroyOnClose?: boolean;
};
type BtnClickDialog = {
options?: DialogOptions;
index?: number;
};
type BtnClickButton = {
btn?: ButtonProps;
index?: number;
};
/** https://element-plus.org/zh-CN/component/button.html#button-attributes */
type ButtonProps = {
/** 按钮文字 */
label: string;
/** 按钮尺寸 */
size?: "large" | "default" | "small";
/** 按钮类型 */
type?: "primary" | "success" | "warning" | "danger" | "info";
/** 是否为朴素按钮,默认 `false` */
plain?: boolean;
/** 是否为文字按钮,默认 `false` */
text?: boolean;
/** 是否显示文字按钮背景颜色,默认 `false` */
bg?: boolean;
/** 是否为链接按钮,默认 `false` */
link?: boolean;
/** 是否为圆角按钮,默认 `false` */
round?: boolean;
/** 是否为圆形按钮,默认 `false` */
circle?: boolean;
/** 是否为加载中状态,默认 `false` */
loading?: boolean;
/** 自定义加载中状态图标组件 */
loadingIcon?: string | Component;
/** 按钮是否为禁用状态,默认 `false` */
disabled?: boolean;
/** 图标组件 */
icon?: string | Component;
/** 是否开启原生 `autofocus` 属性,默认 `false` */
autofocus?: boolean;
/** 原生 `type` 属性,默认 `button` */
nativeType?: "button" | "submit" | "reset";
/** 自动在两个中文字符之间插入空格 */
autoInsertSpace?: boolean;
/** 自定义按钮颜色, 并自动计算 `hover` 和 `active` 触发后的颜色 */
color?: string;
/** `dark` 模式, 意味着自动设置 `color` 为 `dark` 模式的颜色,默认 `false` */
dark?: boolean;
/** 自定义元素标签 */
tag?: string | Component;
/** 点击按钮后触发的回调 */
btnClick?: ({
dialog,
button
}: {
/** 当前 `Dialog` 信息 */
dialog: BtnClickDialog;
/** 当前 `button` 信息 */
button: BtnClickButton;
}) => void;
};
interface DialogOptions extends DialogProps {
/** 内容区组件的 `props`,可通过 `defineProps` 接收 */
props?: any;
/** 是否隐藏 `Dialog` 按钮操作区的内容 */
hideFooter?: boolean;
/**
* @description
* @see {@link https://element-plus.org/zh-CN/component/dialog.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A4%B4%E9%83%A8}
*/
headerRenderer?: ({
close,
titleId,
titleClass
}: {
close: Function;
titleId: string;
titleClass: string;
}) => VNode;
/** 自定义内容渲染器 */
contentRenderer?: ({
options,
index
}: {
options: DialogOptions;
index: number;
}) => VNode;
/** 自定义按钮操作区的内容渲染器,会覆盖`footerButtons`以及默认的 `取消` 和 `确定` 按钮 */
footerRenderer?: ({
options,
index
}: {
options: DialogOptions;
index: number;
}) => VNode;
/** 自定义底部按钮操作 */
footerButtons?: Array<ButtonProps>;
/** `Dialog` 打开后的回调 */
open?: ({
options,
index
}: {
options: DialogOptions;
index: number;
}) => void;
/** `Dialog` 关闭后的回调(只有点击右上角关闭按钮或者空白页关闭页面时才会触发) */
close?: ({
options,
index
}: {
options: DialogOptions;
index: number;
}) => void;
/** `Dialog` 关闭后的回调。 `args` 返回的 `command` 值解析:`cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或者空白页 */
closeCallBack?: ({
options,
index,
args
}: {
options: DialogOptions;
index: number;
args: any;
}) => void;
/** 输入焦点聚焦在 `Dialog` 内容时的回调 */
openAutoFocus?: ({
options,
index
}: {
options: DialogOptions;
index: number;
}) => void;
/** 输入焦点从 `Dialog` 内容失焦时的回调 */
closeAutoFocus?: ({
options,
index
}: {
options: DialogOptions;
index: number;
}) => void;
}
export type { EventType, ArgsType, DialogProps, ButtonProps, DialogOptions };

View File

@ -123,9 +123,9 @@ onUnmounted(() => {
.flip-clock em { .flip-clock em {
display: inline-block; display: inline-block;
line-height: 102px;
font-size: 66px; font-size: 66px;
font-style: normal; font-style: normal;
line-height: 102px;
vertical-align: top; vertical-align: top;
} }
</style> </style>

View File

@ -132,10 +132,10 @@ onMounted(() => {
</template> </template>
<style scoped> <style scoped>
@import "./assets/iconfont/iconfont.css"; @import url("./assets/iconfont/iconfont.css");
.control-container { .control-container {
background: hsla(0, 0%, 100%, 0.8); background: hsl(0deg 0% 100% / 80%);
box-shadow: 0 1px 4px rgb(0 0 0 / 20%); box-shadow: 0 1px 4px rgb(0 0 0 / 20%);
} }

File diff suppressed because one or more lines are too long

View File

@ -200,8 +200,8 @@ watch(
.icon-item { .icon-item {
&:hover { &:hover {
border-color: var(--el-color-primary);
color: var(--el-color-primary); color: var(--el-color-primary);
border-color: var(--el-color-primary);
transition: all 0.4s; transition: all 0.4s;
transform: scaleX(1.05); transform: scaleX(1.05);
} }
@ -224,15 +224,15 @@ watch(
} }
:deep(.el-tabs__item) { :deep(.el-tabs__item) {
height: 30px;
font-size: 12px; font-size: 12px;
font-weight: normal; font-weight: normal;
height: 30px;
line-height: 30px; line-height: 30px;
} }
:deep(.el-tabs__header), :deep(.el-tabs__header),
:deep(.el-tabs__nav-wrap) { :deep(.el-tabs__nav-wrap) {
margin: 0;
position: static; position: static;
margin: 0;
} }
</style> </style>

View File

@ -1,6 +1,7 @@
.qrcode { .qrcode {
&--disabled { &--disabled {
background: rgba(255, 255, 255, 0.95); background: rgb(255 255 255 / 95%);
& > div { & > div {
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
} }

View File

@ -8,42 +8,48 @@ $--element-tree-line-width: 1px !default;
} }
.element-tree-node-label-wrapper { .element-tree-node-label-wrapper {
flex: 1;
display: flex; display: flex;
flex: 1;
align-items: center; align-items: center;
} }
.element-tree-node-label { .element-tree-node-label {
font-size: 12px; font-size: 12px;
} }
.element-tree-node-line-ver { .element-tree-node-line-ver {
display: block;
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
display: block;
height: 100%; height: 100%;
border-left: $--element-tree-line-width $--element-tree-line-style border-left: $--element-tree-line-width $--element-tree-line-style
$--element-tree-line-color; $--element-tree-line-color;
&.last-node-line { &.last-node-line {
border-left: $--element-tree-line-width $--element-tree-line-style border-left: $--element-tree-line-width $--element-tree-line-style
transparent; transparent;
} }
&.last-node-isLeaf-line { &.last-node-isLeaf-line {
height: 50%; height: 50%;
} }
} }
.element-tree-node-line-hor { .element-tree-node-line-hor {
display: block;
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 0; left: 0;
display: block;
height: 0; height: 0;
border-bottom: $--element-tree-line-width $--element-tree-line-style border-bottom: $--element-tree-line-width $--element-tree-line-style
$--element-tree-line-color; $--element-tree-line-color;
} }
.element-tree-node-label-line { .element-tree-node-label-line {
flex: 1; flex: 1;
border-top: $--element-tree-line-width $--element-tree-line-style
$--element-tree-line-color;
align-self: center; align-self: center;
margin: 0 10px; margin: 0 10px;
border-top: $--element-tree-line-width $--element-tree-line-style
$--element-tree-line-color;
} }

View File

@ -49,4 +49,7 @@ export const getServerConfig = async (app: App): Promise<undefined> => {
}); });
}; };
export { getConfig, setConfig }; /** 本地响应式存储的命名空间 */
const responsiveStorageNameSpace = () => getConfig().ResponsiveStorageNameSpace;
export { getConfig, setConfig, responsiveStorageNameSpace };

View File

@ -130,16 +130,16 @@ const transitionMain = defineComponent({
<style scoped> <style scoped>
.app-main { .app-main {
position: relative;
width: 100%; width: 100%;
height: 100vh; height: 100vh;
position: relative;
overflow-x: hidden; overflow-x: hidden;
} }
.app-main-nofixed-header { .app-main-nofixed-header {
position: relative;
width: 100%; width: 100%;
min-height: 100vh; min-height: 100vh;
position: relative;
} }
.main-content { .main-content {

View File

@ -121,28 +121,28 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
overflow: hidden; overflow: hidden;
.hamburger-container { .hamburger-container {
line-height: 48px;
height: 100%;
float: left; float: left;
height: 100%;
line-height: 48px;
cursor: pointer; cursor: pointer;
} }
.vertical-header-right { .vertical-header-right {
display: flex; display: flex;
align-items: center;
justify-content: flex-end;
min-width: 280px; min-width: 280px;
height: 48px; height: 48px;
align-items: center;
color: #000000d9; color: #000000d9;
justify-content: flex-end;
.el-dropdown-link { .el-dropdown-link {
height: 48px;
padding: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
cursor: pointer; height: 48px;
padding: 10px;
color: #000000d9; color: #000000d9;
cursor: pointer;
p { p {
font-size: 14px; font-size: 14px;
@ -182,9 +182,9 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
max-width: 120px; max-width: 120px;
::v-deep(.el-dropdown-menu__item) { ::v-deep(.el-dropdown-menu__item) {
min-width: 100%;
display: inline-flex; display: inline-flex;
flex-wrap: wrap; flex-wrap: wrap;
min-width: 100%;
} }
} }
</style> </style>

View File

@ -46,8 +46,8 @@ notices.value.map(v => (noticesNum.value += v.list.length));
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 48px;
width: 60px; width: 60px;
height: 48px;
cursor: pointer; cursor: pointer;
.header-notice-icon { .header-notice-icon {
@ -59,7 +59,7 @@ notices.value.map(v => (noticesNum.value += v.list.length));
width: 330px; width: 330px;
.noticeList-container { .noticeList-container {
padding: 15px 24px 0 24px; padding: 15px 24px 0;
} }
:deep(.el-tabs__header) { :deep(.el-tabs__header) {
@ -71,7 +71,7 @@ notices.value.map(v => (noticesNum.value += v.list.length));
} }
:deep(.el-tabs__nav-wrap) { :deep(.el-tabs__nav-wrap) {
padding: 0 36px 0 36px; padding: 0 36px;
} }
} }
</style> </style>

View File

@ -118,6 +118,7 @@ function hoverDescription(event, description) {
align-items: flex-start; align-items: flex-start;
justify-content: space-between; justify-content: space-between;
padding: 12px 0; padding: 12px 0;
// border-bottom: 1px solid #f0f0f0; // border-bottom: 1px solid #f0f0f0;
.notice-container-avatar { .notice-container-avatar {
@ -127,15 +128,15 @@ function hoverDescription(event, description) {
.notice-container-text { .notice-container-text {
display: flex; display: flex;
flex: 1;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
flex: 1;
.notice-text-title { .notice-text-title {
display: flex; display: flex;
margin-bottom: 8px; margin-bottom: 8px;
font-weight: 400;
font-size: 14px; font-size: 14px;
font-weight: 400;
line-height: 1.5715; line-height: 1.5715;
cursor: pointer; cursor: pointer;
@ -143,8 +144,8 @@ function hoverDescription(event, description) {
flex: 1; flex: 1;
width: 200px; width: 200px;
overflow: hidden; overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap;
} }
.notice-title-extra { .notice-title-extra {
@ -162,8 +163,8 @@ function hoverDescription(event, description) {
.notice-text-description { .notice-text-description {
display: -webkit-box; display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }

View File

@ -60,9 +60,9 @@ emitter.on("openPanel", () => {
<style> <style>
.showright-panel { .showright-panel {
overflow: hidden;
position: relative; position: relative;
width: calc(100% - 15px); width: calc(100% - 15px);
overflow: hidden;
} }
</style> </style>
@ -71,23 +71,23 @@ emitter.on("openPanel", () => {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
z-index: -1;
background: rgb(0 0 0 / 20%);
opacity: 0; opacity: 0;
transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1); transition: opacity 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
background: rgba(0, 0, 0, 0.2);
z-index: -1;
} }
.right-panel { .right-panel {
width: 100%;
max-width: 315px;
height: 100vh;
position: fixed; position: fixed;
top: 0; top: 0;
right: 0; right: 0;
box-shadow: 0 0 15px 0 rgba(0, 0, 0, 0.05); z-index: 40000;
width: 100%;
max-width: 315px;
height: 100vh;
box-shadow: 0 0 15px 0 rgb(0 0 0 / 5%);
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1); transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
transform: translate(100%); transform: translate(100%);
z-index: 40000;
} }
.show { .show {
@ -95,9 +95,9 @@ emitter.on("openPanel", () => {
.right-panel-background { .right-panel-background {
z-index: 20000; z-index: 20000;
opacity: 1;
width: 100%; width: 100%;
height: 100%; height: 100%;
opacity: 1;
} }
.right-panel { .right-panel {
@ -106,20 +106,20 @@ emitter.on("openPanel", () => {
} }
.handle-button { .handle-button {
position: absolute;
top: 45%;
left: -48px;
z-index: 0;
width: 48px; width: 48px;
height: 48px; height: 48px;
position: absolute;
left: -48px;
text-align: center;
font-size: 24px; font-size: 24px;
border-radius: 6px 0 0 6px !important; line-height: 48px;
z-index: 0; color: #fff;
text-align: center;
pointer-events: auto; pointer-events: auto;
cursor: pointer; cursor: pointer;
color: #fff; background: rgb(24 144 255);
line-height: 48px; border-radius: 6px 0 0 6px !important;
top: 45%;
background: rgb(24, 144, 255);
i { i {
font-size: 24px; font-size: 24px;
@ -128,24 +128,24 @@ emitter.on("openPanel", () => {
} }
.right-panel-items { .right-panel-items {
margin-top: 60px;
height: calc(100vh - 60px); height: calc(100vh - 60px);
margin-top: 60px;
overflow-y: auto; overflow-y: auto;
} }
.project-configuration { .project-configuration {
position: fixed;
top: 15px;
display: flex; display: flex;
align-items: center;
justify-content: space-between;
width: 100%; width: 100%;
height: 30px; height: 30px;
position: fixed;
justify-content: space-between;
align-items: center;
top: 15px;
margin-left: 10px; margin-left: 10px;
} }
:deep(.el-divider--horizontal) { :deep(.el-divider--horizontal) {
width: 90%; width: 90%;
margin: 20px auto 0 auto; margin: 20px auto 0;
} }
</style> </style>

View File

@ -84,11 +84,11 @@ function handleTo() {
display: flex; display: flex;
align-items: center; align-items: center;
height: 56px; height: 56px;
margin-top: 8px;
padding: 14px; padding: 14px;
border-radius: 4px; margin-top: 8px;
cursor: pointer; cursor: pointer;
border: 0.1px solid #ccc; border: 0.1px solid #ccc;
border-radius: 4px;
transition: all 0.3s; transition: all 0.3s;
&-title { &-title {

View File

@ -418,35 +418,35 @@ onBeforeMount(() => {
li { li {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
justify-content: space-between;
margin: 25px; margin: 25px;
} }
} }
.pure-datatheme { .pure-datatheme {
display: block;
width: 100%; width: 100%;
height: 50px; height: 50px;
text-align: center;
display: block;
padding-top: 25px; padding-top: 25px;
text-align: center;
} }
.pure-theme { .pure-theme {
margin-top: 25px;
width: 100%;
height: 50px;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-around; justify-content: space-around;
width: 100%;
height: 50px;
margin-top: 25px;
li { li {
position: relative;
width: 18%; width: 18%;
height: 45px; height: 45px;
background: #f0f2f5;
position: relative;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
background: #f0f2f5;
border-radius: 4px; border-radius: 4px;
box-shadow: 0 1px 2.5px 0 rgb(0 0 0 / 18%); box-shadow: 0 1px 2.5px 0 rgb(0 0 0 / 18%);
@ -459,13 +459,13 @@ onBeforeMount(() => {
} }
&:nth-child(2) { &:nth-child(2) {
width: 70%; position: absolute;
height: 30%;
top: 0; top: 0;
right: 0; right: 0;
width: 70%;
height: 30%;
background: #fff; background: #fff;
box-shadow: 0 0 1px #888; box-shadow: 0 0 1px #888;
position: absolute;
} }
} }
} }
@ -491,13 +491,13 @@ onBeforeMount(() => {
} }
&:nth-child(2) { &:nth-child(2) {
width: 30%; position: absolute;
height: 70%;
bottom: 0; bottom: 0;
left: 0; left: 0;
width: 30%;
height: 70%;
background: #fff; background: #fff;
box-shadow: 0 0 1px #888; box-shadow: 0 0 1px #888;
position: absolute;
} }
} }
} }
@ -505,11 +505,11 @@ onBeforeMount(() => {
} }
.theme-color { .theme-color {
display: flex;
justify-content: center;
width: 100%; width: 100%;
height: 40px; height: 40px;
margin-top: 20px; margin-top: 20px;
display: flex;
justify-content: center;
li { li {
float: left; float: left;
@ -519,8 +519,8 @@ onBeforeMount(() => {
margin-right: 8px; margin-right: 8px;
font-weight: 700; font-weight: 700;
text-align: center; text-align: center;
border-radius: 2px;
cursor: pointer; cursor: pointer;
border-radius: 2px;
&:nth-child(2) { &:nth-child(2) {
border: 1px solid #ddd; border: 1px solid #ddd;

View File

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { isEqual } from "@pureadmin/utils"; import { isEqual } from "@pureadmin/utils";
import { routerArrays } from "@/layout/types";
import { transformI18n } from "@/plugins/i18n"; import { transformI18n } from "@/plugins/i18n";
import { ref, watch, onMounted, toRaw } from "vue"; import { ref, watch, onMounted, toRaw } from "vue";
import { getParentPaths, findRouteByPath } from "@/router/utils"; import { getParentPaths, findRouteByPath } from "@/router/utils";
@ -10,14 +11,9 @@ const route = useRoute();
const levelList = ref([]); const levelList = ref([]);
const router = useRouter(); const router = useRouter();
const routes: any = router.options.routes; const routes: any = router.options.routes;
const { VITE_HIDE_HOME } = import.meta.env;
const multiTags: any = useMultiTagsStoreHook().multiTags; const multiTags: any = useMultiTagsStoreHook().multiTags;
const isDashboard = (route: RouteLocationMatched): boolean | string => {
const name = route && (route.name as string);
if (!name) return false;
return name.trim().toLocaleLowerCase() === "Welcome".toLocaleLowerCase();
};
const getBreadcrumb = (): void => { const getBreadcrumb = (): void => {
// //
let currentRoute; let currentRoute;
@ -35,12 +31,17 @@ const getBreadcrumb = (): void => {
} }
}); });
} else { } else {
currentRoute = findRouteByPath(router.currentRoute.value.path, multiTags); currentRoute = findRouteByPath(router.currentRoute.value.path, routes);
} }
// //
const parentRoutes = getParentPaths(router.currentRoute.value.path, routes); const parentRoutes = getParentPaths(
router.currentRoute.value.name as string,
routes,
"name"
);
// //
let matched = []; let matched = [];
// //
parentRoutes.forEach(path => { parentRoutes.forEach(path => {
if (path !== "/") matched.push(findRouteByPath(path, routes)); if (path !== "/") matched.push(findRouteByPath(path, routes));
@ -48,15 +49,7 @@ const getBreadcrumb = (): void => {
if (currentRoute?.path !== "/welcome") matched.push(currentRoute); if (currentRoute?.path !== "/welcome") matched.push(currentRoute);
if (!isDashboard(matched[0])) { if (VITE_HIDE_HOME === "false") matched = routerArrays.concat(matched);
matched = [
{
path: "/welcome",
parentPath: "/",
meta: { title: "menus.hshome" }
} as unknown as RouteLocationMatched
].concat(matched);
}
matched.forEach((item, index) => { matched.forEach((item, index) => {
if (currentRoute?.query || currentRoute?.params) return; if (currentRoute?.query || currentRoute?.params) return;

View File

@ -19,7 +19,7 @@ const {
title, title,
routers, routers,
logout, logout,
backHome, backTopMenu,
onPanel, onPanel,
menuSelect, menuSelect,
username, username,
@ -45,7 +45,7 @@ watch(
v-loading="usePermissionStoreHook().wholeMenus.length === 0" v-loading="usePermissionStoreHook().wholeMenus.length === 0"
class="horizontal-header" class="horizontal-header"
> >
<div class="horizontal-header-left" @click="backHome"> <div class="horizontal-header-left" @click="backTopMenu">
<img src="/logo.svg" alt="logo" /> <img src="/logo.svg" alt="logo" />
<span>{{ title }}</span> <span>{{ title }}</span>
</div> </div>
@ -156,9 +156,9 @@ watch(
max-width: 120px; max-width: 120px;
::v-deep(.el-dropdown-menu__item) { ::v-deep(.el-dropdown-menu__item) {
min-width: 100%;
display: inline-flex; display: inline-flex;
flex-wrap: wrap; flex-wrap: wrap;
min-width: 100%;
} }
} }
</style> </style>

View File

@ -1,4 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { getTopMenu } from "@/router/utils";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
const props = defineProps({ const props = defineProps({
@ -6,6 +7,7 @@ const props = defineProps({
}); });
const { title } = useNav(); const { title } = useNav();
const topPath = getTopMenu().path;
</script> </script>
<template> <template>
@ -16,7 +18,7 @@ const { title } = useNav();
key="props.collapse" key="props.collapse"
:title="title" :title="title"
class="sidebar-logo-link" class="sidebar-logo-link"
to="/" :to="topPath"
> >
<img src="/logo.svg" alt="logo" /> <img src="/logo.svg" alt="logo" />
<span class="sidebar-title">{{ title }}</span> <span class="sidebar-title">{{ title }}</span>
@ -26,7 +28,7 @@ const { title } = useNav();
key="expand" key="expand"
:title="title" :title="title"
class="sidebar-logo-link" class="sidebar-logo-link"
to="/" :to="topPath"
> >
<img src="/logo.svg" alt="logo" /> <img src="/logo.svg" alt="logo" />
<span class="sidebar-title">{{ title }}</span> <span class="sidebar-title">{{ title }}</span>
@ -37,33 +39,33 @@ const { title } = useNav();
<style lang="scss" scoped> <style lang="scss" scoped>
.sidebar-logo-container { .sidebar-logo-container {
position: relative;
width: 100%; width: 100%;
height: 48px; height: 48px;
overflow: hidden; overflow: hidden;
position: relative;
.sidebar-logo-link { .sidebar-logo-link {
height: 100%;
display: flex; display: flex;
flex-wrap: nowrap; flex-wrap: nowrap;
align-items: center; align-items: center;
height: 100%;
img { img {
height: 32px;
display: inline-block; display: inline-block;
height: 32px;
} }
.sidebar-title { .sidebar-title {
height: 32px;
line-height: 32px;
margin: 2px 0 0 12px;
color: $subMenuActiveText;
display: inline-block; display: inline-block;
height: 32px;
margin: 2px 0 0 12px;
overflow: hidden; overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
line-height: 32px;
color: $subMenuActiveText;
text-overflow: ellipsis;
white-space: nowrap;
} }
} }
} }

View File

@ -188,9 +188,9 @@ watch(
max-width: 120px; max-width: 120px;
::v-deep(.el-dropdown-menu__item) { ::v-deep(.el-dropdown-menu__item) {
min-width: 100%;
display: inline-flex; display: inline-flex;
flex-wrap: wrap; flex-wrap: wrap;
min-width: 100%;
} }
} }
</style> </style>

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import path from "path"; import path from "path";
import { getConfig } from "@/config"; import { getConfig } from "@/config";
import { menuType } from "../../types";
import extraIcon from "./extraIcon.vue"; import extraIcon from "./extraIcon.vue";
import { childrenType } from "../../types";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { transformI18n } from "@/plugins/i18n"; import { transformI18n } from "@/plugins/i18n";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
@ -17,7 +17,7 @@ const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
const props = defineProps({ const props = defineProps({
item: { item: {
type: Object as PropType<childrenType> type: Object as PropType<menuType>
}, },
isNest: { isNest: {
type: Boolean, type: Boolean,
@ -112,7 +112,7 @@ const expandCloseIcon = computed(() => {
}; };
}); });
const onlyOneChild: childrenType = ref(null); const onlyOneChild: menuType = ref(null);
// showTooltip // showTooltip
const hoverMenuMap = new WeakMap(); const hoverMenuMap = new WeakMap();
// dom // dom
@ -149,10 +149,7 @@ function overflowSlice(text, item?: any) {
return newText; return newText;
} }
function hasOneShowingChild( function hasOneShowingChild(children: menuType[] = [], parent: menuType) {
children: childrenType[] = [],
parent: childrenType
) {
const showingChildren = children.filter((item: any) => { const showingChildren = children.filter((item: any) => {
onlyOneChild.value = item; onlyOneChild.value = item;
return true; return true;

View File

@ -6,14 +6,16 @@ import SidebarItem from "./sidebarItem.vue";
import leftCollapse from "./leftCollapse.vue"; import leftCollapse from "./leftCollapse.vue";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { storageLocal } from "@pureadmin/utils"; import { storageLocal } from "@pureadmin/utils";
import { responsiveStorageNameSpace } from "@/config";
import { ref, computed, watch, onBeforeMount } from "vue"; import { ref, computed, watch, onBeforeMount } from "vue";
import { findRouteByPath, getParentPaths } from "@/router/utils"; import { findRouteByPath, getParentPaths } from "@/router/utils";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
const route = useRoute(); const route = useRoute();
const showLogo = ref( const showLogo = ref(
storageLocal().getItem<StorageConfigs>("responsive-configure")?.showLogo ?? storageLocal().getItem<StorageConfigs>(
true `${responsiveStorageNameSpace()}configure`
)?.showLogo ?? true
); );
const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } = const { routers, device, pureApp, isCollapse, menuSelect, toggleSideBar } =
@ -32,6 +34,7 @@ const loading = computed(() =>
); );
function getSubMenuData(path: string) { function getSubMenuData(path: string) {
subMenuData.value = [];
// path // path
const parentPathArr = getParentPaths( const parentPathArr = getParentPaths(
path, path,

View File

@ -1,4 +1,4 @@
@keyframes scheduleInWidth { @keyframes schedule-in-width {
from { from {
width: 0; width: 0;
} }
@ -8,7 +8,7 @@
} }
} }
@keyframes scheduleOutWidth { @keyframes schedule-out-width {
from { from {
width: 100%; width: 100%;
} }
@ -39,41 +39,41 @@
} }
.tags-view { .tags-view {
width: 100%; position: relative;
font-size: 14px;
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%;
font-size: 14px;
color: var(--el-text-color-primary); color: var(--el-text-color-primary);
background: #fff; background: #fff;
position: relative;
box-shadow: 0 0 1px #888; box-shadow: 0 0 1px #888;
.scroll-item { .scroll-item {
border-radius: 3px 3px 0 0;
padding: 0 6px;
box-shadow: 0 0 1px #888;
position: relative; position: relative;
margin-right: 4px;
height: 28px;
display: inline-block; display: inline-block;
height: 28px;
padding: 0 6px;
margin-right: 4px;
line-height: 28px; line-height: 28px;
transition: all 0.4s;
cursor: pointer; cursor: pointer;
border-radius: 3px 3px 0 0;
box-shadow: 0 0 1px #888;
transition: all 0.4s;
.el-icon-close { .el-icon-close {
position: absolute;
top: 50%;
font-size: 10px; font-size: 10px;
color: var(--el-color-primary); color: var(--el-color-primary);
cursor: pointer; cursor: pointer;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
transition: font-size 0.2s; transition: font-size 0.2s;
transform: translate(-50%, -50%);
&:hover { &:hover {
border-radius: 50%; font-size: 13px;
color: #fff; color: #fff;
background: #b4bccc; background: #b4bccc;
font-size: 13px; border-radius: 50%;
} }
} }
@ -91,24 +91,24 @@
} }
a { a {
text-decoration: none;
color: var(--el-text-color-primary);
padding: 0 4px; padding: 0 4px;
color: var(--el-text-color-primary);
text-decoration: none;
} }
.scroll-container { .scroll-container {
flex: 1;
overflow: hidden;
padding: 5px 0;
white-space: nowrap;
position: relative; position: relative;
flex: 1;
padding: 5px 0;
overflow: hidden;
white-space: nowrap;
.tab { .tab {
position: relative; position: relative;
float: left; float: left;
list-style: none;
overflow: visible; overflow: visible;
white-space: nowrap; white-space: nowrap;
list-style: none;
transition: transform 0.5s ease-in-out; transition: transform 0.5s ease-in-out;
.scroll-item { .scroll-item {
@ -123,29 +123,28 @@
/* 右键菜单 */ /* 右键菜单 */
.contextmenu { .contextmenu {
margin: 0;
background: #fff;
position: absolute; position: absolute;
list-style-type: none;
padding: 5px 0; padding: 5px 0;
border-radius: 4px; margin: 0;
color: var(--el-text-color-primary);
font-weight: normal;
font-size: 13px; font-size: 13px;
font-weight: normal;
color: var(--el-text-color-primary);
white-space: nowrap; white-space: nowrap;
list-style-type: none;
background: #fff;
border-radius: 4px;
outline: 0; outline: 0;
box-shadow: 0 2px 8px rgb(0 0 0 / 15%); box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
li { li {
width: 100%;
margin: 0;
padding: 7px 12px;
cursor: pointer;
display: flex; display: flex;
align-items: center; align-items: center;
width: 100%;
padding: 7px 12px;
margin: 0;
cursor: pointer;
&:hover { &:hover {
// background: var(--el-color-primary-light-9);
color: var(--el-color-primary); color: var(--el-color-primary);
} }
@ -159,11 +158,11 @@
.el-dropdown-menu { .el-dropdown-menu {
li { li {
display: flex;
align-items: center;
width: 100%; width: 100%;
margin: 0; margin: 0;
cursor: pointer; cursor: pointer;
display: flex;
align-items: center;
svg { svg {
display: block; display: block;
@ -184,6 +183,7 @@
:deep(.el-dropdown-menu__item--divided) { :deep(.el-dropdown-menu__item--divided) {
margin: 1px 0; margin: 1px 0;
} }
.el-dropdown-menu__item--divided::before { .el-dropdown-menu__item--divided::before {
margin: 0; margin: 0;
} }
@ -193,7 +193,6 @@
} }
.scroll-item.is-active { .scroll-item.is-active {
// background-color: var(--el-color-primary-light-9);
position: relative; position: relative;
color: #fff; color: #fff;
@ -213,16 +212,16 @@
.arrow-left, .arrow-left,
.arrow-right, .arrow-right,
.arrow-down { .arrow-down {
position: relative;
width: 40px; width: 40px;
height: 38px; height: 38px;
color: var(--el-text-color-primary); color: var(--el-text-color-primary);
position: relative;
svg { svg {
width: 20px;
height: 20px;
position: absolute; position: absolute;
left: 50%; left: 50%;
width: 20px;
height: 20px;
transform: translate(-50%, 50%); transform: translate(-50%, 50%);
} }
} }
@ -236,8 +235,8 @@
} }
.arrow-right { .arrow-right {
box-shadow: -5px 0 5px -6px #ccc;
border-right: 0.5px solid #ccc; border-right: 0.5px solid #ccc;
box-shadow: -5px 0 5px -6px #ccc;
&:hover { &:hover {
cursor: e-resize; cursor: e-resize;
@ -255,8 +254,8 @@
/* 卡片模式下鼠标移出隐藏蓝色边框 */ /* 卡片模式下鼠标移出隐藏蓝色边框 */
.card-out { .card-out {
border: none;
color: #666; color: #666;
border: none;
a { a {
color: #666; color: #666;
@ -265,32 +264,32 @@
/* 灵动模式 */ /* 灵动模式 */
.schedule-active { .schedule-active {
position: absolute;
bottom: 0;
left: 0;
width: 100%; width: 100%;
height: 2px; height: 2px;
position: absolute;
left: 0;
bottom: 0;
background: var(--el-color-primary); background: var(--el-color-primary);
} }
/* 灵动模式下鼠标移入显示蓝色进度条 */ /* 灵动模式下鼠标移入显示蓝色进度条 */
.schedule-in { .schedule-in {
position: absolute;
bottom: 0;
left: 0;
width: 100%; width: 100%;
height: 2px; height: 2px;
position: absolute;
left: 0;
bottom: 0;
background: var(--el-color-primary); background: var(--el-color-primary);
animation: scheduleInWidth 200ms ease-in; animation: schedule-in-width 200ms ease-in;
} }
/* 灵动模式下鼠标移出隐藏蓝色进度条 */ /* 灵动模式下鼠标移出隐藏蓝色进度条 */
.schedule-out { .schedule-out {
position: absolute;
bottom: 0;
left: 0;
width: 0; width: 0;
height: 2px; height: 2px;
position: absolute;
left: 0;
bottom: 0;
background: var(--el-color-primary); background: var(--el-color-primary);
animation: scheduleOutWidth 200ms ease-in; animation: schedule-out-width 200ms ease-in;
} }

View File

@ -5,10 +5,10 @@ import { RouteConfigs } from "../../types";
import { useTags } from "../../hooks/useTag"; import { useTags } from "../../hooks/useTag";
import { routerArrays } from "@/layout/types"; import { routerArrays } from "@/layout/types";
import { isEqual, isAllEmpty } from "@pureadmin/utils"; import { isEqual, isAllEmpty } from "@pureadmin/utils";
import { handleAliveRoute, getTopMenu } from "@/router/utils";
import { useSettingStoreHook } from "@/store/modules/settings"; import { useSettingStoreHook } from "@/store/modules/settings";
import { ref, watch, unref, nextTick, onBeforeMount } from "vue";
import { handleAliveRoute, delAliveRoutes } from "@/router/utils";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { ref, watch, unref, toRaw, nextTick, onBeforeMount } from "vue";
import { useResizeObserver, useDebounceFn, useFullscreen } from "@vueuse/core"; import { useResizeObserver, useDebounceFn, useFullscreen } from "@vueuse/core";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill"; import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
@ -50,6 +50,8 @@ const tabDom = ref();
const containerDom = ref(); const containerDom = ref();
const scrollbarDom = ref(); const scrollbarDom = ref();
const isShowArrow = ref(false); const isShowArrow = ref(false);
const topPath = getTopMenu().path;
const { VITE_HIDE_HOME } = import.meta.env;
const { isFullscreen, toggle } = useFullscreen(); const { isFullscreen, toggle } = useFullscreen();
const dynamicTagView = () => { const dynamicTagView = () => {
@ -165,13 +167,11 @@ function onFresh() {
const { fullPath, query } = unref(route); const { fullPath, query } = unref(route);
router.replace({ router.replace({
path: "/redirect" + fullPath, path: "/redirect" + fullPath,
query: query query
}); });
} }
function deleteDynamicTag(obj: any, current: any, tag?: string) { function deleteDynamicTag(obj: any, current: any, tag?: string) {
//
let delAliveRouteList = [];
const valueIndex: number = multiTags.value.findIndex((item: any) => { const valueIndex: number = multiTags.value.findIndex((item: any) => {
if (item.query) { if (item.query) {
if (item.path === obj.path) { if (item.path === obj.path) {
@ -192,9 +192,12 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
other?: boolean other?: boolean
): void => { ): void => {
if (other) { if (other) {
useMultiTagsStoreHook().handleTags("equal", [routerArrays[0], obj]); useMultiTagsStoreHook().handleTags("equal", [
VITE_HIDE_HOME === "false" ? routerArrays[0] : toRaw(getTopMenu()),
obj
]);
} else { } else {
delAliveRouteList = useMultiTagsStoreHook().handleTags("splice", "", { useMultiTagsStoreHook().handleTags("splice", "", {
startIndex, startIndex,
length length
}) as any; }) as any;
@ -214,10 +217,6 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
} }
const newRoute = useMultiTagsStoreHook().handleTags("slice"); const newRoute = useMultiTagsStoreHook().handleTags("slice");
if (current === route.path) { if (current === route.path) {
//
tag
? delAliveRoutes(delAliveRouteList)
: handleAliveRoute(route.matched, "delete");
// tagtag // tagtag
if (tag === "left") return; if (tag === "left") return;
if (newRoute[0]?.query) { if (newRoute[0]?.query) {
@ -228,8 +227,6 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
router.push({ path: newRoute[0].path }); router.push({ path: newRoute[0].path });
} }
} else { } else {
//
tag ? delAliveRoutes(delAliveRouteList) : delAliveRoutes([obj]);
if (!multiTags.value.length) return; if (!multiTags.value.length) return;
if (multiTags.value.some(item => item.path === route.path)) return; if (multiTags.value.some(item => item.path === route.path)) return;
if (newRoute[0]?.query) { if (newRoute[0]?.query) {
@ -244,6 +241,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
function deleteMenu(item, tag?: string) { function deleteMenu(item, tag?: string) {
deleteDynamicTag(item, item.path, tag); deleteDynamicTag(item, item.path, tag);
handleAliveRoute(route as toRouteType);
} }
function onClickDrop(key, item, selectRoute?: RouteConfigs) { function onClickDrop(key, item, selectRoute?: RouteConfigs) {
@ -290,7 +288,8 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
startIndex: 1, startIndex: 1,
length: multiTags.value.length length: multiTags.value.length
}); });
router.push("/welcome"); router.push(topPath);
handleAliveRoute(route as toRouteType);
break; break;
case 6: case 6:
// //
@ -346,7 +345,7 @@ function disabledMenus(value: boolean) {
}); });
} }
/** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是首页,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */ /** 检查当前右键的菜单两边是否存在别的菜单,如果左侧的菜单是顶级菜单,则不显示关闭左侧标签页,如果右侧没有菜单,则不显示关闭右侧标签页 */
function showMenuModel( function showMenuModel(
currentPath: string, currentPath: string,
query: object = {}, query: object = {},
@ -368,11 +367,11 @@ function showMenuModel(
} }
/** /**
* currentIndex为1时左侧的菜单是首页则不显示关闭左侧标签页 * currentIndex为1时左侧的菜单顶级菜单则不显示关闭左侧标签页
* 如果currentIndex等于routeLength-1右侧没有菜单则不显示关闭右侧标签页 * 如果currentIndex等于routeLength-1右侧没有菜单则不显示关闭右侧标签页
*/ */
if (currentIndex === 1 && routeLength !== 2) { if (currentIndex === 1 && routeLength !== 2) {
// //
tagsViews[2].show = false; tagsViews[2].show = false;
Array.of(1, 3, 4, 5).forEach(v => { Array.of(1, 3, 4, 5).forEach(v => {
tagsViews[v].disabled = false; tagsViews[v].disabled = false;
@ -380,7 +379,7 @@ function showMenuModel(
tagsViews[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[v].show = false; tagsViews[v].show = false;
tagsViews[v].disabled = true; tagsViews[v].disabled = true;
@ -392,8 +391,8 @@ function showMenuModel(
tagsViews[v].disabled = false; tagsViews[v].disabled = false;
}); });
tagsViews[3].disabled = true; tagsViews[3].disabled = true;
} else if (currentIndex === 0 || currentPath === "/redirect/welcome") { } else if (currentIndex === 0 || currentPath === `/redirect${topPath}`) {
// //
disabledMenus(true); disabledMenus(true);
} else { } else {
disabledMenus(false); disabledMenus(false);
@ -402,8 +401,8 @@ function showMenuModel(
function openMenu(tag, e) { function openMenu(tag, e) {
closeMenu(); closeMenu();
if (tag.path === "/welcome") { if (tag.path === topPath) {
// //
showMenus(false); showMenus(false);
tagsViews[0].show = true; tagsViews[0].show = true;
} else if (route.path !== tag.path && route.name !== tag.name) { } else if (route.path !== tag.path && route.name !== tag.name) {
@ -609,5 +608,5 @@ onMounted(() => {
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "./index.scss"; @import url("./index.scss");
</style> </style>

View File

@ -56,15 +56,15 @@ onMounted(() => {
<style lang="scss" scoped> <style lang="scss" scoped>
.frame { .frame {
height: calc(100vh - 88px);
z-index: 998; z-index: 998;
height: calc(100vh - 88px);
.frame-iframe { .frame-iframe {
box-sizing: border-box;
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
border: 0; border: 0;
box-sizing: border-box;
} }
} }

View File

@ -3,6 +3,7 @@ import { getConfig } from "@/config";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { emitter } from "@/utils/mitt"; import { emitter } from "@/utils/mitt";
import { routeMetaType } from "../types"; import { routeMetaType } from "../types";
import { getTopMenu } from "@/router/utils";
import { useGlobal } from "@pureadmin/utils"; import { useGlobal } from "@pureadmin/utils";
import { transformI18n } from "@/plugins/i18n"; import { transformI18n } from "@/plugins/i18n";
import { router, remainingPaths } from "@/router"; import { router, remainingPaths } from "@/router";
@ -85,8 +86,8 @@ export function useNav() {
useUserStoreHook().logOut(); useUserStoreHook().logOut();
} }
function backHome() { function backTopMenu() {
router.push("/welcome"); router.push(getTopMenu().path);
} }
function onPanel() { function onPanel() {
@ -154,7 +155,7 @@ export function useNav() {
logout, logout,
routers, routers,
$storage, $storage,
backHome, backTopMenu,
onPanel, onPanel,
getDivStyle, getDivStyle,
changeTitle, changeTitle,

View File

@ -12,6 +12,7 @@ import { tagsViewsType } from "../types";
import { useEventListener } from "@vueuse/core"; import { useEventListener } from "@vueuse/core";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { transformI18n, $t } from "@/plugins/i18n"; import { transformI18n, $t } from "@/plugins/i18n";
import { responsiveStorageNameSpace } from "@/config";
import { useSettingStoreHook } from "@/store/modules/settings"; import { useSettingStoreHook } from "@/store/modules/settings";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { import {
@ -46,13 +47,16 @@ export function useTags() {
/** 显示模式,默认灵动模式 */ /** 显示模式,默认灵动模式 */
const showModel = ref( const showModel = ref(
storageLocal().getItem<StorageConfigs>("responsive-configure")?.showModel || storageLocal().getItem<StorageConfigs>(
"smart" `${responsiveStorageNameSpace()}configure`
)?.showModel || "smart"
); );
/** 是否隐藏标签页,默认显示 */ /** 是否隐藏标签页,默认显示 */
const showTags = const showTags =
ref( ref(
storageLocal().getItem<StorageConfigs>("responsive-configure").hideTabs storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}configure`
).hideTabs
) ?? ref("false"); ) ?? ref("false");
const multiTags: any = computed(() => { const multiTags: any = computed(() => {
return useMultiTagsStoreHook().multiTags; return useMultiTagsStoreHook().multiTags;
@ -201,10 +205,13 @@ export function useTags() {
onMounted(() => { onMounted(() => {
if (!showModel.value) { if (!showModel.value) {
const configure = storageLocal().getItem<StorageConfigs>( const configure = storageLocal().getItem<StorageConfigs>(
"responsive-configure" `${responsiveStorageNameSpace()}configure`
); );
configure.showModel = "card"; configure.showModel = "card";
storageLocal().setItem("responsive-configure", configure); storageLocal().setItem(
`${responsiveStorageNameSpace()}configure`,
configure
);
} }
}); });

View File

@ -179,20 +179,16 @@ const layoutHeader = defineComponent({
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
@mixin clearfix { .app-wrapper {
position: relative;
width: 100%;
height: 100%;
&::after { &::after {
content: "";
display: table; display: table;
clear: both; clear: both;
content: "";
} }
}
.app-wrapper {
@include clearfix;
position: relative;
height: 100%;
width: 100%;
&.mobile.openSidebar { &.mobile.openSidebar {
position: fixed; position: fixed;
@ -201,13 +197,13 @@ const layoutHeader = defineComponent({
} }
.app-mask { .app-mask {
position: absolute;
top: 0;
z-index: 999;
width: 100%;
height: 100%;
background: #000; background: #000;
opacity: 0.3; opacity: 0.3;
width: 100%;
top: 0;
height: 100%;
position: absolute;
z-index: 999;
} }
.re-screen { .re-screen {

View File

@ -1,6 +1,9 @@
import type { IconifyIcon } from "@iconify/vue"; import type { IconifyIcon } from "@iconify/vue";
const { VITE_HIDE_HOME } = import.meta.env;
export const routerArrays: Array<RouteConfigs> = [ export const routerArrays: Array<RouteConfigs> =
VITE_HIDE_HOME === "false"
? [
{ {
path: "/welcome", path: "/welcome",
parentPath: "/", parentPath: "/",
@ -9,7 +12,8 @@ export const routerArrays: Array<RouteConfigs> = [
icon: "homeFilled" icon: "homeFilled"
} }
} }
]; ]
: [];
export type routeMetaType = { export type routeMetaType = {
title?: string; title?: string;
@ -58,20 +62,23 @@ export interface setType {
hideTabs: boolean; hideTabs: boolean;
} }
export type childrenType = { export type menuType = {
id?: number;
path?: string; path?: string;
noShowingChildren?: boolean; noShowingChildren?: boolean;
children?: childrenType[]; children?: menuType[];
value: unknown; value: unknown;
meta?: { meta?: {
icon?: string; icon?: string;
title?: string; title?: string;
rank?: number;
showParent?: boolean; showParent?: boolean;
extraIcon?: string; extraIcon?: string;
}; };
showTooltip?: boolean; showTooltip?: boolean;
parentId?: number; parentId?: number;
pathList?: number[]; pathList?: number[];
redirect?: string;
}; };
export type themeColorsType = { export type themeColorsType = {

View File

@ -2,6 +2,7 @@
import { App, WritableComputedRef } from "vue"; import { App, WritableComputedRef } from "vue";
import { storageLocal } from "@pureadmin/utils"; import { storageLocal } from "@pureadmin/utils";
import { type I18n, createI18n } from "vue-i18n"; import { type I18n, createI18n } from "vue-i18n";
import { responsiveStorageNameSpace } from "@/config";
// element-plus国际化 // element-plus国际化
import enLocale from "element-plus/lib/locale/lang/en"; import enLocale from "element-plus/lib/locale/lang/en";
@ -63,7 +64,9 @@ export const $t = (key: string) => key;
export const i18n: I18n = createI18n({ export const i18n: I18n = createI18n({
legacy: false, legacy: false,
locale: locale:
storageLocal().getItem<StorageConfigs>("responsive-locale")?.locale ?? "zh", storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}locale`
)?.locale ?? "zh",
fallbackLocale: "en", fallbackLocale: "en",
messages: localesConfigs messages: localesConfigs
}); });

View File

@ -13,6 +13,7 @@ import {
} from "vue-router"; } from "vue-router";
import { import {
ascending, ascending,
getTopMenu,
initRouter, initRouter,
isOneOfArray, isOneOfArray,
getHistoryMode, getHistoryMode,
@ -96,13 +97,14 @@ export function resetRouter() {
/** 路由白名单 */ /** 路由白名单 */
const whiteList = ["/login"]; const whiteList = ["/login"];
const { VITE_HIDE_HOME } = import.meta.env;
router.beforeEach((to: toRouteType, _from, next) => { router.beforeEach((to: toRouteType, _from, next) => {
if (to.meta?.keepAlive) { if (to.meta?.keepAlive) {
const newMatched = to.matched; handleAliveRoute(to, "add");
handleAliveRoute(newMatched, "add");
// 页面整体刷新和点击标签页刷新 // 页面整体刷新和点击标签页刷新
if (_from.name === undefined || _from.name === "Redirect") { if (_from.name === undefined || _from.name === "Redirect") {
handleAliveRoute(newMatched); handleAliveRoute(to);
} }
} }
const userInfo = storageSession().getItem<DataInfo<number>>(sessionKey); const userInfo = storageSession().getItem<DataInfo<number>>(sessionKey);
@ -126,6 +128,10 @@ router.beforeEach((to: toRouteType, _from, next) => {
if (to.meta?.roles && !isOneOfArray(to.meta?.roles, userInfo?.roles)) { if (to.meta?.roles && !isOneOfArray(to.meta?.roles, userInfo?.roles)) {
next({ path: "/error/403" }); next({ path: "/error/403" });
} }
// 开启隐藏首页后在浏览器地址栏手动输入首页welcome路由则跳转到404页面
if (VITE_HIDE_HOME === "true" && to.fullPath === "/welcome") {
next({ path: "/error/404" });
}
if (_from?.name) { if (_from?.name) {
// name为超链接 // name为超链接
if (externalLink) { if (externalLink) {
@ -147,6 +153,7 @@ router.beforeEach((to: toRouteType, _from, next) => {
path, path,
router.options.routes[0].children router.options.routes[0].children
); );
getTopMenu(true);
// query、params模式路由传参数的标签页不在此处处理 // query、params模式路由传参数的标签页不在此处处理
if (route && route.meta?.title) { if (route && route.meta?.title) {
useMultiTagsStoreHook().handleTags("push", { useMultiTagsStoreHook().handleTags("push", {

View File

@ -11,11 +11,11 @@ export default {
}, },
children: [ children: [
{ {
path: "/components/message", path: "/components/dialog",
name: "Message", name: "Dialog",
component: () => import("@/views/components/message/index.vue"), component: () => import("@/views/components/dialog/index.vue"),
meta: { meta: {
title: $t("menus.hsmessage"), title: $t("menus.hsdialog"),
extraIcon: "IF-pure-iconfont-new svg", extraIcon: "IF-pure-iconfont-new svg",
transition: { transition: {
enterTransition: "animate__fadeInLeft", enterTransition: "animate__fadeInLeft",
@ -23,6 +23,14 @@ export default {
} }
} }
}, },
{
path: "/components/message",
name: "Message",
component: () => import("@/views/components/message/index.vue"),
meta: {
title: $t("menus.hsmessage")
}
},
{ {
path: "/components/video", path: "/components/video",
name: "Video", name: "Video",

View File

@ -1,5 +1,6 @@
import { $t } from "@/plugins/i18n"; import { $t } from "@/plugins/i18n";
import { home } from "@/router/enums"; import { home } from "@/router/enums";
const { VITE_HIDE_HOME } = import.meta.env;
const Layout = () => import("@/layout/index.vue"); const Layout = () => import("@/layout/index.vue");
export default { export default {
@ -18,7 +19,8 @@ export default {
name: "Welcome", name: "Welcome",
component: () => import("@/views/welcome/index.vue"), component: () => import("@/views/welcome/index.vue"),
meta: { meta: {
title: $t("menus.hshome") title: $t("menus.hshome"),
showLink: VITE_HIDE_HOME === "true" ? false : true
} }
} }
] ]

View File

@ -16,8 +16,7 @@ export default [
path: "/redirect", path: "/redirect",
component: Layout, component: Layout,
meta: { meta: {
icon: "homeFilled", title: $t("status.hsLoad"),
title: $t("menus.hshome"),
showLink: false, showLink: false,
rank: 102 rank: 102
}, },

View File

@ -3,13 +3,11 @@ import {
RouteRecordRaw, RouteRecordRaw,
RouteComponent, RouteComponent,
createWebHistory, createWebHistory,
createWebHashHistory, createWebHashHistory
RouteRecordNormalized
} from "vue-router"; } from "vue-router";
import { router } from "./index"; import { router } from "./index";
import { isProxy, toRaw } from "vue"; import { isProxy, toRaw } from "vue";
import { useTimeoutFn } from "@vueuse/core"; import { useTimeoutFn } from "@vueuse/core";
import { RouteConfigs } from "@/layout/types";
import { import {
isString, isString,
cloneDeep, cloneDeep,
@ -19,8 +17,10 @@ import {
isIncludeAllChildren isIncludeAllChildren
} from "@pureadmin/utils"; } from "@pureadmin/utils";
import { getConfig } from "@/config"; import { getConfig } from "@/config";
import { menuType } from "@/layout/types";
import { buildHierarchyTree } from "@/utils/tree"; import { buildHierarchyTree } from "@/utils/tree";
import { sessionKey, type DataInfo } from "@/utils/auth"; import { sessionKey, type DataInfo } from "@/utils/auth";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
const IFrame = () => import("@/layout/frameView.vue"); const IFrame = () => import("@/layout/frameView.vue");
// https://cn.vitejs.dev/guide/features.html#glob-import // https://cn.vitejs.dev/guide/features.html#glob-import
@ -94,30 +94,20 @@ function filterNoPermissionTree(data: RouteComponent[]) {
return filterChildrenTree(newTree); return filterChildrenTree(newTree);
} }
/** 批量删除缓存路由(keepalive) */ /** 通过指定 `key` 获取父级路径集合,默认 `key` 为 `path` */
function delAliveRoutes(delAliveRouteList: Array<RouteConfigs>) { function getParentPaths(value: string, routes: RouteRecordRaw[], key = "path") {
delAliveRouteList.forEach(route => {
usePermissionStoreHook().cacheOperate({
mode: "delete",
name: route?.name
});
});
}
/** 通过path获取父级路径 */
function getParentPaths(path: string, routes: RouteRecordRaw[]) {
// 深度遍历查找 // 深度遍历查找
function dfs(routes: RouteRecordRaw[], path: string, parents: string[]) { function dfs(routes: RouteRecordRaw[], value: string, parents: string[]) {
for (let i = 0; i < routes.length; i++) { for (let i = 0; i < routes.length; i++) {
const item = routes[i]; const item = routes[i];
// 找到path则返回父级path // 返回父级path
if (item.path === path) return parents; if (item[key] === value) return parents;
// children不存在或为空则不递归 // children不存在或为空则不递归
if (!item.children || !item.children.length) continue; if (!item.children || !item.children.length) continue;
// 往下查找时将当前path入栈 // 往下查找时将当前path入栈
parents.push(item.path); parents.push(item.path);
if (dfs(item.children, path, parents).length) return parents; if (dfs(item.children, value, parents).length) return parents;
// 深度遍历查找未找到时当前path 出栈 // 深度遍历查找未找到时当前path 出栈
parents.pop(); parents.pop();
} }
@ -125,10 +115,10 @@ function getParentPaths(path: string, routes: RouteRecordRaw[]) {
return []; return [];
} }
return dfs(routes, path, []); return dfs(routes, value, []);
} }
/** 查找对应path的路由信息 */ /** 查找对应 `path` 的路由信息 */
function 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) {
@ -266,27 +256,29 @@ function formatTwoStageRoutes(routesList: RouteRecordRaw[]) {
} }
/** 处理缓存路由(添加、删除、刷新) */ /** 处理缓存路由(添加、删除、刷新) */
function handleAliveRoute(matched: RouteRecordNormalized[], mode?: string) { function handleAliveRoute({ name }: toRouteType, mode?: string) {
switch (mode) { switch (mode) {
case "add": case "add":
matched.forEach(v => { usePermissionStoreHook().cacheOperate({
usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name }); mode: "add",
name
}); });
break; break;
case "delete": case "delete":
usePermissionStoreHook().cacheOperate({ usePermissionStoreHook().cacheOperate({
mode: "delete", mode: "delete",
name: matched[matched.length - 1].name name
}); });
break; break;
default: default:
usePermissionStoreHook().cacheOperate({ usePermissionStoreHook().cacheOperate({
mode: "delete", mode: "delete",
name: matched[matched.length - 1].name name
}); });
useTimeoutFn(() => { useTimeoutFn(() => {
matched.forEach(v => { usePermissionStoreHook().cacheOperate({
usePermissionStoreHook().cacheOperate({ mode: "add", name: v.name }); mode: "add",
name
}); });
}, 100); }, 100);
} }
@ -361,17 +353,24 @@ function hasAuth(value: string | Array<string>): boolean {
return isAuths ? true : false; return isAuths ? true : false;
} }
/** 获取所有菜单中的第一个菜单(顶级菜单)*/
function getTopMenu(tag = false): menuType {
const topMenu = usePermissionStoreHook().wholeMenus[0]?.children[0];
tag && useMultiTagsStoreHook().handleTags("push", topMenu);
return topMenu;
}
export { export {
hasAuth, hasAuth,
getAuths, getAuths,
ascending, ascending,
filterTree, filterTree,
initRouter, initRouter,
getTopMenu,
addPathMatch, addPathMatch,
isOneOfArray, isOneOfArray,
getHistoryMode, getHistoryMode,
addAsyncRoutes, addAsyncRoutes,
delAliveRoutes,
getParentPaths, getParentPaths,
findRouteByPath, findRouteByPath,
handleAliveRoute, handleAliveRoute,

View File

@ -1,7 +1,7 @@
import { store } from "@/store"; import { store } from "@/store";
import { appType } from "./types"; import { appType } from "./types";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { getConfig } from "@/config"; import { getConfig, responsiveStorageNameSpace } from "@/config";
import { deviceDetection, storageLocal } from "@pureadmin/utils"; import { deviceDetection, storageLocal } from "@pureadmin/utils";
export const useAppStore = defineStore({ export const useAppStore = defineStore({
@ -9,15 +9,17 @@ export const useAppStore = defineStore({
state: (): appType => ({ state: (): appType => ({
sidebar: { sidebar: {
opened: opened:
storageLocal().getItem<StorageConfigs>("responsive-layout") storageLocal().getItem<StorageConfigs>(
?.sidebarStatus ?? getConfig().SidebarStatus, `${responsiveStorageNameSpace()}layout`
)?.sidebarStatus ?? getConfig().SidebarStatus,
withoutAnimation: false, withoutAnimation: false,
isClickCollapse: false isClickCollapse: false
}, },
// 这里的layout用于监听容器拖拉后恢复对应的导航模式 // 这里的layout用于监听容器拖拉后恢复对应的导航模式
layout: layout:
storageLocal().getItem<StorageConfigs>("responsive-layout")?.layout ?? storageLocal().getItem<StorageConfigs>(
getConfig().Layout, `${responsiveStorageNameSpace()}layout`
)?.layout ?? getConfig().Layout,
device: deviceDetection() ? "mobile" : "desktop", device: deviceDetection() ? "mobile" : "desktop",
// 作用于 src/views/components/draggable/index.vue 页面,当离开页面并不会销毁 new Swap()sortablejs 官网也没有提供任何销毁的 api // 作用于 src/views/components/draggable/index.vue 页面,当离开页面并不会销毁 new Swap()sortablejs 官网也没有提供任何销毁的 api
sortSwap: false sortSwap: false
@ -32,8 +34,9 @@ export const useAppStore = defineStore({
}, },
actions: { actions: {
TOGGLE_SIDEBAR(opened?: boolean, resize?: string) { TOGGLE_SIDEBAR(opened?: boolean, resize?: string) {
const layout = const layout = storageLocal().getItem<StorageConfigs>(
storageLocal().getItem<StorageConfigs>("responsive-layout"); `${responsiveStorageNameSpace()}layout`
);
if (opened && resize) { if (opened && resize) {
this.sidebar.withoutAnimation = true; this.sidebar.withoutAnimation = true;
this.sidebar.opened = true; this.sidebar.opened = true;
@ -48,7 +51,7 @@ export const useAppStore = defineStore({
this.sidebar.isClickCollapse = !this.sidebar.opened; this.sidebar.isClickCollapse = !this.sidebar.opened;
layout.sidebarStatus = this.sidebar.opened; layout.sidebarStatus = this.sidebar.opened;
} }
storageLocal().setItem("responsive-layout", layout); storageLocal().setItem(`${responsiveStorageNameSpace()}layout`, layout);
}, },
async toggleSideBar(opened?: boolean, resize?: string) { async toggleSideBar(opened?: boolean, resize?: string) {
await this.TOGGLE_SIDEBAR(opened, resize); await this.TOGGLE_SIDEBAR(opened, resize);

View File

@ -1,17 +1,19 @@
import { store } from "@/store"; import { store } from "@/store";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { getConfig } from "@/config";
import { storageLocal } from "@pureadmin/utils"; import { storageLocal } from "@pureadmin/utils";
import { getConfig, responsiveStorageNameSpace } from "@/config";
export const useEpThemeStore = defineStore({ export const useEpThemeStore = defineStore({
id: "pure-epTheme", id: "pure-epTheme",
state: () => ({ state: () => ({
epThemeColor: epThemeColor:
storageLocal().getItem<StorageConfigs>("responsive-layout") storageLocal().getItem<StorageConfigs>(
?.epThemeColor ?? getConfig().EpThemeColor, `${responsiveStorageNameSpace()}layout`
)?.epThemeColor ?? getConfig().EpThemeColor,
epTheme: epTheme:
storageLocal().getItem<StorageConfigs>("responsive-layout")?.theme ?? storageLocal().getItem<StorageConfigs>(
getConfig().Theme `${responsiveStorageNameSpace()}layout`
)?.theme ?? getConfig().Theme
}), }),
getters: { getters: {
getEpThemeColor(state) { getEpThemeColor(state) {
@ -30,13 +32,14 @@ export const useEpThemeStore = defineStore({
}, },
actions: { actions: {
setEpThemeColor(newColor: string): void { setEpThemeColor(newColor: string): void {
const layout = const layout = storageLocal().getItem<StorageConfigs>(
storageLocal().getItem<StorageConfigs>("responsive-layout"); `${responsiveStorageNameSpace()}layout`
);
this.epTheme = layout?.theme; this.epTheme = layout?.theme;
this.epThemeColor = newColor; this.epThemeColor = newColor;
if (!layout) return; if (!layout) return;
layout.epThemeColor = newColor; layout.epThemeColor = newColor;
storageLocal().setItem("responsive-layout", layout); storageLocal().setItem(`${responsiveStorageNameSpace()}layout`, layout);
} }
} }
}); });

View File

@ -2,18 +2,22 @@ import { defineStore } from "pinia";
import { store } from "@/store"; import { store } from "@/store";
import { routerArrays } from "@/layout/types"; import { routerArrays } from "@/layout/types";
import { multiType, positionType } from "./types"; import { multiType, positionType } from "./types";
import { responsiveStorageNameSpace } from "@/config";
import { isEqual, isBoolean, isUrl, storageLocal } from "@pureadmin/utils"; import { isEqual, isBoolean, isUrl, storageLocal } from "@pureadmin/utils";
export const useMultiTagsStore = defineStore({ export const useMultiTagsStore = defineStore({
id: "pure-multiTags", id: "pure-multiTags",
state: () => ({ state: () => ({
// 存储标签页信息(路由信息) // 存储标签页信息(路由信息)
multiTags: storageLocal().getItem<StorageConfigs>("responsive-configure") multiTags: storageLocal().getItem<StorageConfigs>(
?.multiTagsCache `${responsiveStorageNameSpace()}configure`
? storageLocal().getItem<StorageConfigs>("responsive-tags") )?.multiTagsCache
? storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}tags`
)
: [...routerArrays], : [...routerArrays],
multiTagsCache: storageLocal().getItem<StorageConfigs>( multiTagsCache: storageLocal().getItem<StorageConfigs>(
"responsive-configure" `${responsiveStorageNameSpace()}configure`
)?.multiTagsCache )?.multiTagsCache
}), }),
getters: { getters: {
@ -25,14 +29,20 @@ export const useMultiTagsStore = defineStore({
multiTagsCacheChange(multiTagsCache: boolean) { multiTagsCacheChange(multiTagsCache: boolean) {
this.multiTagsCache = multiTagsCache; this.multiTagsCache = multiTagsCache;
if (multiTagsCache) { if (multiTagsCache) {
storageLocal().setItem("responsive-tags", this.multiTags); storageLocal().setItem(
`${responsiveStorageNameSpace()}tags`,
this.multiTags
);
} else { } else {
storageLocal().removeItem("responsive-tags"); storageLocal().removeItem(`${responsiveStorageNameSpace()}tags`);
} }
}, },
tagsCache(multiTags) { tagsCache(multiTags) {
this.getMultiTagsCache && this.getMultiTagsCache &&
storageLocal().setItem("responsive-tags", multiTags); storageLocal().setItem(
`${responsiveStorageNameSpace()}tags`,
multiTags
);
}, },
handleTags<T>( handleTags<T>(
mode: string, mode: string,

View File

@ -2,6 +2,8 @@ import { defineStore } from "pinia";
import { store } from "@/store"; import { store } from "@/store";
import { cacheType } from "./types"; import { cacheType } from "./types";
import { constantMenus } from "@/router"; import { constantMenus } from "@/router";
import { getKeyList } from "@pureadmin/utils";
import { useMultiTagsStoreHook } from "./multiTags";
import { ascending, filterTree, filterNoPermissionTree } from "@/router/utils"; import { ascending, filterTree, filterNoPermissionTree } from "@/router/utils";
export const usePermissionStore = defineStore({ export const usePermissionStore = defineStore({
@ -25,7 +27,6 @@ export const usePermissionStore = defineStore({
switch (mode) { switch (mode) {
case "add": case "add":
this.cachePageList.push(name); this.cachePageList.push(name);
this.cachePageList = [...new Set(this.cachePageList)];
break; break;
case "delete": case "delete":
// eslint-disable-next-line no-case-declarations // eslint-disable-next-line no-case-declarations
@ -33,6 +34,20 @@ export const usePermissionStore = defineStore({
delIndex !== -1 && this.cachePageList.splice(delIndex, 1); delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
break; break;
} }
/** 监听缓存页面是否存在于标签页,不存在则删除 */
(() => {
let cacheLength = this.cachePageList.length;
const nameList = getKeyList(useMultiTagsStoreHook().multiTags, "name");
while (cacheLength > 0) {
nameList.findIndex(v => v === this.cachePageList[cacheLength - 1]) ===
-1 &&
this.cachePageList.splice(
this.cachePageList.indexOf(this.cachePageList[cacheLength - 1]),
1
);
cacheLength--;
}
})();
}, },
/** 清空缓存页面 */ /** 清空缓存页面 */
clearAllCachePage() { clearAllCachePage() {

View File

@ -32,8 +32,8 @@ html.dark {
.tags-view { .tags-view {
.arrow-left, .arrow-left,
.arrow-right { .arrow-right {
box-shadow: none;
border-right: 1px solid $border-style; border-right: 1px solid $border-style;
box-shadow: none;
} }
.arrow-right { .arrow-right {
@ -46,6 +46,7 @@ html.dark {
.el-divider__text { .el-divider__text {
--el-bg-color: var(--el-bg-color); --el-bg-color: var(--el-bg-color);
} }
.el-divider--horizontal { .el-divider--horizontal {
border-top: none; border-top: none;
} }
@ -63,10 +64,10 @@ html.dark {
} }
.form-edit-widget-label a { .form-edit-widget-label a {
color: $color-white;
background: var(--el-color-primary); background: var(--el-color-primary);
border: none; border: none;
border-radius: 5px; border-radius: 5px;
color: $color-white;
} }
.el-aside { .el-aside {
@ -84,14 +85,18 @@ html.dark {
.el-table__cell { .el-table__cell {
background: var(--el-bg-color); background: var(--el-bg-color);
} }
.el-card { .el-card {
--el-card-bg-color: var(--el-bg-color); --el-card-bg-color: var(--el-bg-color);
// border: none !important; // border: none !important;
} }
.el-backtop { .el-backtop {
--el-backtop-bg-color: var(--el-color-primary-light-9); --el-backtop-bg-color: var(--el-color-primary-light-9);
--el-backtop-hover-bg-color: var(--el-color-primary); --el-backtop-hover-bg-color: var(--el-color-primary);
} }
.el-dropdown-menu__item:not(.is-disabled):hover { .el-dropdown-menu__item:not(.is-disabled):hover {
background: transparent; background: transparent;
} }
@ -103,18 +108,18 @@ html.dark {
&.el-message-box__close, &.el-message-box__close,
&.el-notification__closeBtn { &.el-notification__closeBtn {
&:hover { &:hover {
color: rgba(255, 255, 255, 0.85) !important; color: rgb(255 255 255 / 85%) !important;
background-color: rgba(255, 255, 255, 0.12); background-color: rgb(255 255 255 / 12%);
} }
} }
} }
/* 克隆并自定义 ElMessage 样式,不会影响 ElMessage 原本样式,在 src/utils/message.ts 中调用自定义样式 ElMessage 方法即可,非暗黑模式在 src/style/element-plus.scss 文件进行了适配 */ /* 克隆并自定义 ElMessage 样式,不会影响 ElMessage 原本样式,在 src/utils/message.ts 中调用自定义样式 ElMessage 方法即可,非暗黑模式在 src/style/element-plus.scss 文件进行了适配 */
.pure-message { .pure-message {
background-color: rgb(36 37 37) !important;
background-image: initial !important; background-image: initial !important;
background-color: rgb(36, 37, 37) !important; box-shadow: rgb(13 13 13 / 12%) 0 3px 6px -4px,
box-shadow: rgb(13 13 13 / 12%) 0px 3px 6px -4px, rgb(13 13 13 / 8%) 0 6px 16px 0, rgb(13 13 13 / 5%) 0 9px 28px 8px !important;
rgb(13 13 13 / 8%) 0px 6px 16px 0px, rgb(13 13 13 / 5%) 0px 9px 28px 8px !important;
& .el-message__content { & .el-message__content {
color: $color-white !important; color: $color-white !important;
@ -124,8 +129,8 @@ html.dark {
& .el-message__closeBtn { & .el-message__closeBtn {
&:hover { &:hover {
color: rgba(255, 255, 255, 0.85); color: rgb(255 255 255 / 85%);
background-color: rgba(255, 255, 255, 0.12); background-color: rgb(255 255 255 / 12%);
} }
} }
} }

View File

@ -78,6 +78,7 @@
} }
} }
} }
.el-icon { .el-icon {
&.el-dialog__close, &.el-dialog__close,
&.el-drawer__close, &.el-drawer__close,
@ -85,22 +86,23 @@
&.el-notification__closeBtn { &.el-notification__closeBtn {
width: 24px; width: 24px;
height: 24px; height: 24px;
outline: none;
border-radius: 4px; border-radius: 4px;
outline: none;
transition: background-color 0.2s, color 0.2s; transition: background-color 0.2s, color 0.2s;
&:hover { &:hover {
color: rgba(0, 0, 0, 0.88) !important; color: rgb(0 0 0 / 88%) !important;
background-color: rgba(0, 0, 0, 0.06);
text-decoration: none; text-decoration: none;
background-color: rgb(0 0 0 / 6%);
} }
} }
} }
/* 克隆并自定义 ElMessage 样式,不会影响 ElMessage 原本样式,在 src/utils/message.ts 中调用自定义样式 ElMessage 方法即可,暗黑模式在 src/style/dark.scss 文件进行了适配 */ /* 克隆并自定义 ElMessage 样式,不会影响 ElMessage 原本样式,在 src/utils/message.ts 中调用自定义样式 ElMessage 方法即可,暗黑模式在 src/style/dark.scss 文件进行了适配 */
.pure-message { .pure-message {
border-width: 0 !important;
background: #fff !important;
padding: 10px 13px !important; padding: 10px 13px !important;
background: #fff !important;
border-width: 0 !important;
box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014,
0 9px 28px 8px #0000000d !important; 0 9px 28px 8px #0000000d !important;
@ -119,13 +121,13 @@
} }
& .el-message__closeBtn { & .el-message__closeBtn {
outline: none;
border-radius: 4px;
right: 9px !important; right: 9px !important;
border-radius: 4px;
outline: none;
transition: background-color 0.2s, color 0.2s; transition: background-color 0.2s, color 0.2s;
&:hover { &:hover {
background-color: rgba(0, 0, 0, 0.06); background-color: rgb(0 0 0 / 6%);
} }
} }
} }

View File

@ -1,8 +1,7 @@
@import "./mixin.scss"; @import "./transition";
@import "./transition.scss"; @import "./element-plus";
@import "./element-plus.scss"; @import "./sidebar";
@import "./sidebar.scss"; @import "./dark";
@import "./dark.scss";
/* 自定义全局 CssVar */ /* 自定义全局 CssVar */
:root { :root {

View File

@ -1,28 +0,0 @@
@mixin clearfix {
&::after {
content: "";
display: table;
clear: both;
}
}
@mixin relative {
position: relative;
width: 100%;
height: 100%;
}
@mixin scrollBar {
&::-webkit-scrollbar-track-piece {
background: #d3dce6;
}
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #99a9bf;
border-radius: 20px;
}
}

View File

@ -2,9 +2,9 @@
::before, ::before,
::after { ::after {
box-sizing: border-box; box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: currentColor; border-color: currentColor;
border-style: solid;
border-width: 0;
} }
#app { #app {
@ -13,25 +13,24 @@
} }
html { html {
line-height: 1.5; box-sizing: border-box;
-webkit-text-size-adjust: 100%;
-moz-tab-size: 4;
tab-size: 4;
width: 100%; width: 100%;
height: 100%; height: 100%;
box-sizing: border-box; line-height: 1.5;
tab-size: 4;
text-size-adjust: 100%;
} }
body { body {
margin: 0;
line-height: inherit;
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
line-height: inherit;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
text-rendering: optimizelegibility; text-rendering: optimizelegibility;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
} }
hr { hr {
@ -69,9 +68,9 @@ small {
sub, sub,
sup { sup {
position: relative;
font-size: 75%; font-size: 75%;
line-height: 0; line-height: 0;
position: relative;
vertical-align: baseline; vertical-align: baseline;
} }
@ -85,8 +84,8 @@ sup {
table { table {
text-indent: 0; text-indent: 0;
border-color: inherit;
border-collapse: collapse; border-collapse: collapse;
border-color: inherit;
} }
button, button,
@ -94,12 +93,12 @@ input,
optgroup, optgroup,
select, select,
textarea { textarea {
padding: 0;
margin: 0;
font-family: inherit; font-family: inherit;
font-size: 100%; font-size: 100%;
line-height: inherit; line-height: inherit;
color: inherit; color: inherit;
margin: 0;
padding: 0;
} }
button, button,
@ -160,8 +159,8 @@ pre {
} }
fieldset { fieldset {
margin: 0;
padding: 0; padding: 0;
margin: 0;
} }
legend { legend {
@ -171,9 +170,9 @@ legend {
ol, ol,
ul, ul,
menu { menu {
list-style: none;
margin: 0;
padding: 0; padding: 0;
margin: 0;
list-style: none;
} }
textarea { textarea {
@ -182,8 +181,8 @@ textarea {
input::placeholder, input::placeholder,
textarea::placeholder { textarea::placeholder {
opacity: 1;
color: #9ca3af; color: #9ca3af;
opacity: 1;
} }
button, button,
@ -238,9 +237,9 @@ a:active {
a, a,
a:focus, a:focus,
a:hover { a:hover {
cursor: pointer;
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
cursor: pointer;
} }
div:focus { div:focus {
@ -249,11 +248,11 @@ div:focus {
.clearfix { .clearfix {
&::after { &::after {
visibility: hidden;
display: block; display: block;
font-size: 0;
content: " ";
clear: both;
height: 0; height: 0;
clear: both;
font-size: 0;
visibility: hidden;
content: " ";
} }
} }

View File

@ -2,20 +2,21 @@
@mixin merge-style($sideBarWidth) { @mixin merge-style($sideBarWidth) {
$menuActiveText: #7a80b4; $menuActiveText: #7a80b4;
@media screen and (min-width: 150px) and (max-width: 420px) { @media screen and (width >= 150px) and (width <= 420px) {
.app-main-nofixed-header { .app-main-nofixed-header {
overflow-y: hidden; overflow-y: hidden;
} }
} }
@media screen and (min-width: 420px) {
@media screen and (width >= 420px) {
.app-main-nofixed-header { .app-main-nofixed-header {
overflow: hidden; overflow: hidden;
} }
} }
.sub-menu-icon { .sub-menu-icon {
font-size: 18px;
margin-right: 5px; margin-right: 5px;
font-size: 18px;
svg { svg {
width: 18px; width: 18px;
@ -24,26 +25,27 @@
} }
.set-icon { .set-icon {
height: 48px;
width: 40px;
display: flex; display: flex;
cursor: pointer;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 40px;
height: 48px;
cursor: pointer;
} }
.main-container { .main-container {
position: relative;
height: 100vh; height: 100vh;
min-height: 100%; min-height: 100%;
/* main-content 属性动画 */
transition: margin-left var(--pure-transition-duration);
margin-left: $sideBarWidth; margin-left: $sideBarWidth;
position: relative;
background: #f0f2f5; background: #f0f2f5;
/* main-content 属性动画 */
transition: margin-left var(--pure-transition-duration);
.el-scrollbar__wrap { .el-scrollbar__wrap {
overflow: auto;
height: 100%; height: 100%;
overflow: auto;
} }
} }
@ -53,6 +55,7 @@
right: 0; right: 0;
z-index: 998; z-index: 998;
width: calc(100% - 210px); width: calc(100% - 210px);
/* fixed-header 属性左上角动画 */ /* fixed-header 属性左上角动画 */
transition: width var(--pure-transition-duration); transition: width var(--pure-transition-duration);
} }
@ -70,20 +73,21 @@
} }
.sidebar-container { .sidebar-container {
/* 展开动画 */
transition: width var(--pure-transition-duration);
width: $sideBarWidth !important;
background: $menuBg;
height: 100%;
position: fixed; position: fixed;
font-size: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
left: 0; left: 0;
z-index: 1001; z-index: 1001;
width: $sideBarWidth !important;
height: 100%;
overflow: hidden; overflow: hidden;
font-size: 0;
background: $menuBg;
box-shadow: 0 0 1px #888; box-shadow: 0 0 1px #888;
/* 展开动画 */
transition: width var(--pure-transition-duration);
.scrollbar-wrapper { .scrollbar-wrapper {
overflow-x: hidden !important; overflow-x: hidden !important;
} }
@ -101,6 +105,7 @@
/* logo: 48px、leftCollapse: 40px、leftCollapse-shadow: 4px */ /* logo: 48px、leftCollapse: 40px、leftCollapse-shadow: 4px */
height: calc(100% - 92px); height: calc(100% - 92px);
} }
.el-scrollbar.mobile { .el-scrollbar.mobile {
height: 100%; height: 100%;
} }
@ -113,15 +118,15 @@
a { a {
display: inline-block; display: inline-block;
display: flex; display: flex;
padding-left: 10px;
flex-wrap: wrap; flex-wrap: wrap;
width: 100%; width: 100%;
padding-left: 10px;
} }
.el-menu { .el-menu {
border: none;
height: 100%; height: 100%;
background-color: transparent !important; background-color: transparent !important;
border: none;
} }
.el-menu-item, .el-menu-item,
@ -158,8 +163,8 @@
} }
.is-active { .is-active {
transition: color 0.3s;
color: $subMenuActiveText !important; color: $subMenuActiveText !important;
transition: color 0.3s;
} }
.el-menu-item.is-active.nest-menu > * { .el-menu-item.is-active.nest-menu > * {
@ -168,22 +173,19 @@
} }
.el-menu-item.is-active.nest-menu::before { .el-menu-item.is-active.nest-menu::before {
content: "";
clear: both;
position: absolute; position: absolute;
left: 8px; inset: 0 8px;
right: 8px;
margin: 4px 0; margin: 4px 0;
top: 0; clear: both;
bottom: 0; content: "";
border-radius: 3px;
background: var(--el-color-primary) !important; background: var(--el-color-primary) !important;
border-radius: 3px;
} }
.el-menu .el-menu--inline .el-sub-menu__title, .el-menu .el-menu--inline .el-sub-menu__title,
& .el-sub-menu .el-menu-item { & .el-sub-menu .el-menu-item {
font-size: 12px;
min-width: $sideBarWidth !important; min-width: $sideBarWidth !important;
font-size: 12px;
background-color: $subMenuBg !important; background-color: $subMenuBg !important;
} }
@ -196,21 +198,21 @@
left: 0; left: 0;
width: 2px; width: 2px;
height: 100%; height: 100%;
background-color: $menuActiveBefore;
content: "";
clear: both; clear: both;
content: "";
background-color: $menuActiveBefore;
transition: all var(--pure-transition-duration) ease-in-out; transition: all var(--pure-transition-duration) ease-in-out;
transform: translateY(0); transform: translateY(0);
} }
.el-menu--collapse .outer-most.el-sub-menu > .el-sub-menu__title::before { .el-menu--collapse .outer-most.el-sub-menu > .el-sub-menu__title::before {
content: "";
display: block;
position: absolute; position: absolute;
height: 0;
width: 3px;
transform: translateY(-50%);
top: 50%; top: 50%;
display: block;
width: 3px;
height: 0;
content: "";
transform: translateY(-50%);
} }
/* 无子集的激活菜单背景 */ /* 无子集的激活菜单背景 */
@ -219,17 +221,15 @@
color: #fff; color: #fff;
background: transparent !important; background: transparent !important;
} }
.is-active.submenu-title-noDropdown.outer-most::before { .is-active.submenu-title-noDropdown.outer-most::before {
content: "";
clear: both;
position: absolute; position: absolute;
left: 8px; inset: 0 8px;
right: 8px;
margin: 4px 0; margin: 4px 0;
top: 0; clear: both;
bottom: 0; content: "";
border-radius: 3px;
background: var(--el-color-primary) !important; background: var(--el-color-primary) !important;
border-radius: 3px;
} }
} }
@ -262,8 +262,8 @@
/* 子菜单中还有子菜单 */ /* 子菜单中还有子菜单 */
.el-menu .el-sub-menu__title { .el-menu .el-sub-menu__title {
font-size: 12px;
min-width: $sideBarWidth !important; min-width: $sideBarWidth !important;
font-size: 12px;
background-color: $subMenuBg !important; background-color: $subMenuBg !important;
} }
@ -280,8 +280,8 @@
} }
.is-active { .is-active {
transition: color 0.3s;
color: $subMenuActiveText !important; color: $subMenuActiveText !important;
transition: color 0.3s;
} }
.el-menu-item.is-active.nest-menu > * { .el-menu-item.is-active.nest-menu > * {
@ -290,15 +290,12 @@
} }
.el-menu-item.is-active.nest-menu::before { .el-menu-item.is-active.nest-menu::before {
content: "";
clear: both;
position: absolute; position: absolute;
left: 8px; inset: 0 8px;
right: 8px; clear: both;
top: 0; content: "";
bottom: 0;
border-radius: 3px;
background: var(--el-color-primary) !important; background: var(--el-color-primary) !important;
border-radius: 3px;
} }
.el-menu-item, .el-menu-item,
@ -346,8 +343,8 @@
/* 子菜单中还有子菜单 */ /* 子菜单中还有子菜单 */
.el-menu .el-sub-menu__title { .el-menu .el-sub-menu__title {
font-size: 12px;
min-width: $sideBarWidth !important; min-width: $sideBarWidth !important;
font-size: 12px;
background-color: $subMenuBg !important; background-color: $subMenuBg !important;
&:hover { &:hover {
@ -372,8 +369,8 @@
} }
.el-menu-item.is-active { .el-menu-item.is-active {
transition: color 0.3s;
color: $subMenuActiveText !important; color: $subMenuActiveText !important;
transition: color 0.3s;
} }
.el-menu-item.is-active.nest-menu > * { .el-menu-item.is-active.nest-menu > * {
@ -382,68 +379,65 @@
} }
.el-menu-item.is-active.nest-menu::before { .el-menu-item.is-active.nest-menu::before {
content: "";
clear: both;
position: absolute; position: absolute;
left: 5px; inset: 0 5px;
right: 5px; clear: both;
top: 0; content: "";
bottom: 0;
border-radius: 3px;
background: var(--el-color-primary) !important; background: var(--el-color-primary) !important;
border-radius: 3px;
} }
} }
.horizontal-header { .horizontal-header {
display: flex; display: flex;
align-items: center;
justify-content: space-around; justify-content: space-around;
background: $menuBg;
width: 100%; width: 100%;
height: 48px; height: 48px;
align-items: center; background: $menuBg;
.horizontal-header-left { .horizontal-header-left {
display: flex; display: flex;
height: 100%; align-items: center;
width: auto; width: auto;
min-width: 200px; min-width: 200px;
align-items: center; height: 100%;
padding-left: 10px; padding-left: 10px;
cursor: pointer; cursor: pointer;
transition: all var(--pure-transition-duration) ease; transition: all var(--pure-transition-duration) ease;
img { img {
height: 32px;
display: inline-block; display: inline-block;
height: 32px;
} }
span { span {
height: 32px;
line-height: 32px;
margin: 2px 0 0 12px;
color: $subMenuActiveText;
display: inline-block; display: inline-block;
height: 32px;
margin: 2px 0 0 12px;
overflow: hidden; overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
line-height: 32px;
color: $subMenuActiveText;
text-overflow: ellipsis;
white-space: nowrap;
} }
} }
.horizontal-header-menu { .horizontal-header-menu {
height: 100%;
min-width: 0;
flex: 1; flex: 1;
align-items: center; align-items: center;
min-width: 0;
height: 100%;
} }
.horizontal-header-right { .horizontal-header-right {
display: flex; display: flex;
min-width: 340px;
align-items: center; align-items: center;
color: $subMenuActiveText;
justify-content: flex-end; justify-content: flex-end;
min-width: 340px;
color: $subMenuActiveText;
/* 搜索 */ /* 搜索 */
.search-container, .search-container,
@ -469,19 +463,19 @@
width: 40px; width: 40px;
height: 48px; height: 48px;
padding: 11px; padding: 11px;
outline: none;
cursor: pointer;
color: $subMenuActiveText; color: $subMenuActiveText;
cursor: pointer;
outline: none;
} }
.el-dropdown-link { .el-dropdown-link {
height: 48px;
padding: 10px;
display: flex; display: flex;
cursor: pointer;
align-items: center; align-items: center;
justify-content: space-around; justify-content: space-around;
height: 48px;
padding: 10px;
color: $subMenuActiveText; color: $subMenuActiveText;
cursor: pointer;
p { p {
font-size: 14px; font-size: 14px;
@ -496,10 +490,10 @@
} }
.el-menu { .el-menu {
border: none;
height: 100%;
width: 100% !important; width: 100% !important;
height: 100%;
background-color: transparent; background-color: transparent;
border: none;
} }
.el-menu-item, .el-menu-item,
@ -533,8 +527,8 @@
} }
.is-active { .is-active {
transition: color 0.3s;
color: $subMenuActiveText !important; color: $subMenuActiveText !important;
transition: color 0.3s;
} }
} }
@ -554,8 +548,8 @@
} }
.sidebar-container { .sidebar-container {
transition: transform var(--pure-transition-duration);
width: $sideBarWidth; width: $sideBarWidth;
transition: transform var(--pure-transition-duration);
} }
&.hideSidebar { &.hideSidebar {
@ -570,6 +564,7 @@
body[layout="vertical"] { body[layout="vertical"] {
$sideBarWidth: 210px; $sideBarWidth: 210px;
@include merge-style($sideBarWidth); @include merge-style($sideBarWidth);
.el-menu--collapse { .el-menu--collapse {
@ -587,8 +582,8 @@ body[layout="vertical"] {
} }
.sidebar-container { .sidebar-container {
transition: width var(--pure-transition-duration);
width: 54px !important; width: 54px !important;
transition: width var(--pure-transition-duration);
.is-active.submenu-title-noDropdown.outer-most { .is-active.submenu-title-noDropdown.outer-most {
background: transparent !important; background: transparent !important;
@ -604,8 +599,8 @@ body[layout="vertical"] {
.el-sub-menu { .el-sub-menu {
& > .el-sub-menu__title { & > .el-sub-menu__title {
& > span { & > span {
height: 100%;
width: 100%; width: 100%;
height: 100%;
text-align: center; text-align: center;
visibility: visible; visibility: visible;
} }
@ -644,6 +639,7 @@ body[layout="vertical"] {
body[layout="horizontal"] { body[layout="horizontal"] {
$sideBarWidth: 0; $sideBarWidth: 0;
@include merge-style($sideBarWidth); @include merge-style($sideBarWidth);
.fixed-header, .fixed-header,
@ -658,6 +654,7 @@ body[layout="horizontal"] {
body[layout="mix"] { body[layout="mix"] {
$sideBarWidth: 210px; $sideBarWidth: 210px;
@include merge-style($sideBarWidth); @include merge-style($sideBarWidth);
.el-menu--collapse { .el-menu--collapse {
@ -675,8 +672,8 @@ body[layout="mix"] {
} }
.sidebar-container { .sidebar-container {
transition: width var(--pure-transition-duration);
width: 54px !important; width: 54px !important;
transition: width var(--pure-transition-duration);
.is-active.submenu-title-noDropdown.outer-most { .is-active.submenu-title-noDropdown.outer-most {
background: transparent !important; background: transparent !important;
@ -692,9 +689,10 @@ body[layout="mix"] {
.el-sub-menu { .el-sub-menu {
& > .el-sub-menu__title { & > .el-sub-menu__title {
padding: 0; padding: 0;
& > span { & > span {
height: 100%;
width: 100%; width: 100%;
height: 100%;
text-align: center; text-align: center;
visibility: visible; visibility: visible;
} }

View File

@ -31,6 +31,7 @@
} }
.breadcrumb-leave-active { .breadcrumb-leave-active {
position: absolute;
transition: all 0.3s; transition: all 0.3s;
} }
@ -40,10 +41,6 @@
transform: translateX(20px); transform: translateX(20px);
} }
.breadcrumb-leave-active {
position: absolute;
}
/** /**
* @description 重置el-menu的展开收起动画时长 * @description 重置el-menu的展开收起动画时长
*/ */

View File

@ -60,7 +60,7 @@ class PureHttp {
/** 请求拦截 */ /** 请求拦截 */
private httpInterceptorsRequest(): void { private httpInterceptorsRequest(): void {
PureHttp.axiosInstance.interceptors.request.use( PureHttp.axiosInstance.interceptors.request.use(
async (config: PureHttpRequestConfig) => { async (config: PureHttpRequestConfig): Promise<any> => {
// 开启进度条动画 // 开启进度条动画
NProgress.start(); NProgress.start();
// 优先判断post/get等方法是否传入回掉否则执行初始化设置等回掉 // 优先判断post/get等方法是否传入回掉否则执行初始化设置等回掉

View File

@ -2,10 +2,10 @@
import { App } from "vue"; import { App } from "vue";
import Storage from "responsive-storage"; import Storage from "responsive-storage";
import { routerArrays } from "@/layout/types"; import { routerArrays } from "@/layout/types";
import { responsiveStorageNameSpace } from "@/config";
const nameSpace = "responsive-";
export const injectResponsiveStorage = (app: App, config: ServerConfigs) => { export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
const nameSpace = responsiveStorageNameSpace();
const configObj = Object.assign( const configObj = Object.assign(
{ {
// 国际化 默认中文zh // 国际化 默认中文zh
@ -31,7 +31,7 @@ export const injectResponsiveStorage = (app: App, config: ServerConfigs) => {
}, },
config.MultiTagsCache config.MultiTagsCache
? { ? {
// 默认显示首页tag // 默认显示顶级菜单tag
tags: Storage.getData("tags", nameSpace) ?? routerArrays tags: Storage.getData("tags", nameSpace) ?? routerArrays
} }
: {} : {}

View File

@ -38,7 +38,7 @@ const barcodes = [
<el-link <el-link
href="https://lindell.me/JsBarcode/" href="https://lindell.me/JsBarcode/"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 5px 4px 0" style="margin: 0 5px 4px 0; font-size: 16px"
> >
JsBarcode JsBarcode
</el-link> </el-link>

View File

@ -76,7 +76,7 @@ const exportExcel = () => {
<el-link <el-link
href="https://github.com/SheetJS/sheetjs" href="https://github.com/SheetJS/sheetjs"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 5px 4px 0" style="margin: 0 5px 4px 0; font-size: 16px"
> >
github地址 github地址
</el-link> </el-link>

View File

@ -39,7 +39,7 @@ const load = () => {
<el-link <el-link
href="https://github.com/yujinpan/el-table-infinite-scroll" href="https://github.com/yujinpan/el-table-infinite-scroll"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 5px 4px 0" style="margin: 0 5px 4px 0; font-size: 16px"
> >
github地址 github地址
</el-link> </el-link>

View File

@ -52,7 +52,7 @@ const filterMethod = (query: string, node: treeNode) => {
href="https://element-plus.gitee.io/zh-CN/component/tree-v2.html" href="https://element-plus.gitee.io/zh-CN/component/tree-v2.html"
target="_blank" target="_blank"
:icon="useRenderIcon(NodeTree)" :icon="useRenderIcon(NodeTree)"
style="font-size: 16px; margin: 0 5px 4px 0" style="margin: 0 5px 4px 0; font-size: 16px"
> >
Tree V2 Tree V2
</el-link> </el-link>

View File

@ -41,7 +41,7 @@ const onPrint = () => {
<el-link <el-link
href="https://github.com/hrynko/vue-pdf-embed" href="https://github.com/hrynko/vue-pdf-embed"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 5px 4px 0" style="margin: 0 5px 4px 0; font-size: 16px"
> >
github地址 github地址
</el-link> </el-link>

View File

@ -201,7 +201,7 @@ const tableData: User[] = [
.card-header { .card-header {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
justify-content: space-between;
} }
</style> </style>

View File

@ -30,7 +30,7 @@ const disabledClick = () => {
<el-link <el-link
href="https://github.com/soldair/node-qrcode" href="https://github.com/soldair/node-qrcode"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 5px 4px 0" style="margin: 0 5px 4px 0; font-size: 16px"
> >
qrcode qrcode
</el-link> </el-link>

View File

@ -91,7 +91,7 @@ const swiperExample: any[] = [
<el-link <el-link
href="https://github.com/nolimits4web/swiper" href="https://github.com/nolimits4web/swiper"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 5px 4px 0" style="margin: 0 5px 4px 0; font-size: 16px"
> >
github地址 github地址
</el-link> </el-link>

View File

@ -80,26 +80,26 @@ const activities = [
<style scoped> <style scoped>
.message { .message {
position: relative;
box-sizing: border-box;
width: 200px; width: 200px;
padding: 5px 12px;
line-height: 18px;
color: #fff;
word-break: break-all;
background-color: var(--el-color-primary); background-color: var(--el-color-primary);
border-color: var(--el-color-primary); border-color: var(--el-color-primary);
color: #fff;
line-height: 18px;
padding: 5px 12px 5px 12px;
box-sizing: border-box;
border-radius: 6px; border-radius: 6px;
position: relative;
word-break: break-all;
} }
.message::after { .message::after {
content: "";
position: absolute; position: absolute;
top: 8px; top: 8px;
left: -10px; left: -10px;
width: 0; width: 0;
height: 0; height: 0;
overflow: hidden; overflow: hidden;
content: "";
border-color: var(--el-color-primary) transparent transparent; border-color: var(--el-color-primary) transparent transparent;
border-style: solid dashed dashed; border-style: solid dashed dashed;
border-width: 10px; border-width: 10px;

View File

@ -65,9 +65,9 @@ const filteredItems = computed(() => {
<style scoped> <style scoped>
.dynamic-scroller-demo { .dynamic-scroller-demo {
overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
} }
.scroller { .scroller {
@ -81,10 +81,10 @@ const filteredItems = computed(() => {
} }
.message { .message {
box-sizing: border-box;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 32px; min-height: 32px;
padding: 12px; padding: 12px;
box-sizing: border-box;
} }
</style> </style>

View File

@ -16,7 +16,7 @@ defineOptions({
<el-link <el-link
href="https://github.com/Akryum/vue-virtual-scroller/tree/next/packages/vue-virtual-scroller" href="https://github.com/Akryum/vue-virtual-scroller/tree/next/packages/vue-virtual-scroller"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 5px 4px 0" style="margin: 0 5px 4px 0; font-size: 16px"
> >
github地址 github地址
</el-link> </el-link>

View File

@ -60,9 +60,9 @@ const filteredItems = computed(() => {
<style scoped> <style scoped>
.dynamic-scroller-demo { .dynamic-scroller-demo {
overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: hidden;
} }
.scroller { .scroller {
@ -70,9 +70,9 @@ const filteredItems = computed(() => {
} }
.message { .message {
box-sizing: border-box;
display: flex; display: flex;
min-height: 32px; min-height: 32px;
padding: 12px; padding: 12px;
box-sizing: border-box;
} }
</style> </style>

View File

@ -57,10 +57,11 @@ export function useColumns() {
label: "QQ交流群", label: "QQ交流群",
cellRenderer: () => { cellRenderer: () => {
return ( return (
<a href="https://jq.qq.com/?_wv=1027&k=E9fwmFGr" target="_blank"> <a
<span style="color: var(--el-color-primary)"> href="https://yiming_chang.gitee.io/pure-admin-doc/pages/support/#qq-%E4%BA%A4%E6%B5%81%E7%BE%A4"
Pure Admin target="_blank"
</span> >
<span style="color: var(--el-color-primary)"></span>
</a> </a>
); );
} }

View File

@ -63,12 +63,12 @@ export default defineComponent({
<style scoped> <style scoped>
.wrapper { .wrapper {
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
justify-content: center;
width: 300px; width: 300px;
height: 200px; height: 200px;
border: 3px dashed rgba(90, 167, 164, 0.9); background-color: rgb(90 167 164 / 20%);
border: 3px dashed rgb(90 167 164 / 90%);
border-radius: 8px; border-radius: 8px;
background-color: rgba(90, 167, 164, 0.2);
} }
</style> </style>

View File

@ -98,13 +98,13 @@ export default defineComponent({
<style scoped> <style scoped>
.wrapper { .wrapper {
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
justify-content: center;
width: 300px; width: 300px;
height: 200px; height: 200px;
margin-bottom: 30px; margin-bottom: 30px;
border: 3px dashed rgba(90, 167, 164, 0.9); background-color: rgb(90 167 164 / 20%);
border: 3px dashed rgb(90 167 164 / 90%);
border-radius: 8px; border-radius: 8px;
background-color: rgba(90, 167, 164, 0.2);
} }
</style> </style>

View File

@ -60,12 +60,12 @@ export default ExampleSFC;
<style scoped> <style scoped>
.wrapper { .wrapper {
display: flex; display: flex;
justify-content: center;
align-items: center; align-items: center;
justify-content: center;
width: 300px; width: 300px;
height: 200px; height: 200px;
border: 3px dashed rgba(90, 167, 164, 0.9); background-color: rgb(90 167 164 / 20%);
border: 3px dashed rgb(90 167 164 / 90%);
border-radius: 8px; border-radius: 8px;
background-color: rgba(90, 167, 164, 0.2);
} }
</style> </style>

View File

@ -44,8 +44,8 @@ const onCropper = (): void => {
<style scoped> <style scoped>
.cropper-container { .cropper-container {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
justify-content: space-between;
} }
.el-button { .el-button {

View File

@ -110,7 +110,7 @@ function addDanmu() {
<el-link <el-link
href="https://github.com/hellodigua/vue-danmaku/tree/vue3" href="https://github.com/hellodigua/vue-danmaku/tree/vue3"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
vue3-danmaku vue3-danmaku
</el-link> </el-link>
@ -197,10 +197,10 @@ function addDanmu() {
align-items: center; align-items: center;
.img { .img {
height: 25px;
width: 25px; width: 25px;
border-radius: 50%; height: 25px;
margin-right: 5px; margin-right: 5px;
border-radius: 50%;
} }
} }
} }

View File

@ -0,0 +1,271 @@
<script setup lang="tsx">
import { h, createVNode } from "vue";
import { message } from "@/utils/message";
import { addDialog, closeDialog, closeAllDialog } from "@/components/ReDialog";
defineOptions({
name: "Dialog"
});
function onBaseClick() {
addDialog({
title: "基本使用",
contentRenderer: () => <p>弹框内容-基本使用</p> // jsx .vuejsxscriptlang="tsx"
});
}
function onDraggableClick() {
addDialog({
title: "可拖拽",
draggable: true,
contentRenderer: () => h("p", "弹框内容-可拖拽") // h 渲染函数 https://cn.vuejs.org/api/render-function.html#h
});
}
function onFullscreenClick() {
addDialog({
title: "全屏",
fullscreen: true,
contentRenderer: () => createVNode("p", null, "弹框内容-全屏") // createVNode 渲染函数 https://cn.vuejs.org/guide/extras/render-function.html#creating-vnodes
});
}
function onModalClick() {
addDialog({
title: "无背景遮罩层",
modal: false,
contentRenderer: () => <p>弹框内容-无背景遮罩层</p>
});
}
function onStyleClick() {
addDialog({
title: "自定义弹出位置",
top: "60vh",
style: { marginRight: "20px" },
contentRenderer: () => <p>弹框内容-自定义弹出位置</p>
});
}
function onoOpenDelayClick() {
addDialog({
title: "延时2秒打开弹框",
openDelay: 2000,
contentRenderer: () => <p>弹框内容-延时2秒打开弹框</p>
});
}
function onCloseDelayClick() {
addDialog({
title: "延时2秒关闭弹框",
closeDelay: 2000,
contentRenderer: () => <p>弹框内容-延时2秒关闭弹框</p>
});
}
function onShowCloseClick() {
addDialog({
title: "不显示右上角关闭按钮图标",
showClose: false,
contentRenderer: () => <p>弹框内容-不显示右上角关闭按钮图标</p>
});
}
function onBeforeCloseClick() {
addDialog({
title: "禁止通过键盘ESC关闭",
closeOnPressEscape: false,
contentRenderer: () => <p>弹框内容-禁止通过键盘ESC关闭</p>
});
}
function onCloseOnClickModalClick() {
addDialog({
title: "禁止通过点击modal关闭",
closeOnClickModal: false,
contentRenderer: () => <p>弹框内容-禁止通过点击modal关闭</p>
});
}
function onHideFooterClick() {
addDialog({
title: "隐藏底部取消、确定按钮",
hideFooter: true,
contentRenderer: () => <p>弹框内容-隐藏底部取消确定按钮</p>
});
}
function onHeaderRendererClick() {
addDialog({
title: "自定义头部",
showClose: false,
headerRenderer: ({ close, titleId, titleClass }) => (
// jsx
<div class="flex flex-row justify-between">
<h4 id={titleId} class={titleClass}>
自定义头部
</h4>
<el-button type="danger" onClick={close}>
关闭
</el-button>
</div>
),
contentRenderer: () => <p>弹框内容-自定义头部</p>
});
}
function onFooterRendererClick() {
addDialog({
title: "自定义底部",
footerRenderer: ({ options, index }) => (
<el-button onClick={() => closeDialog(options, index)}>
{options.title}-{index}
</el-button>
),
contentRenderer: () => <p>弹框内容-自定义底部</p>
});
}
function onFooterButtonsClick() {
addDialog({
title: "自定义底部按钮",
footerButtons: [
{
label: "按钮1",
size: "small",
type: "success",
btnClick: ({ dialog: { options, index }, button }) => {
console.log(options, index, button);
closeDialog(options, index);
}
},
{
label: "按钮2",
text: true,
bg: true,
btnClick: ({ dialog: { options, index }, button }) => {
console.log(options, index, button);
closeDialog(options, index);
}
},
{
label: "按钮3",
size: "large",
type: "warning",
btnClick: ({ dialog: { options, index }, button }) => {
console.log(options, index, button);
closeDialog(options, index);
}
}
],
contentRenderer: () => <p>弹框内容-自定义底部按钮</p>
});
}
function onOpenClick() {
addDialog({
title: "打开后的回调",
open: ({ options, index }) => message({ options, index } as any),
contentRenderer: () => <p>弹框内容-打开后的回调</p>
});
}
function onCloseCallBackClick() {
addDialog({
title: "关闭后的回调",
closeCallBack: ({ options, index, args }) => {
console.log(options, index, args);
let text = "";
if (args?.command === "cancel") {
text = "您点击了取消按钮";
} else if (args?.command === "sure") {
text = "您点击了确定按钮";
} else {
text = "您点击了右上角关闭按钮或者空白页";
}
message(text);
},
contentRenderer: () => <p>弹框内容-关闭后的回调</p>
});
}
function onNestingClick() {
addDialog({
title: "嵌套的弹框",
contentRenderer: ({ index }) => (
<el-button
onClick={() =>
addDialog({
title: `${index + 1}个子弹框`,
width: "40%",
contentRenderer: ({ index }) => (
<el-button
onClick={() =>
addDialog({
title: `${index + 1}个子弹框`,
width: "30%",
contentRenderer: () => (
<>
<el-button round onClick={() => closeAllDialog()}>
哎呦你干嘛赶快关闭所有弹框
</el-button>
</>
)
})
}
>
点击打开第{index + 1}个子弹框
</el-button>
)
})
}
>
点击打开第{index + 1}个子弹框
</el-button>
)
});
}
</script>
<template>
<el-card shadow="never">
<template #header>
<div class="card-header">
<span class="font-medium">
二次封装 element-plus
<el-link
href="https://element-plus.org/zh-CN/component/dialog.html"
target="_blank"
style="margin: 0 4px 5px; font-size: 16px"
>
Dialog
</el-link>
采用函数式调用弹框组件
</span>
</div>
</template>
<el-space wrap>
<el-button @click="onBaseClick"> 基本使用 </el-button>
<el-button @click="onDraggableClick"> 可拖拽 </el-button>
<el-button @click="onFullscreenClick"> 全屏 </el-button>
<el-button @click="onModalClick"> 无背景遮罩层 </el-button>
<el-button @click="onStyleClick"> 自定义弹出位置 </el-button>
<el-button @click="onoOpenDelayClick"> 延时2秒打开弹框 </el-button>
<el-button @click="onCloseDelayClick"> 延时2秒关闭弹框 </el-button>
<el-button @click="onShowCloseClick">
不显示右上角关闭按钮图标
</el-button>
<el-button @click="onBeforeCloseClick"> 禁止通过键盘ESC关闭 </el-button>
<el-button @click="onCloseOnClickModalClick">
禁止通过点击modal关闭
</el-button>
<el-button @click="onHideFooterClick"> 隐藏底部取消确定按钮 </el-button>
<el-button @click="onHeaderRendererClick"> 自定义头部 </el-button>
<el-button @click="onFooterRendererClick"> 自定义底部 </el-button>
<el-button @click="onFooterButtonsClick"> 自定义底部按钮 </el-button>
<el-button @click="onOpenClick"> 打开后的回调 </el-button>
<el-button @click="onCloseCallBackClick"> 关闭后的回调 </el-button>
<el-button @click="onNestingClick"> 嵌套的弹框 </el-button>
</el-space>
</el-card>
</template>

View File

@ -65,7 +65,7 @@ onMounted(() => {
href="https://sortablejs.github.io/vue.draggable.next/#/simple" href="https://sortablejs.github.io/vue.draggable.next/#/simple"
target="_blank" target="_blank"
:icon="useRenderIcon(Rank)" :icon="useRenderIcon(Rank)"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
vuedraggable vuedraggable
</el-link> </el-link>
@ -150,35 +150,36 @@ onMounted(() => {
/* grid列表拖拽 */ /* grid列表拖拽 */
.grid-container { .grid-container {
display: grid; display: grid;
grid-template-columns: 33.3% 33.3% 33.3%;
grid-template-rows: 33.3% 33.3% 33.3%; grid-template-rows: 33.3% 33.3% 33.3%;
grid-template-columns: 33.3% 33.3% 33.3%;
} }
.item-single { .item-single {
font-size: 1.5em;
height: 77px; height: 77px;
text-align: center; font-size: 1.5em;
line-height: 85px; line-height: 85px;
border: 1px solid #e5e4e9; text-align: center;
cursor: move; cursor: move;
border: 1px solid #e5e4e9;
} }
.item-cut { .item-cut {
font-size: 1.5em;
height: 77px; height: 77px;
font-size: 1.5em;
line-height: 77px; line-height: 77px;
text-align: center; text-align: center;
border: 1px solid #e5e4e9;
cursor: move; cursor: move;
border: 1px solid #e5e4e9;
} }
.item { .item {
font-size: 2em; font-size: 2em;
text-align: center;
line-height: 100px; line-height: 100px;
border: 1px solid #e5e4e9; text-align: center;
cursor: move; cursor: move;
@media screen and (max-width: 750px) { border: 1px solid #e5e4e9;
@media screen and (width <= 750px) {
line-height: 90px; line-height: 90px;
} }
} }

View File

@ -80,7 +80,7 @@ watch(
<el-link <el-link
href="https://github.com/leezng/vue-json-pretty" href="https://github.com/leezng/vue-json-pretty"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
vue-json-pretty vue-json-pretty
</el-link> </el-link>
@ -91,7 +91,7 @@ watch(
<el-link <el-link
href="https://github.com/surmon-china/vue-codemirror" href="https://github.com/surmon-china/vue-codemirror"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
codemirror6 codemirror6
</el-link> </el-link>

View File

@ -125,8 +125,8 @@ function changeDirection(val) {
<style lang="scss" scoped> <style lang="scss" scoped>
.card-header { .card-header {
display: flex; display: flex;
justify-content: space-between;
align-items: center; align-items: center;
justify-content: space-between;
span { span {
margin-right: 20px; margin-right: 20px;
@ -134,23 +134,23 @@ function changeDirection(val) {
} }
.warp { .warp {
height: 270px;
width: 360px; width: 360px;
height: 270px;
margin: 0 auto; margin: 0 auto;
overflow: hidden; overflow: hidden;
ul { ul {
list-style: none;
padding: 0; padding: 0;
margin: 0 auto; margin: 0 auto;
list-style: none;
li, li,
a { a {
height: 30px;
line-height: 30px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
height: 30px;
font-size: 15px; font-size: 15px;
line-height: 30px;
} }
} }
} }

View File

@ -57,9 +57,9 @@ $H: 70vh;
.split-pane { .split-pane {
width: 70vw; width: 70vw;
height: $H; height: $H;
text-align: center;
font-size: 50px; font-size: 50px;
color: #fff; color: #fff;
text-align: center;
border: 1px solid #e5e6eb; border: 1px solid #e5e6eb;
.dv-a, .dv-a,
@ -67,8 +67,8 @@ $H: 70vh;
.dv-c { .dv-c {
width: $W; width: $W;
height: $W; height: $W;
color: rgba($color: dodgerblue, $alpha: 0.8);
line-height: $H; line-height: $H;
color: rgba($color: dodgerblue, $alpha: 80%);
} }
.dv-b, .dv-b,
@ -77,11 +77,11 @@ $H: 70vh;
} }
.dv-b { .dv-b {
color: rgba($color: #000, $alpha: 0.8); color: rgba($color: #000, $alpha: 80%);
} }
.dv-c { .dv-c {
color: rgba($color: #ce272d, $alpha: 0.8); color: rgba($color: #ce272d, $alpha: 80%);
} }
} }
</style> </style>

View File

@ -1,13 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted } from "vue"; import { onMounted } from "vue";
import Player from "xgplayer/dist/simple_player";
import volume from "xgplayer/es/controls/volume";
import { deviceDetection } from "@pureadmin/utils"; import { deviceDetection } from "@pureadmin/utils";
import screenShot from "xgplayer/es/controls/screenShot";
import playbackRate from "xgplayer/es/controls/playbackRate";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import VideoPlay from "@iconify-icons/ep/video-play"; import VideoPlay from "@iconify-icons/ep/video-play";
import Player from "xgplayer";
import "xgplayer/dist/index.min.css";
defineOptions({ defineOptions({
name: "Video" name: "Video"
}); });
@ -15,15 +14,18 @@ defineOptions({
onMounted(() => { onMounted(() => {
new Player({ new Player({
id: "mse", id: "mse",
lang: "zh",
// //
volume: 0, volume: 0,
autoplay: false, autoplay: false,
screenShot: true, screenShot: true,
videoAttributes: {
crossOrigin: "anonymous"
},
url: "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4", url: "https://sf1-hscdn-tos.pstatp.com/obj/media-fe/xgplayer_doc_video/mp4/xgplayer-demo-720p.mp4",
poster: poster:
"https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg", "https://s2.pstatp.com/cdn/expire-1-M/byted-player-videos/1.0.0/poster.jpg",
fluid: deviceDetection(), fluid: deviceDetection(),
controlPlugins: [volume, playbackRate, screenShot],
// //
playbackRate: [0.5, 0.75, 1, 1.5, 2] playbackRate: [0.5, 0.75, 1, 1.5, 2]
}); });
@ -37,10 +39,10 @@ onMounted(() => {
<span class="font-medium"> <span class="font-medium">
视频组件采用开源的 视频组件采用开源的
<el-link <el-link
href="https://v2.h5player.bytedance.com" href="https://v3.h5player.bytedance.com/"
target="_blank" target="_blank"
:icon="useRenderIcon(VideoPlay)" :icon="useRenderIcon(VideoPlay)"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
西瓜播放器 西瓜播放器
</el-link> </el-link>

View File

@ -48,7 +48,7 @@ const handleCreated = editor => {
href="https://www.wangeditor.com" href="https://www.wangeditor.com"
target="_blank" target="_blank"
:icon="useRenderIcon(Edit)" :icon="useRenderIcon(Edit)"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
Wangeditor Wangeditor
</el-link> </el-link>

View File

@ -18,21 +18,21 @@ defineOptions({
<style lang="scss" scoped> <style lang="scss" scoped>
.back { .back {
width: 200px;
height: 200px;
cursor: pointer;
border-radius: 18px;
display: flex;
align-items: center;
justify-content: center;
position: relative; position: relative;
top: 50%; top: 50%;
left: 50%; left: 50%;
display: flex;
align-items: center;
justify-content: center;
width: 200px;
height: 200px;
cursor: pointer;
background: rgb(138 150 160 / 8%);
border-radius: 18px;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
background: rgba(138, 150, 160, 0.08);
&:hover { &:hover {
background: rgba(138, 150, 160, 0.2); background: rgb(138 150 160 / 20%);
transition: background 0.6s; transition: background 0.6s;
} }
} }

View File

@ -40,7 +40,7 @@ function initLf() {
LogicFlow.use(Menu); LogicFlow.use(Menu);
const domLf = new LogicFlow({ const domLf = new LogicFlow({
...unref(config), ...unref(config),
container: document.querySelector("#LF-Turbo") container: document.querySelector("#turbo")
}); });
lf.value = domLf; lf.value = domLf;
// bpmn:sequenceFlow // bpmn:sequenceFlow
@ -74,7 +74,7 @@ onMounted(() => {
href="http://logic-flow.org/" href="http://logic-flow.org/"
target="_blank" target="_blank"
:icon="useRenderIcon(SetUp)" :icon="useRenderIcon(SetUp)"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
LogicFlow LogicFlow
</el-link> </el-link>
@ -93,7 +93,7 @@ onMounted(() => {
<!-- 节点面板 --> <!-- 节点面板 -->
<NodePanel :lf="lf" :nodeList="nodeList" /> <NodePanel :lf="lf" :nodeList="nodeList" />
<!-- 画布 --> <!-- 画布 -->
<div id="LF-Turbo" /> <div id="turbo" />
<!-- 数据查看面板 --> <!-- 数据查看面板 -->
<el-dialog <el-dialog
class="flow-dialog" class="flow-dialog"
@ -110,19 +110,19 @@ onMounted(() => {
</template> </template>
<style scoped> <style scoped>
#LF-Turbo { #turbo {
width: 100%; width: 100%;
height: 70vh; height: 70vh;
} }
.logic-flow-view { .logic-flow-view {
margin: 10px;
position: relative; position: relative;
margin: 10px;
} }
.demo-title { .demo-title {
text-align: center;
margin: 20px; margin: 20px;
text-align: center;
} }
.demo-control { .demo-control {
@ -139,23 +139,23 @@ onMounted(() => {
.add-panel { .add-panel {
position: absolute; position: absolute;
z-index: 11; z-index: 11;
background-color: white;
padding: 10px 5px; padding: 10px 5px;
background-color: white;
} }
.el-drawer__body { .el-drawer__body {
height: 80%;
overflow: auto;
margin-top: -30px;
z-index: 3; z-index: 3;
height: 80%;
margin-top: -30px;
overflow: auto;
} }
:deep(.flow-dialog) { :deep(.flow-dialog) {
transform: none;
left: 0;
top: 5vh;
position: relative; position: relative;
top: 5vh;
left: 0;
margin: 0 auto; margin: 0 auto;
transform: none;
} }
:deep(.flow-dialog) .el-dialog__body { :deep(.flow-dialog) .el-dialog__body {

View File

@ -94,25 +94,25 @@ const cardLogoClass = computed(() => [
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-bottom: 12px; margin-bottom: 12px;
border-radius: 3px;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
border-radius: 3px;
&_detail { &_detail {
flex: 1; flex: 1;
padding: 24px 32px;
min-height: 140px; min-height: 140px;
padding: 24px 32px;
&--logo { &--logo {
display: flex;
align-items: center;
justify-content: center;
width: 56px; width: 56px;
height: 56px; height: 56px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
background: #e0ebff;
font-size: 32px; font-size: 32px;
color: #0052d9; color: #0052d9;
background: #e0ebff;
border-radius: 50%;
&__disabled { &__disabled {
color: #a1c4ff; color: #a1c4ff;
@ -129,21 +129,21 @@ const cardLogoClass = computed(() => [
} }
&--name { &--name {
margin: 24px 0 8px 0; margin: 24px 0 8px;
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
} }
&--desc { &--desc {
display: -webkit-box;
height: 40px;
margin-bottom: 24px;
overflow: hidden;
font-size: 12px; font-size: 12px;
line-height: 20px; line-height: 20px;
overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
margin-bottom: 24px;
height: 40px;
} }
} }

View File

@ -18,13 +18,13 @@ import TypeIt from "@/components/ReTypeit";
import qrCode from "./components/qrCode.vue"; import qrCode from "./components/qrCode.vue";
import regist from "./components/regist.vue"; import regist from "./components/regist.vue";
import update from "./components/update.vue"; import update from "./components/update.vue";
import { initRouter } from "@/router/utils";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import type { FormInstance } from "element-plus"; import type { FormInstance } from "element-plus";
import { $t, transformI18n } from "@/plugins/i18n"; import { $t, transformI18n } from "@/plugins/i18n";
import { operates, thirdParty } from "./utils/enums"; import { operates, thirdParty } from "./utils/enums";
import { useLayout } from "@/layout/hooks/useLayout"; import { useLayout } from "@/layout/hooks/useLayout";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
import { initRouter, getTopMenu } from "@/router/utils";
import { bg, avatar, illustration } from "./utils/static"; import { bg, avatar, illustration } from "./utils/static";
import { ReImageVerify } from "@/components/ReImageVerify"; import { ReImageVerify } from "@/components/ReImageVerify";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
@ -76,7 +76,7 @@ const onLogin = async (formEl: FormInstance | undefined) => {
if (res.success) { if (res.success) {
// //
initRouter().then(() => { initRouter().then(() => {
router.push("/"); router.push(getTopMenu(true).path);
message("登录成功", { type: "success" }); message("登录成功", { type: "success" });
}); });
} }

View File

@ -22,7 +22,7 @@ function tabClick({ index }) {
<el-link <el-link
href="https://github.com/pure-admin/pure-admin-table" href="https://github.com/pure-admin/pure-admin-table"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
@pureadmin/table @pureadmin/table
</el-link> </el-link>

View File

@ -24,7 +24,7 @@ function tabClick({ index }) {
<el-link <el-link
href="https://github.com/pure-admin/pure-admin-table" href="https://github.com/pure-admin/pure-admin-table"
target="_blank" target="_blank"
style="font-size: 16px; margin: 0 4px 5px" style="margin: 0 4px 5px; font-size: 16px"
> >
@pureadmin/table @pureadmin/table
</el-link> </el-link>

View File

@ -58,7 +58,7 @@ const columns = [
role="img" role="img"
aria-label="dingding" aria-label="dingding"
class="anticon anticon-dingding flex items-center cursor-pointer" class="anticon anticon-dingding flex items-center cursor-pointer"
style="color: rgb(0, 160, 233); margin-left: 8px" style="margin-left: 8px; color: rgb(0 160 233)"
> >
<svg <svg
viewBox="64 64 896 896" viewBox="64 64 896 896"

View File

@ -281,6 +281,6 @@ axios
} }
.main-content { .main-content {
margin: 20px 20px 0 20px !important; margin: 20px 20px 0 !important;
} }
</style> </style>

View File

@ -1,20 +1,39 @@
module.exports = { module.exports = {
root: true, root: true,
plugins: ["stylelint-order"], extends: [
customSyntax: "postcss-html", "stylelint-config-standard",
extends: ["stylelint-config-standard", "stylelint-config-prettier"], "stylelint-config-html/vue",
"stylelint-config-recess-order"
],
plugins: ["stylelint-order", "stylelint-prettier", "stylelint-scss"],
overrides: [
{
files: ["**/*.(css|html|vue)"],
customSyntax: "postcss-html"
},
{
files: ["*.scss", "**/*.scss"],
customSyntax: "postcss-scss",
extends: [
"stylelint-config-standard-scss",
"stylelint-config-recommended-vue/scss"
]
}
],
rules: { rules: {
"selector-class-pattern": null, "selector-class-pattern": null,
"no-descending-specificity": null,
"scss/dollar-variable-pattern": null,
"selector-pseudo-class-no-unknown": [ "selector-pseudo-class-no-unknown": [
true, true,
{ {
ignorePseudoClasses: ["global"] ignorePseudoClasses: ["deep", "global"]
} }
], ],
"selector-pseudo-element-no-unknown": [ "selector-pseudo-element-no-unknown": [
true, true,
{ {
ignorePseudoElements: ["v-deep"] ignorePseudoElements: ["v-deep", "v-global", "v-slotted"]
} }
], ],
"at-rule-no-unknown": [ "at-rule-no-unknown": [
@ -30,17 +49,11 @@ module.exports = {
"if", "if",
"each", "each",
"include", "include",
"mixin" "mixin",
"use"
] ]
} }
], ],
"no-empty-source": null,
"named-grid-areas-no-invalid": null,
"unicode-bom": "never",
"no-descending-specificity": null,
"font-family-no-missing-generic-family-keyword": null,
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"rule-empty-line-before": [ "rule-empty-line-before": [
"always", "always",
{ {
@ -67,26 +80,5 @@ module.exports = {
{ severity: "warning" } { severity: "warning" }
] ]
}, },
ignoreFiles: ["**/*.js", "**/*.jsx", "**/*.tsx", "**/*.ts", "**/*.json"], ignoreFiles: ["**/*.js", "**/*.ts", "**/*.jsx", "**/*.tsx"]
overrides: [
{
files: ["*.vue", "**/*.vue", "*.html", "**/*.html"],
extends: ["stylelint-config-recommended", "stylelint-config-html"],
rules: {
"keyframes-name-pattern": null,
"selector-pseudo-class-no-unknown": [
true,
{
ignorePseudoClasses: ["deep", "global"]
} }
],
"selector-pseudo-element-no-unknown": [
true,
{
ignorePseudoElements: ["v-deep", "v-global", "v-slotted"]
}
]
}
}
]
};

Some files were not shown because too many files have changed in this diff Show More