Compare commits

..

3 Commits
v5.8.0 ... main

Author SHA1 Message Date
xiaoxian521
d2543cf9d3 release: update 6.0.0 2025-04-25 09:50:52 +08:00
xiaoxian521
13a36c0acb release: update 5.9.0 2024-12-15 10:45:15 +08:00
xiaoxian521
85e5074665 chore: update 2024-08-20 16:04:50 +08:00
91 changed files with 3722 additions and 4070 deletions

2
.nvmrc
View File

@ -1 +1 @@
v20.15.0 v22.14.0

View File

@ -36,6 +36,7 @@
"v-copy", "v-copy",
"v-longpress", "v-longpress",
"v-optimize", "v-optimize",
"v-perms",
"v-ripple" "v-ripple"
], ],
"vscodeCustomCodeColor.highlightValueColor": "#b392f0", "vscodeCustomCodeColor.highlightValueColor": "#b392f0",

View File

@ -10,11 +10,11 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
## `js` version ## `js` version
[Click me to view js version](https://pure-admin.github.io/pure-admin-doc/pages/js/) [Click me to view js version](https://pure-admin.cn/pages/js/)
## `max` version ## `max` version
[Click me to view the max version](https://github.com/pure-admin/vue-pure-admin-max) [Click me to view the max version](https://pure-admin.cn/pages/max/)
## Supporting video ## Supporting video
@ -23,12 +23,12 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
## Nanny-level documents ## Nanny-level documents
[Click me to view vue-pure-admin documentation](https://pure-admin.github.io/pure-admin-doc) [Click me to view vue-pure-admin documentation](https://pure-admin.cn/)
[Click me to view @pureadmin/utils documentation](https://pure-admin-utils.netlify.app) [Click me to view @pureadmin/utils documentation](https://pure-admin-utils.netlify.app)
## Quality service, software outsourcing, sponsorship support ## Quality service, software outsourcing, sponsorship support
[Click me to view details](https://pure-admin.github.io/pure-admin-doc/pages/service/) [Click me to view details](https://pure-admin.cn/pages/service/)
## Preview ## Preview

View File

@ -14,11 +14,11 @@
## `js` 版本 ## `js` 版本
[点我查看 js 版本](https://pure-admin.github.io/pure-admin-doc/pages/js/) [点我查看 js 版本](https://pure-admin.cn/pages/js/)
## `max` 版本 ## `max` 版本
[点我查看 max 版本](https://github.com/pure-admin/vue-pure-admin-max) [点我查看 max 版本](https://pure-admin.cn/pages/max/)
## 配套视频 ## 配套视频
@ -27,12 +27,12 @@
## 配套保姆级文档 ## 配套保姆级文档
[点我查看 vue-pure-admin 文档](https://pure-admin.github.io/pure-admin-doc) [点我查看 vue-pure-admin 文档](https://pure-admin.cn/)
[点我查看 @pureadmin/utils 文档](https://pure-admin-utils.netlify.app) [点我查看 @pureadmin/utils 文档](https://pure-admin-utils.netlify.app)
## 优质服务、软件外包、赞助支持 ## 优质服务、软件外包、赞助支持
[点我查看详情](https://pure-admin.github.io/pure-admin-doc/pages/service/) [点我查看详情](https://pure-admin.cn/pages/service/)
## 预览 ## 预览

View File

@ -1,13 +1,13 @@
import type { Plugin } from "vite"; import type { Plugin } from "vite";
import gradient from "gradient-string";
import { getPackageSize } from "./utils"; import { getPackageSize } from "./utils";
import dayjs, { type Dayjs } from "dayjs"; import dayjs, { type Dayjs } from "dayjs";
import duration from "dayjs/plugin/duration"; import duration from "dayjs/plugin/duration";
import gradientString from "gradient-string";
import boxen, { type Options as BoxenOptions } from "boxen"; import boxen, { type Options as BoxenOptions } from "boxen";
dayjs.extend(duration); dayjs.extend(duration);
const welcomeMessage = gradientString("cyan", "magenta").multiline( const welcomeMessage = gradient(["cyan", "magenta"]).multiline(
`您好! 欢迎使用 pure-admin 开源项目\n我们为您精心准备了下面两个贴心的保姆级文档\nhttps://pure-admin.github.io/pure-admin-doc\nhttps://pure-admin-utils.netlify.app` `您好! 欢迎使用 pure-admin 开源项目\n我们为您精心准备了下面两个贴心的保姆级文档\nhttps://pure-admin.cn\nhttps://pure-admin-utils.netlify.app`
); );
const boxenOptions: BoxenOptions = { const boxenOptions: BoxenOptions = {
@ -41,7 +41,7 @@ export function viteBuildInfo(): Plugin {
callback: (size: string) => { callback: (size: string) => {
console.log( console.log(
boxen( boxen(
gradientString("cyan", "magenta").multiline( gradient(["cyan", "magenta"]).multiline(
`🎉 恭喜打包完成(总用时${dayjs `🎉 恭喜打包完成(总用时${dayjs
.duration(endTime.diff(startTime)) .duration(endTime.diff(startTime))
.format("mm分ss秒")}${size}` .format("mm分ss秒")}${size}`

View File

@ -22,12 +22,8 @@ const include = [
/** /**
* *
* `@iconify-icons/` `exclude` 使 * 使
*/ */
const exclude = [ const exclude = ["@iconify/json"];
"@iconify-icons/ep",
"@iconify-icons/ri",
"@pureadmin/theme/dist/browser-utils"
];
export { include, exclude }; export { include, exclude };

View File

@ -1,18 +1,16 @@
import { cdn } from "./cdn"; import { cdn } from "./cdn";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import { pathResolve } from "./utils";
import { viteBuildInfo } from "./info"; import { viteBuildInfo } from "./info";
import svgLoader from "vite-svg-loader"; import svgLoader from "vite-svg-loader";
import Icons from "unplugin-icons/vite";
import type { PluginOption } from "vite"; import type { PluginOption } from "vite";
import checker from "vite-plugin-checker";
import vueJsx from "@vitejs/plugin-vue-jsx"; import vueJsx from "@vitejs/plugin-vue-jsx";
import Inspector from "vite-plugin-vue-inspector"; import tailwindcss from "@tailwindcss/vite";
import { configCompressPlugin } from "./compress"; import { configCompressPlugin } from "./compress";
import removeNoMatch from "vite-plugin-router-warn"; import removeNoMatch from "vite-plugin-router-warn";
import { visualizer } from "rollup-plugin-visualizer"; import { visualizer } from "rollup-plugin-visualizer";
import removeConsole from "vite-plugin-remove-console"; import removeConsole from "vite-plugin-remove-console";
import { themePreprocessorPlugin } from "@pureadmin/theme"; import { codeInspectorPlugin } from "code-inspector-plugin";
import { genScssMultipleScopeVars } from "../src/layout/theme";
import { vitePluginFakeServer } from "vite-plugin-fake-server"; import { vitePluginFakeServer } from "vite-plugin-fake-server";
export function getPluginsList( export function getPluginsList(
@ -21,21 +19,20 @@ export function getPluginsList(
): PluginOption[] { ): PluginOption[] {
const lifecycle = process.env.npm_lifecycle_event; const lifecycle = process.env.npm_lifecycle_event;
return [ return [
tailwindcss(),
vue(), vue(),
// jsx、tsx语法支持 // jsx、tsx语法支持
vueJsx(), vueJsx(),
checker({ /**
typescript: true, * DOM IDE
vueTsc: true, * Mac Option + Shift
eslint: { * Windows Alt + Shift
lintCommand: `eslint ${pathResolve("../{src,mock,build}/**/*.{vue,js,ts,tsx}")}`, * https://inspector.fe-dev.cn/guide/start.html
useFlatConfig: true */
}, codeInspectorPlugin({
terminal: false, bundler: "vite",
enableBuild: false hideConsole: true
}), }),
// 按下Command(⌘)+Shift(⇧)然后点击页面元素会自动打开本地IDE并跳转到对应的代码位置
Inspector(),
viteBuildInfo(), viteBuildInfo(),
/** /**
* vue-router动态路由警告No match found for location with path * vue-router动态路由警告No match found for location with path
@ -50,15 +47,13 @@ export function getPluginsList(
infixName: false, infixName: false,
enableProd: true enableProd: true
}), }),
// 自定义主题
themePreprocessorPlugin({
scss: {
multipleScopeVars: genScssMultipleScopeVars(),
extract: true
}
}),
// svg组件化支持 // svg组件化支持
svgLoader(), svgLoader(),
// 自动按需加载图标
Icons({
compiler: "vue3",
scale: 1
}),
VITE_CDN ? cdn : null, VITE_CDN ? cdn : null,
configCompressPlugin(VITE_COMPRESSION), configCompressPlugin(VITE_COMPRESSION),
// 线上环境删除console // 线上环境删除console

View File

@ -1,26 +1,25 @@
import js from "@eslint/js"; import js from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginVue from "eslint-plugin-vue"; import pluginVue from "eslint-plugin-vue";
import * as parserVue from "vue-eslint-parser"; import * as parserVue from "vue-eslint-parser";
import configPrettier from "eslint-config-prettier"; import configPrettier from "eslint-config-prettier";
import pluginPrettier from "eslint-plugin-prettier"; import pluginPrettier from "eslint-plugin-prettier";
import { defineFlatConfig } from "eslint-define-config"; import { defineConfig, globalIgnores } from "eslint/config";
import * as parserTypeScript from "@typescript-eslint/parser";
import pluginTypeScript from "@typescript-eslint/eslint-plugin";
export default defineFlatConfig([ export default defineConfig([
{ globalIgnores([
...js.configs.recommended,
ignores: [
"**/.*", "**/.*",
"dist/*", "dist/*",
"*.d.ts", "*.d.ts",
"public/*", "public/*",
"src/assets/**", "src/assets/**",
"src/**/iconfont/**" "src/**/iconfont/**"
], ]),
{
...js.configs.recommended,
languageOptions: { languageOptions: {
globals: { globals: {
// index.d.ts // types/index.d.ts
RefType: "readonly", RefType: "readonly",
EmitType: "readonly", EmitType: "readonly",
TargetContext: "readonly", TargetContext: "readonly",
@ -73,26 +72,18 @@ export default defineFlatConfig([
] ]
} }
}, },
{ ...tseslint.config({
extends: [...tseslint.configs.recommended],
files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"], files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"],
languageOptions: {
parser: parserTypeScript,
parserOptions: {
sourceType: "module"
}
},
plugins: {
"@typescript-eslint": pluginTypeScript
},
rules: { rules: {
...pluginTypeScript.configs.strict.rules,
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-redeclare": "error", "@typescript-eslint/no-redeclare": "error",
"@typescript-eslint/ban-ts-comment": "off", "@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/prefer-as-const": "warn", "@typescript-eslint/prefer-as-const": "warn",
"@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-expressions": "off",
"@typescript-eslint/no-unsafe-function-type": "off",
"@typescript-eslint/no-import-type-side-effects": "error", "@typescript-eslint/no-import-type-side-effects": "error",
"@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/consistent-type-imports": [ "@typescript-eslint/consistent-type-imports": [
@ -111,20 +102,20 @@ export default defineFlatConfig([
} }
] ]
} }
}, }),
{ {
files: ["**/*.d.ts"], files: ["**/*.d.ts"],
rules: { rules: {
"eslint-comments/no-unlimited-disable": "off", "eslint-comments/no-unlimited-disable": "off",
"import/no-duplicates": "off", "import/no-duplicates": "off",
"no-restricted-syntax": "off",
"unused-imports/no-unused-vars": "off" "unused-imports/no-unused-vars": "off"
} }
}, },
{ {
files: ["**/*.?([cm])js"], files: ["**/*.?([cm])js"],
rules: { rules: {
"@typescript-eslint/no-require-imports": "off", "@typescript-eslint/no-require-imports": "off"
"@typescript-eslint/no-var-requires": "off"
} }
}, },
{ {
@ -145,18 +136,19 @@ export default defineFlatConfig([
jsx: true jsx: true
}, },
extraFileExtensions: [".vue"], extraFileExtensions: [".vue"],
parser: "@typescript-eslint/parser", parser: tseslint.parser,
sourceType: "module" sourceType: "module"
} }
}, },
plugins: { plugins: {
"@typescript-eslint": tseslint.plugin,
vue: pluginVue vue: pluginVue
}, },
processor: pluginVue.processors[".vue"], processor: pluginVue.processors[".vue"],
rules: { rules: {
...pluginVue.configs.base.rules, ...pluginVue.configs.base.rules,
...pluginVue.configs["vue3-essential"].rules, ...pluginVue.configs.essential.rules,
...pluginVue.configs["vue3-recommended"].rules, ...pluginVue.configs.recommended.rules,
"no-undef": "off", "no-undef": "off",
"no-unused-vars": "off", "no-unused-vars": "off",
"vue/no-v-html": "off", "vue/no-v-html": "off",

View File

@ -1,4 +1,4 @@
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
@ -10,9 +10,6 @@
/> />
<title>pure-admin-thin</title> <title>pure-admin-thin</title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<script>
window.process = {};
</script>
</head> </head>
<body> <body>

View File

@ -1,6 +1,6 @@
{ {
"name": "pure-admin-thin", "name": "pure-admin-thin",
"version": "5.8.0", "version": "6.0.0",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@ -49,97 +49,94 @@
}, },
"dependencies": { "dependencies": {
"@pureadmin/descriptions": "^1.2.1", "@pureadmin/descriptions": "^1.2.1",
"@pureadmin/table": "^3.2.0", "@pureadmin/table": "^3.2.1",
"@pureadmin/utils": "^2.4.8", "@pureadmin/utils": "^2.6.0",
"@vueuse/core": "^10.11.1", "@vueuse/core": "^13.1.0",
"@vueuse/motion": "^2.2.3", "@vueuse/motion": "^3.0.3",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.7.4", "axios": "^1.9.0",
"dayjs": "^1.11.12", "dayjs": "^1.11.13",
"echarts": "^5.5.1", "echarts": "^5.6.0",
"element-plus": "^2.8.0", "element-plus": "^2.9.8",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path": "^0.12.7", "path-browserify": "^1.0.1",
"pinia": "^2.2.2", "pinia": "^3.0.2",
"pinyin-pro": "^3.24.2", "pinyin-pro": "^3.26.0",
"qs": "^6.13.0", "qs": "^6.14.0",
"responsive-storage": "^2.2.0", "responsive-storage": "^2.2.0",
"sortablejs": "^1.15.2", "sortablejs": "^1.15.6",
"vue": "^3.4.38", "vue": "^3.5.13",
"vue-router": "^4.4.3", "vue-router": "^4.5.0",
"vue-tippy": "^6.4.4", "vue-tippy": "^6.7.0",
"vue-types": "^5.1.3" "vue-types": "^6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^19.4.0", "@commitlint/cli": "^19.8.0",
"@commitlint/config-conventional": "^19.2.2", "@commitlint/config-conventional": "^19.8.0",
"@commitlint/types": "^19.0.3", "@commitlint/types": "^19.8.0",
"@eslint/js": "^9.9.0", "@eslint/js": "^9.25.1",
"@faker-js/faker": "^8.4.1", "@faker-js/faker": "^9.7.0",
"@iconify-icons/ep": "^1.2.12", "@iconify/json": "^2.2.331",
"@iconify-icons/ri": "^1.2.10", "@iconify/vue": "4.2.0",
"@iconify/vue": "^4.1.2", "@tailwindcss/vite": "^4.1.4",
"@pureadmin/theme": "^3.2.0",
"@types/gradient-string": "^1.1.6",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/node": "^20.16.1", "@types/node": "^20.17.30",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/qs": "^6.9.15", "@types/path-browserify": "^1.0.3",
"@types/qs": "^6.9.18",
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^7.18.0", "@vitejs/plugin-vue": "^5.2.3",
"@typescript-eslint/parser": "^7.18.0", "@vitejs/plugin-vue-jsx": "^4.1.2",
"@vitejs/plugin-vue": "^5.1.2", "boxen": "^8.0.1",
"@vitejs/plugin-vue-jsx": "^4.0.1", "code-inspector-plugin": "^0.20.10",
"autoprefixer": "^10.4.20", "cssnano": "^7.0.6",
"boxen": "^7.1.1", "eslint": "^9.25.1",
"cssnano": "^7.0.5", "eslint-config-prettier": "^10.1.2",
"eslint": "^9.9.0", "eslint-plugin-prettier": "^5.2.6",
"eslint-config-prettier": "^9.1.0", "eslint-plugin-vue": "^10.0.0",
"eslint-define-config": "^2.1.0", "gradient-string": "^3.0.0",
"eslint-plugin-prettier": "^5.2.1", "husky": "^9.1.7",
"eslint-plugin-vue": "^9.27.0", "lint-staged": "^15.5.1",
"gradient-string": "^2.0.2", "postcss": "^8.5.3",
"husky": "^9.1.4", "postcss-html": "^1.8.0",
"lint-staged": "^15.2.9", "postcss-load-config": "^6.0.1",
"postcss": "^8.4.41",
"postcss-html": "^1.7.0",
"postcss-import": "^16.1.0",
"postcss-scss": "^4.0.9", "postcss-scss": "^4.0.9",
"prettier": "^3.3.3", "prettier": "^3.5.3",
"rimraf": "^5.0.10", "rimraf": "^6.0.1",
"rollup-plugin-visualizer": "^5.12.0", "rollup-plugin-visualizer": "^5.14.0",
"sass": "^1.77.8", "sass": "^1.87.0",
"stylelint": "^16.8.2", "stylelint": "^16.19.0",
"stylelint-config-recess-order": "^5.0.1", "stylelint-config-recess-order": "^6.0.0",
"stylelint-config-recommended-vue": "^1.5.0", "stylelint-config-recommended-vue": "^1.6.0",
"stylelint-config-standard-scss": "^13.1.0", "stylelint-config-standard-scss": "^14.0.0",
"stylelint-prettier": "^5.0.2", "stylelint-prettier": "^5.0.3",
"svgo": "^3.3.2", "svgo": "^3.3.2",
"tailwindcss": "^3.4.10", "tailwindcss": "^4.1.4",
"typescript": "^5.5.4", "typescript": "^5.8.3",
"vite": "^5.4.1", "typescript-eslint": "^8.31.0",
"unplugin-icons": "^22.1.0",
"vite": "^6.3.3",
"vite-plugin-cdn-import": "^1.0.1", "vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-checker": "^0.7.2",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-fake-server": "^2.1.1", "vite-plugin-fake-server": "^2.2.0",
"vite-plugin-remove-console": "^2.2.0", "vite-plugin-remove-console": "^2.2.0",
"vite-plugin-router-warn": "^1.0.0", "vite-plugin-router-warn": "^1.0.0",
"vite-plugin-vue-inspector": "^5.1.3",
"vite-svg-loader": "^5.1.0", "vite-svg-loader": "^5.1.0",
"vue-eslint-parser": "^9.4.3", "vue-eslint-parser": "^10.1.3",
"vue-tsc": "^2.0.29" "vue-tsc": "^2.2.10"
}, },
"engines": { "engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0", "node": "^18.18.0 || ^20.9.0 || >=22.0.0",
"pnpm": ">=9" "pnpm": ">=9"
}, },
"pnpm": { "pnpm": {
"allowedDeprecatedVersions": { "allowedDeprecatedVersions": {
"are-we-there-yet": "*", "are-we-there-yet": "*",
"sourcemap-codec": "*", "sourcemap-codec": "*",
"lodash.isequal": "*",
"domexception": "*", "domexception": "*",
"w3c-hr-time": "*", "w3c-hr-time": "*",
"inflight": "*", "inflight": "*",
@ -150,10 +147,13 @@
"abab": "*", "abab": "*",
"glob": "*" "glob": "*"
}, },
"peerDependencyRules": { "onlyBuiltDependencies": [
"allowedVersions": { "@parcel/watcher",
"eslint": "9" "core-js",
} "es5-ext",
} "esbuild",
"typeit",
"vue-demi"
]
} }
} }

6508
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,6 @@
/** @type {import('postcss-load-config').Config} */ /** @type {import('postcss-load-config').Config} */
export default { export default {
plugins: { plugins: {
"postcss-import": {},
"tailwindcss/nesting": {},
tailwindcss: {},
autoprefixer: {},
...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {}) ...(process.env.NODE_ENV === "production" ? { cssnano: {} } : {})
} }
}; };

View File

@ -1,5 +1,5 @@
{ {
"Version": "5.8.0", "Version": "6.0.0",
"Title": "PureAdmin", "Title": "PureAdmin",
"FixedHeader": true, "FixedHeader": true,
"HiddenSideBar": false, "HiddenSideBar": false,

View File

@ -28,8 +28,7 @@
c = document.createElement("div"); c = document.createElement("div");
(c.innerHTML = e._iconfont_svg_string_2208059), (c.innerHTML = e._iconfont_svg_string_2208059),
(c = c.getElementsByTagName("svg")[0]) && (c = c.getElementsByTagName("svg")[0]) &&
(c.setAttribute("aria-hidden", "true"), ((c.style.position = "absolute"),
(c.style.position = "absolute"),
(c.style.width = 0), (c.style.width = 0),
(c.style.height = 0), (c.style.height = 0),
(c.style.overflow = "hidden"), (c.style.overflow = "hidden"),

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="iconify iconify--ant-design" viewBox="0 0 1024 1024"><path fill="currentColor" d="M864 170h-60c-4.4 0-8 3.6-8 8v518H310v-73c0-6.7-7.8-10.5-13-6.3l-141.9 112a8 8 0 0 0 0 12.6l141.9 112c5.3 4.2 13 .4 13-6.3v-75h498c35.3 0 64-28.7 64-64V178c0-4.4-3.6-8-8-8"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" class="iconify iconify--ant-design" viewBox="0 0 1024 1024"><path fill="currentColor" d="M864 170h-60c-4.4 0-8 3.6-8 8v518H310v-73c0-6.7-7.8-10.5-13-6.3l-141.9 112a8 8 0 0 0 0 12.6l141.9 112c5.3 4.2 13 .4 13-6.3v-75h498c35.3 0 64-28.7 64-64V178c0-4.4-3.6-8-8-8"/></svg>

Before

Width:  |  Height:  |  Size: 351 B

After

Width:  |  Height:  |  Size: 332 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" aria-hidden="true" class="re-screen" color="#00000073" viewBox="0 0 16 16"><path fill="currentColor" d="M3.5 4H1V3h2V1h1v2.5zM13 3V1h-1v2.5l.5.5H15V3zm-1 9.5V15h1v-2h2v-1h-2.5zM1 12v1h2v2h1v-2.5l-.5-.5zm11-1.5-.5.5h-7l-.5-.5v-5l.5-.5h7l.5.5zM10 7H6v2h4z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" class="re-screen" color="#00000073" viewBox="0 0 16 16"><path fill="currentColor" d="M3.5 4H1V3h2V1h1v2.5zM13 3V1h-1v2.5l.5.5H15V3zm-1 9.5V15h1v-2h2v-1h-2.5zM1 12v1h2v2h1v-2.5l-.5-.5zm11-1.5-.5.5h-7l-.5-.5v-5l.5-.5h7l.5.5zM10 7H6v2h4z"/></svg>

Before

Width:  |  Height:  |  Size: 327 B

After

Width:  |  Height:  |  Size: 308 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" aria-hidden="true" class="re-screen" color="#00000073" viewBox="0 0 16 16"><path fill="currentColor" d="M3 12h10V4H3zm2-6h6v4H5zM2 6H1V2.5l.5-.5H5v1H2zm13-3.5V6h-1V3h-3V2h3.5zM14 10h1v3.5l-.5.5H11v-1h3zM2 13h3v1H1.5l-.5-.5V10h1z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" class="re-screen" color="#00000073" viewBox="0 0 16 16"><path fill="currentColor" d="M3 12h10V4H3zm2-6h6v4H5zM2 6H1V2.5l.5-.5H5v1H2zm13-3.5V6h-1V3h-3V2h3.5zM14 10h1v3.5l-.5.5H11v-1h3zM2 13h3v1H1.5l-.5-.5V10h1z"/></svg>

Before

Width:  |  Height:  |  Size: 302 B

After

Width:  |  Height:  |  Size: 283 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" aria-hidden="true" class="iconify iconify--mdi" viewBox="0 0 24 24"><path fill="currentColor" d="M1 7h6v2H3v2h4v2H3v2h4v2H1zm10 0h4v2h-4v2h2a2 2 0 0 1 2 2v2c0 1.11-.89 2-2 2H9v-2h4v-2h-2a2 2 0 0 1-2-2V9c0-1.1.9-2 2-2m8 0h2a2 2 0 0 1 2 2v1h-2V9h-2v6h2v-1h2v1c0 1.11-.89 2-2 2h-2a2 2 0 0 1-2-2V9c0-1.1.9-2 2-2"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" class="iconify iconify--mdi" viewBox="0 0 24 24"><path fill="currentColor" d="M1 7h6v2H3v2h4v2H3v2h4v2H1zm10 0h4v2h-4v2h2a2 2 0 0 1 2 2v2c0 1.11-.89 2-2 2H9v-2h4v-2h-2a2 2 0 0 1-2-2V9c0-1.1.9-2 2-2m8 0h2a2 2 0 0 1 2 2v1h-2V9h-2v6h2v-1h2v1c0 1.11-.89 2-2 2h-2a2 2 0 0 1-2-2V9c0-1.1.9-2 2-2"/></svg>

Before

Width:  |  Height:  |  Size: 379 B

After

Width:  |  Height:  |  Size: 360 B

View File

@ -1 +1 @@
<svg width="32" height="32" fill="currentColor" aria-hidden="true" data-icon="holder" viewBox="64 64 896 896"><path d="M300 276.5a56 56 0 1 0 56-97 56 56 0 0 0-56 97m0 284a56 56 0 1 0 56-97 56 56 0 0 0-56 97M640 228a56 56 0 1 0 112 0 56 56 0 0 0-112 0m0 284a56 56 0 1 0 112 0 56 56 0 0 0-112 0M300 844.5a56 56 0 1 0 56-97 56 56 0 0 0-56 97M640 796a56 56 0 1 0 112 0 56 56 0 0 0-112 0"/></svg> <svg width="32" height="32" fill="currentColor" data-icon="holder" viewBox="64 64 896 896"><path d="M300 276.5a56 56 0 1 0 56-97 56 56 0 0 0-56 97m0 284a56 56 0 1 0 56-97 56 56 0 0 0-56 97M640 228a56 56 0 1 0 112 0 56 56 0 0 0-112 0m0 284a56 56 0 1 0 112 0 56 56 0 0 0-112 0M300 844.5a56 56 0 1 0 56-97 56 56 0 0 0-56 97M640 796a56 56 0 1 0 112 0 56 56 0 0 0-112 0"/></svg>

Before

Width:  |  Height:  |  Size: 392 B

After

Width:  |  Height:  |  Size: 373 B

View File

@ -8,8 +8,8 @@ import {
} from "./index"; } from "./index";
import { ref, computed } from "vue"; import { ref, computed } from "vue";
import { isFunction } from "@pureadmin/utils"; import { isFunction } from "@pureadmin/utils";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill"; import Fullscreen from "~icons/ri/fullscreen-fill";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill"; import ExitFullscreen from "~icons/ri/fullscreen-exit-fill";
defineOptions({ defineOptions({
name: "ReDialog" name: "ReDialog"
@ -79,7 +79,7 @@ const fullscreenClass = computed(() => {
"el-dialog__close", "el-dialog__close",
"-translate-x-2", "-translate-x-2",
"cursor-pointer", "cursor-pointer",
"hover:!text-[red]" "hover:text-[red]!"
]; ];
}); });

View File

@ -1,10 +1,10 @@
import type { iconType } from "./types"; import type { iconType } from "./types";
import { h, defineComponent, type Component } from "vue"; import { h, defineComponent, type Component } from "vue";
import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index"; import { FontIcon, IconifyIconOnline, IconifyIconOffline } from "../index";
/** /**
* `iconfont` `svg` `iconify` * `iconfont` `svg` `iconify`
* @see {@link https://pure-admin.github.io/pure-admin-doc/pages/icon/} * @see {@link https://pure-admin.cn/pages/icon/}
* @param icon * @param icon
* @param attrs iconType * @param attrs iconType
* @returns Component * @returns Component
@ -49,10 +49,12 @@ export function useRenderIcon(icon: any, attrs?: iconType): Component {
return defineComponent({ return defineComponent({
name: "Icon", name: "Icon",
render() { render() {
const IconifyIcon = if (!icon) return;
icon && icon.includes(":") ? IconifyIconOnline : IconifyIconOffline; const IconifyIcon = icon.includes(":")
? IconifyIconOnline
: IconifyIconOffline;
return h(IconifyIcon, { return h(IconifyIcon, {
icon: icon, icon,
...attrs ...attrs
}); });
} }

View File

@ -27,8 +27,7 @@ export default defineComponent({
return h( return h(
"svg", "svg",
{ {
class: "icon-svg", class: "icon-svg"
"aria-hidden": true
}, },
{ {
default: () => [ default: () => [

View File

@ -13,10 +13,26 @@ export default defineComponent({
render() { render() {
if (typeof this.icon === "object") addIcon(this.icon, this.icon); if (typeof this.icon === "object") addIcon(this.icon, this.icon);
const attrs = this.$attrs; const attrs = this.$attrs;
if (typeof this.icon === "string") {
return h( return h(
IconifyIcon, IconifyIcon,
{ {
icon: this.icon, icon: this.icon,
"aria-hidden": false,
style: attrs?.style
? Object.assign(attrs.style, { outline: "none" })
: { outline: "none" },
...attrs
},
{
default: () => []
}
);
} else {
return h(
this.icon,
{
"aria-hidden": false,
style: attrs?.style style: attrs?.style
? Object.assign(attrs.style, { outline: "none" }) ? Object.assign(attrs.style, { outline: "none" })
: { outline: "none" }, : { outline: "none" },
@ -27,4 +43,5 @@ export default defineComponent({
} }
); );
} }
}
}); });

View File

@ -17,6 +17,7 @@ export default defineComponent({
IconifyIcon, IconifyIcon,
{ {
icon: `${this.icon}`, icon: `${this.icon}`,
"aria-hidden": false,
style: attrs?.style style: attrs?.style
? Object.assign(attrs.style, { outline: "none" }) ? Object.assign(attrs.style, { outline: "none" })
: { outline: "none" }, : { outline: "none" },

View File

@ -1,14 +1,23 @@
// 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载 // 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载
import { getSvgInfo } from "@pureadmin/utils";
import { addIcon } from "@iconify/vue/dist/offline"; import { addIcon } from "@iconify/vue/dist/offline";
// https://icon-sets.iconify.design/ep/?keyword=ep
import EpHomeFilled from "~icons/ep/home-filled?raw";
// https://icon-sets.iconify.design/ri/?keyword=ri
import RiSearchLine from "~icons/ri/search-line?raw";
import RiInformationLine from "~icons/ri/information-line?raw";
const icons = [
// Element Plus Icon: https://github.com/element-plus/element-plus-icons
["ep/home-filled", EpHomeFilled],
// Remix Icon: https://github.com/Remix-Design/RemixIcon
["ri/search-line", RiSearchLine],
["ri/information-line", RiInformationLine]
];
// 本地菜单图标,后端在路由的 icon 中返回对应的图标字符串并且前端在此处使用 addIcon 添加即可渲染菜单图标 // 本地菜单图标,后端在路由的 icon 中返回对应的图标字符串并且前端在此处使用 addIcon 添加即可渲染菜单图标
// @iconify-icons/ep icons.forEach(([name, icon]) => {
import Lollipop from "@iconify-icons/ep/lollipop"; addIcon(name as string, getSvgInfo(icon as string));
import HomeFilled from "@iconify-icons/ep/home-filled"; });
addIcon("ep:lollipop", Lollipop);
addIcon("ep:home-filled", HomeFilled);
// @iconify-icons/ri
import Search from "@iconify-icons/ri/search-line";
import InformationLine from "@iconify-icons/ri/information-line";
addIcon("ri:search-line", Search);
addIcon("ri:information-line", InformationLine);

View File

@ -17,8 +17,8 @@ import {
getKeyList getKeyList
} from "@pureadmin/utils"; } from "@pureadmin/utils";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill"; import Fullscreen from "~icons/ri/fullscreen-fill";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill"; import ExitFullscreen from "~icons/ri/fullscreen-exit-fill";
import DragIcon from "@/assets/table-bar/drag.svg?component"; import DragIcon from "@/assets/table-bar/drag.svg?component";
import ExpandIcon from "@/assets/table-bar/expand.svg?component"; import ExpandIcon from "@/assets/table-bar/expand.svg?component";
import RefreshIcon from "@/assets/table-bar/refresh.svg?component"; import RefreshIcon from "@/assets/table-bar/refresh.svg?component";
@ -53,7 +53,7 @@ const props = {
export default defineComponent({ export default defineComponent({
name: "PureTableBar", name: "PureTableBar",
props, props,
emits: ["refresh"], emits: ["refresh", "fullscreen"],
setup(props, { emit, slots, attrs }) { setup(props, { emit, slots, attrs }) {
const size = ref("default"); const size = ref("default");
const loading = ref(false); const loading = ref(false);
@ -86,9 +86,9 @@ export default defineComponent({
"text-black", "text-black",
"dark:text-white", "dark:text-white",
"duration-100", "duration-100",
"hover:!text-primary", "hover:text-primary!",
"cursor-pointer", "cursor-pointer",
"outline-none" "outline-hidden"
]; ];
}); });
@ -116,6 +116,11 @@ export default defineComponent({
toggleRowExpansionAll(props.tableRef.data, isExpandAll.value); toggleRowExpansionAll(props.tableRef.data, isExpandAll.value);
} }
function onFullscreen() {
isFullscreen.value = !isFullscreen.value;
emit("fullscreen", isFullscreen.value);
}
function toggleRowExpansionAll(data, isExpansion) { function toggleRowExpansionAll(data, isExpansion) {
data.forEach(item => { data.forEach(item => {
props.tableRef.toggleRowExpansion(item, isExpansion); props.tableRef.toggleRowExpansion(item, isExpansion);
@ -245,12 +250,12 @@ export default defineComponent({
<div <div
{...attrs} {...attrs}
class={[ class={[
"w-[99/100]", "w-full",
"px-2", "px-2",
"pb-2", "pb-2",
"bg-bg_color", "bg-bg_color",
isFullscreen.value isFullscreen.value
? ["!w-full", "!h-full", "z-[2002]", "fixed", "inset-0"] ? ["h-full!", "z-2002", "fixed", "inset-0"]
: "mt-2" : "mt-2"
]} ]}
> >
@ -307,7 +312,7 @@ export default defineComponent({
> >
<div class={[topClass.value]}> <div class={[topClass.value]}>
<el-checkbox <el-checkbox
class="!-mr-1" class="-mr-1!"
label="列展示" label="列展示"
v-model={checkAll.value} v-model={checkAll.value}
indeterminate={isIndeterminate.value} indeterminate={isIndeterminate.value}
@ -337,8 +342,8 @@ export default defineComponent({
class={[ class={[
"drag-btn w-[16px] mr-2", "drag-btn w-[16px] mr-2",
isFixedColumn(item) isFixedColumn(item)
? "!cursor-no-drop" ? "cursor-no-drop!"
: "!cursor-grab" : "cursor-grab!"
]} ]}
onMouseenter={(event: { onMouseenter={(event: {
preventDefault: () => void; preventDefault: () => void;
@ -373,7 +378,7 @@ export default defineComponent({
class={["w-[16px]", iconClass.value]} class={["w-[16px]", iconClass.value]}
icon={isFullscreen.value ? ExitFullscreen : Fullscreen} icon={isFullscreen.value ? ExitFullscreen : Fullscreen}
v-tippy={isFullscreen.value ? "退出全屏" : "全屏"} v-tippy={isFullscreen.value ? "退出全屏" : "全屏"}
onClick={() => (isFullscreen.value = !isFullscreen.value)} onClick={() => onFullscreen()}
/> />
</div> </div>
</div> </div>

View File

@ -98,7 +98,6 @@
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
transition: 0.1s;
} }
.pure-segmented-group { .pure-segmented-group {

View File

@ -6,7 +6,7 @@ export interface OptionsType {
label?: string | (() => VNode | Component); label?: string | (() => VNode | Component);
/** /**
* @description `useRenderIcon` * @description `useRenderIcon`
* @see {@link https://pure-admin.github.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks } * @see {@link https://pure-admin.cn/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks }
*/ */
icon?: string | Component; icon?: string | Component;
/** 图标属性、样式配置 */ /** 图标属性、样式配置 */

View File

@ -1,6 +1,6 @@
<script lang="ts" setup> <script setup lang="ts">
import { h, onMounted, ref, useSlots } from "vue"; import { h, onMounted, ref } from "vue";
import { type TippyOptions, useTippy } from "vue-tippy"; import { type TippyOptions, type TippyContent, useTippy } from "vue-tippy";
defineOptions({ defineOptions({
name: "ReText" name: "ReText"
@ -17,7 +17,10 @@ const props = defineProps({
} }
}); });
const $slots = useSlots(); const slots = defineSlots<{
content: () => TippyContent;
default: () => any;
}>();
const textRef = ref(); const textRef = ref();
const tippyFunc = ref(); const tippyFunc = ref();
@ -33,7 +36,7 @@ const isTextEllipsis = (el: HTMLElement) => {
}; };
const getTippyProps = () => ({ const getTippyProps = () => ({
content: h($slots.content || $slots.default), content: h(slots.content || slots.default),
...props.tippyProps ...props.tippyProps
}); });

View File

@ -32,8 +32,8 @@ const calculate = (
const offset = el.getBoundingClientRect(); const offset = el.getBoundingClientRect();
// 获取点击位置距离 el 的垂直和水平距离 // 获取点击位置距离 el 的垂直和水平距离
let localX = e.clientX - offset.left; const localX = e.clientX - offset.left;
let localY = e.clientY - offset.top; const localY = e.clientY - offset.top;
let radius = 0; let radius = 0;
let scale = 0.3; let scale = 0.3;

View File

@ -1,4 +1,4 @@
<script lang="ts" setup> <script setup lang="ts">
import { getConfig } from "@/config"; import { getConfig } from "@/config";
const TITLE = getConfig("Title"); const TITLE = getConfig("Title");

View File

@ -7,8 +7,8 @@ import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vu
import LaySidebarBreadCrumb from "../lay-sidebar/components/SidebarBreadCrumb.vue"; import LaySidebarBreadCrumb from "../lay-sidebar/components/SidebarBreadCrumb.vue";
import LaySidebarTopCollapse from "../lay-sidebar/components/SidebarTopCollapse.vue"; import LaySidebarTopCollapse from "../lay-sidebar/components/SidebarTopCollapse.vue";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line"; import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line"; import Setting from "~icons/ri/settings-3-line";
const { const {
layout, layout,
@ -24,7 +24,7 @@ const {
</script> </script>
<template> <template>
<div class="navbar bg-[#fff] shadow-sm shadow-[rgba(0,21,41,0.08)]"> <div class="navbar bg-[#fff] shadow-xs shadow-[rgba(0,21,41,0.08)]">
<LaySidebarTopCollapse <LaySidebarTopCollapse
v-if="device === 'mobile'" v-if="device === 'mobile'"
class="hamburger-container" class="hamburger-container"

View File

@ -49,7 +49,7 @@ function hoverDescription(event, description) {
<template> <template>
<div <div
class="notice-container border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]" class="notice-container border-0 border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]"
> >
<el-avatar <el-avatar
v-if="noticeItem.avatar" v-if="noticeItem.avatar"
@ -112,7 +112,7 @@ function hoverDescription(event, description) {
max-width: 238px; max-width: 238px;
} }
</style> </style>
<style scoped lang="scss"> <style lang="scss" scoped>
.notice-container { .notice-container {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;

View File

@ -2,7 +2,7 @@
import { ref, computed } from "vue"; import { ref, computed } from "vue";
import { noticesData } from "./data"; import { noticesData } from "./data";
import NoticeList from "./components/NoticeList.vue"; import NoticeList from "./components/NoticeList.vue";
import BellIcon from "@iconify-icons/ep/bell"; import BellIcon from "~icons/ep/bell";
const noticesNum = ref(0); const noticesNum = ref(0);
const notices = ref(noticesData); const notices = ref(noticesData);

View File

@ -3,7 +3,7 @@ import { emitter } from "@/utils/mitt";
import { onClickOutside } from "@vueuse/core"; import { onClickOutside } from "@vueuse/core";
import { ref, computed, onMounted, onBeforeUnmount } from "vue"; import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import CloseIcon from "@iconify-icons/ep/close"; import CloseIcon from "~icons/ep/close";
const target = ref(null); const target = ref(null);
const show = ref<Boolean>(false); const show = ref<Boolean>(false);
@ -15,7 +15,7 @@ const iconClass = computed(() => {
"flex", "flex",
"justify-center", "justify-center",
"items-center", "items-center",
"outline-none", "outline-hidden",
"rounded-[4px]", "rounded-[4px]",
"cursor-pointer", "cursor-pointer",
"transition-colors", "transition-colors",
@ -49,7 +49,7 @@ onBeforeUnmount(() => {
<div class="right-panel-background" /> <div class="right-panel-background" />
<div ref="target" class="right-panel bg-bg_color"> <div ref="target" class="right-panel bg-bg_color">
<div <div
class="project-configuration border-b-[1px] border-solid border-[var(--pure-border-color)]" class="project-configuration border-0 border-b-[1px] border-solid border-[var(--pure-border-color)]"
> >
<h4 class="dark:text-white">系统配置</h4> <h4 class="dark:text-white">系统配置</h4>
<span <span
@ -74,7 +74,7 @@ onBeforeUnmount(() => {
</el-scrollbar> </el-scrollbar>
<div <div
class="flex justify-end p-3 border-t-[1px] border-solid border-[var(--pure-border-color)]" class="flex justify-end p-3 border-0 border-t-[1px] border-solid border-[var(--pure-border-color)]"
> >
<el-button <el-button
v-tippy="{ v-tippy="{
@ -117,8 +117,8 @@ onBeforeUnmount(() => {
width: 100%; width: 100%;
max-width: 280px; max-width: 280px;
box-shadow: 0 0 15px 0 rgb(0 0 0 / 5%); box-shadow: 0 0 15px 0 rgb(0 0 0 / 5%);
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
transform: translate(100%); transform: translate(100%);
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
} }
.show { .show {

View File

@ -2,10 +2,10 @@
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import MdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component"; import MdiKeyboardEsc from "@/assets/svg/keyboard_esc.svg?component";
import EnterOutlined from "@/assets/svg/enter_outlined.svg?component"; import EnterOutlined from "@/assets/svg/enter_outlined.svg?component";
import ArrowUpLine from "@iconify-icons/ri/arrow-up-line"; import ArrowUpLine from "~icons/ri/arrow-up-line";
import ArrowDownLine from "@iconify-icons/ri/arrow-down-line"; import ArrowDownLine from "~icons/ri/arrow-down-line";
withDefaults(defineProps<{ total: number }>(), { withDefaults(defineProps<{ total?: number }>(), {
total: 0 total: 0
}); });

View File

@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import type { optionsItem } from "../types"; import type { optionsItem } from "../types";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import StarIcon from "@iconify-icons/ep/star"; import StarIcon from "~icons/ep/star";
import CloseIcon from "@iconify-icons/ep/close"; import CloseIcon from "~icons/ep/close";
interface Props { interface Props {
item: optionsItem; item: optionsItem;

View File

@ -11,7 +11,7 @@ import { ref, computed, shallowRef, watch } from "vue";
import { useDebounceFn, onKeyStroke } from "@vueuse/core"; import { useDebounceFn, onKeyStroke } from "@vueuse/core";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import { cloneDeep, isAllEmpty, storageLocal } from "@pureadmin/utils"; import { cloneDeep, isAllEmpty, storageLocal } from "@pureadmin/utils";
import SearchIcon from "@iconify-icons/ri/search-line"; import SearchIcon from "~icons/ri/search-line";
interface Props { interface Props {
/** 弹窗显隐 */ /** 弹窗显隐 */

View File

@ -14,7 +14,7 @@ function handleSearch() {
class="search-container w-[40px] h-[48px] flex-c cursor-pointer navbar-bg-hover" class="search-container w-[40px] h-[48px] flex-c cursor-pointer navbar-bg-hover"
@click="handleSearch" @click="handleSearch"
> >
<IconifyIconOffline icon="ri:search-line" /> <IconifyIconOffline icon="ri/search-line" />
</div> </div>
<SearchModal v-model:value="show" /> <SearchModal v-model:value="show" />
</div> </div>

View File

@ -13,15 +13,14 @@ import { emitter } from "@/utils/mitt";
import LayPanel from "../lay-panel/index.vue"; import LayPanel from "../lay-panel/index.vue";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { useAppStoreHook } from "@/store/modules/app"; import { useAppStoreHook } from "@/store/modules/app";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import Segmented, { type OptionsType } from "@/components/ReSegmented"; import Segmented, { type OptionsType } from "@/components/ReSegmented";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import { useDark, useGlobal, debounce, isNumber } from "@pureadmin/utils"; import { useDark, useGlobal, debounce, isNumber } from "@pureadmin/utils";
import Check from "@iconify-icons/ep/check"; import Check from "~icons/ep/check";
import LeftArrow from "@iconify-icons/ri/arrow-left-s-line"; import LeftArrow from "~icons/ri/arrow-left-s-line?width=20&height=20";
import RightArrow from "@iconify-icons/ri/arrow-right-s-line"; import RightArrow from "~icons/ri/arrow-right-s-line?width=20&height=20";
import DayIcon from "@/assets/svg/day.svg?component"; import DayIcon from "@/assets/svg/day.svg?component";
import DarkIcon from "@/assets/svg/dark.svg?component"; import DarkIcon from "@/assets/svg/dark.svg?component";
import SystemIcon from "@/assets/svg/system.svg?component"; import SystemIcon from "@/assets/svg/system.svg?component";
@ -48,9 +47,7 @@ const {
if (unref(layoutTheme)) { if (unref(layoutTheme)) {
const layout = unref(layoutTheme).layout; const layout = unref(layoutTheme).layout;
const theme = unref(layoutTheme).theme; const theme = unref(layoutTheme).theme;
toggleTheme({ document.documentElement.setAttribute("data-theme", theme);
scopeName: `layout-theme-${theme}`
});
setLayoutModel(layout); setLayoutModel(layout);
} }
@ -190,7 +187,7 @@ const getThemeColor = computed(() => {
}); });
const pClass = computed(() => { const pClass = computed(() => {
return ["mb-[12px]", "font-medium", "text-sm", "dark:text-white"]; return ["mb-[12px]!", "font-medium", "text-sm", "dark:text-white"];
}); });
const themeOptions = computed<Array<OptionsType>>(() => { const themeOptions = computed<Array<OptionsType>>(() => {
@ -336,7 +333,7 @@ onUnmounted(() => removeMatchMedia);
" "
/> />
<p :class="['mt-5', pClass]">主题色</p> <p :class="['mt-5!', pClass]">主题色</p>
<ul class="theme-color"> <ul class="theme-color">
<li <li
v-for="(item, index) in themeColors" v-for="(item, index) in themeColors"
@ -355,7 +352,7 @@ onUnmounted(() => removeMatchMedia);
</li> </li>
</ul> </ul>
<p :class="['mt-5', pClass]">导航模式</p> <p :class="['mt-5!', pClass]">导航模式</p>
<ul class="pure-theme"> <ul class="pure-theme">
<li <li
ref="verticalRef" ref="verticalRef"
@ -398,7 +395,7 @@ onUnmounted(() => removeMatchMedia);
</ul> </ul>
<span v-if="useAppStoreHook().getViewportWidth > 1280"> <span v-if="useAppStoreHook().getViewportWidth > 1280">
<p :class="['mt-5', pClass]">页宽</p> <p :class="['mt-5!', pClass]">页宽</p>
<Segmented <Segmented
resize resize
class="mb-2 select-none" class="mb-2 select-none"
@ -427,21 +424,19 @@ onUnmounted(() => removeMatchMedia);
> >
<IconifyIconOffline <IconifyIconOffline
:icon="settings.stretch ? RightArrow : LeftArrow" :icon="settings.stretch ? RightArrow : LeftArrow"
height="20"
/> />
<div <div
class="flex-grow border-b border-dashed" class="grow border-0 border-b border-dashed"
style="border-color: var(--el-color-primary)" style="border-color: var(--el-color-primary)"
/> />
<IconifyIconOffline <IconifyIconOffline
:icon="settings.stretch ? LeftArrow : RightArrow" :icon="settings.stretch ? LeftArrow : RightArrow"
height="20"
/> />
</div> </div>
</button> </button>
</span> </span>
<p :class="['mt-4', pClass]">页签风格</p> <p :class="['mt-4!', pClass]">页签风格</p>
<Segmented <Segmented
resize resize
class="select-none" class="select-none"
@ -450,7 +445,7 @@ onUnmounted(() => removeMatchMedia);
@change="onChange" @change="onChange"
/> />
<p class="mt-5 font-medium text-sm dark:text-white">界面显示</p> <p class="mt-5! font-medium text-sm dark:text-white">界面显示</p>
<ul class="setting"> <ul class="setting">
<li> <li>
<span class="dark:text-white">灰色模式</span> <span class="dark:text-white">灰色模式</span>

View File

@ -10,8 +10,8 @@ import { usePermissionStoreHook } from "@/store/modules/permission";
import LaySidebarItem from "../lay-sidebar/components/SidebarItem.vue"; import LaySidebarItem from "../lay-sidebar/components/SidebarItem.vue";
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue"; import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line"; import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line"; import Setting from "~icons/ri/settings-3-line";
const menuRef = ref(); const menuRef = ref();
const showLogo = ref( const showLogo = ref(

View File

@ -10,8 +10,8 @@ import { usePermissionStoreHook } from "@/store/modules/permission";
import LaySidebarExtraIcon from "../lay-sidebar/components/SidebarExtraIcon.vue"; import LaySidebarExtraIcon from "../lay-sidebar/components/SidebarExtraIcon.vue";
import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue"; import LaySidebarFullScreen from "../lay-sidebar/components/SidebarFullScreen.vue";
import LogoutCircleRLine from "@iconify-icons/ri/logout-circle-r-line"; import LogoutCircleRLine from "~icons/ri/logout-circle-r-line";
import Setting from "@iconify-icons/ri/settings-3-line"; import Setting from "~icons/ri/settings-3-line";
const menuRef = ref(); const menuRef = ref();
const defaultActive = ref(null); const defaultActive = ref(null);

View File

@ -104,12 +104,12 @@ watch(
</script> </script>
<template> <template>
<el-breadcrumb class="!leading-[50px] select-none" separator="/"> <el-breadcrumb class="leading-[50px]! select-none" separator="/">
<transition-group name="breadcrumb"> <transition-group name="breadcrumb">
<el-breadcrumb-item <el-breadcrumb-item
v-for="item in levelList" v-for="item in levelList"
:key="item.path" :key="item.path"
class="!inline !items-stretch" class="inline! items-stretch!"
> >
<a @click.prevent="handleLink(item)"> <a @click.prevent="handleLink(item)">
{{ item.meta.title }} {{ item.meta.title }}

View File

@ -3,10 +3,10 @@ import { computed } from "vue";
import { useGlobal } from "@pureadmin/utils"; import { useGlobal } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import ArrowLeft from "@iconify-icons/ri/arrow-left-double-fill"; import ArrowLeft from "~icons/ri/arrow-left-double-fill";
interface Props { interface Props {
isActive: boolean; isActive?: boolean;
} }
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {

View File

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import path from "path";
import { getConfig } from "@/config"; import { getConfig } from "@/config";
import { posix } from "path-browserify";
import { menuType } from "@/layout/types"; import { menuType } from "@/layout/types";
import { ReText } from "@/components/ReText"; import { ReText } from "@/components/ReText";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
@ -16,10 +16,10 @@ import {
useAttrs useAttrs
} from "vue"; } from "vue";
import ArrowUp from "@iconify-icons/ep/arrow-up-bold"; import ArrowUp from "~icons/ep/arrow-up-bold";
import EpArrowDown from "@iconify-icons/ep/arrow-down-bold"; import EpArrowDown from "~icons/ep/arrow-down-bold";
import ArrowLeft from "@iconify-icons/ep/arrow-left-bold"; import ArrowLeft from "~icons/ep/arrow-left-bold";
import ArrowRight from "@iconify-icons/ep/arrow-right-bold"; import ArrowRight from "~icons/ep/arrow-right-bold";
const attrs = useAttrs(); const attrs = useAttrs();
const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav(); const { layout, isCollapse, tooltipEffect, getDivStyle } = useNav();
@ -60,6 +60,21 @@ const getSubMenuIconStyle = computed((): CSSProperties => {
}; };
}); });
const textClass = computed(() => {
const item = props.item;
const baseClass = "w-full! text-inherit!";
if (
layout.value !== "horizontal" &&
isCollapse.value &&
!toRaw(item.meta.icon) &&
((layout.value === "vertical" && item.parentId === null) ||
(layout.value === "mix" && item.pathList.length === 2))
) {
return `${baseClass} min-w-[54px]! text-center! px-3!`;
}
return baseClass;
});
const expandCloseIcon = computed(() => { const expandCloseIcon = computed(() => {
if (!getConfig()?.MenuArrowIconNoTransition) return ""; if (!getConfig()?.MenuArrowIconNoTransition) return "";
return { return {
@ -98,8 +113,7 @@ function resolvePath(routePath) {
if (httpReg.test(routePath) || httpReg.test(props.basePath)) { if (httpReg.test(routePath) || httpReg.test(props.basePath)) {
return routePath || props.basePath; return routePath || props.basePath;
} else { } else {
// 使path.posix.resolvepath.resolve windows使electron return posix.resolve(props.basePath, routePath);
return path.posix.resolve(props.basePath, routePath);
} }
} }
</script> </script>
@ -144,7 +158,7 @@ function resolvePath(routePath) {
item?.pathList?.length === 2) item?.pathList?.length === 2)
" "
truncated truncated
class="!w-full !pl-4 !text-inherit" class="w-full! px-3! min-w-[54px]! text-center! text-inherit!"
> >
{{ onlyOneChild.meta.title }} {{ onlyOneChild.meta.title }}
</el-text> </el-text>
@ -156,7 +170,7 @@ function resolvePath(routePath) {
offset: [0, -10], offset: [0, -10],
theme: tooltipEffect theme: tooltipEffect
}" }"
class="!w-full !text-inherit" class="w-full! text-inherit!"
> >
{{ onlyOneChild.meta.title }} {{ onlyOneChild.meta.title }}
</ReText> </ReText>
@ -195,15 +209,7 @@ function resolvePath(routePath) {
offset: [0, -10], offset: [0, -10],
theme: tooltipEffect theme: tooltipEffect
}" }"
:class="{ :class="textClass"
'!w-full': true,
'!text-inherit': true,
'!pl-4':
layout !== 'horizontal' &&
isCollapse &&
!toRaw(item.meta.icon) &&
item.parentId === null
}"
> >
{{ item.meta.title }} {{ item.meta.title }}
</ReText> </ReText>

View File

@ -3,10 +3,10 @@ import { computed } from "vue";
import { useGlobal } from "@pureadmin/utils"; import { useGlobal } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import MenuFold from "@iconify-icons/ri/menu-fold-fill"; import MenuFold from "~icons/ri/menu-fold-fill";
interface Props { interface Props {
isActive: boolean; isActive?: boolean;
} }
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
@ -21,7 +21,7 @@ const iconClass = computed(() => {
"mb-1", "mb-1",
"w-[16px]", "w-[16px]",
"h-[16px]", "h-[16px]",
"inline-block", "inline-block!",
"align-middle", "align-middle",
"cursor-pointer", "cursor-pointer",
"duration-[100ms]" "duration-[100ms]"

View File

@ -60,11 +60,11 @@ const { title, getLogo } = useNav();
height: 32px; height: 32px;
margin: 2px 0 0 12px; margin: 2px 0 0 12px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
line-height: 32px; line-height: 32px;
color: $subMenuActiveText; color: var(--pure-theme-sub-menu-active-text);
text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
} }

View File

@ -1,9 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import MenuFold from "@iconify-icons/ri/menu-fold-fill"; import MenuFold from "~icons/ri/menu-fold-fill";
import MenuUnfold from "@iconify-icons/ri/menu-unfold-fill"; import MenuUnfold from "~icons/ri/menu-unfold-fill";
interface Props { interface Props {
isActive: boolean; isActive?: boolean;
} }
withDefaults(defineProps<Props>(), { withDefaults(defineProps<Props>(), {
@ -27,7 +27,7 @@ const toggleClick = () => {
> >
<IconifyIconOffline <IconifyIconOffline
:icon="isActive ? MenuFold : MenuUnfold" :icon="isActive ? MenuFold : MenuUnfold"
class="inline-block align-middle hover:text-primary dark:hover:!text-white" class="inline-block! align-middle hover:text-primary dark:hover:text-white!"
/> />
</div> </div>
</template> </template>

View File

@ -59,10 +59,10 @@
color: var(--el-color-primary); color: var(--el-color-primary);
cursor: pointer; cursor: pointer;
border-radius: 4px; border-radius: 4px;
transform: translate(0, -50%);
transition: transition:
background-color 0.12s, background-color 0.12s,
color 0.12s; color 0.12s;
transform: translate(0, -50%);
&:hover { &:hover {
color: rgb(0 0 0 / 88%) !important; color: rgb(0 0 0 / 88%) !important;
@ -127,10 +127,10 @@
font-weight: normal; font-weight: normal;
color: var(--el-text-color-primary); color: var(--el-text-color-primary);
white-space: nowrap; white-space: nowrap;
outline: 0;
list-style-type: none; list-style-type: none;
background: #fff; background: #fff;
border-radius: 4px; border-radius: 4px;
outline: 0;
box-shadow: 0 2px 8px rgb(0 0 0 / 15%); box-shadow: 0 2px 8px rgb(0 0 0 / 15%);
li { li {

View File

@ -17,11 +17,11 @@ import {
useResizeObserver useResizeObserver
} from "@pureadmin/utils"; } from "@pureadmin/utils";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill"; import ExitFullscreen from "~icons/ri/fullscreen-exit-fill";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill"; import Fullscreen from "~icons/ri/fullscreen-fill";
import ArrowDown from "@iconify-icons/ri/arrow-down-s-line"; import ArrowDown from "~icons/ri/arrow-down-s-line";
import ArrowRightSLine from "@iconify-icons/ri/arrow-right-s-line"; import ArrowRightSLine from "~icons/ri/arrow-right-s-line";
import ArrowLeftSLine from "@iconify-icons/ri/arrow-left-s-line"; import ArrowLeftSLine from "~icons/ri/arrow-left-s-line";
const { const {
Close, Close,
@ -509,6 +509,7 @@ function tagOnClick(item) {
} else { } else {
router.push({ path }); router.push({ path });
} }
emitter.emit("tagOnClick", item);
} }
onClickOutside(contextmenuRef, closeMenu, { onClickOutside(contextmenuRef, closeMenu, {
@ -586,7 +587,7 @@ onBeforeUnmount(() => {
> >
<template v-if="showModel !== 'chrome'"> <template v-if="showModel !== 'chrome'">
<span <span
class="tag-title dark:!text-text_color_primary dark:hover:!text-primary" class="tag-title dark:text-text_color_primary! dark:hover:text-primary!"
> >
{{ item.meta.title }} {{ item.meta.title }}
</span> </span>

View File

@ -6,14 +6,9 @@ import { routerArrays } from "@/layout/types";
import { router, resetRouter } from "@/router"; import { router, resetRouter } from "@/router";
import type { themeColorsType } from "../types"; import type { themeColorsType } from "../types";
import { useAppStoreHook } from "@/store/modules/app"; import { useAppStoreHook } from "@/store/modules/app";
import { useGlobal, storageLocal } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme"; import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { import { darken, lighten, useGlobal, storageLocal } from "@pureadmin/utils";
darken,
lighten,
toggleTheme
} from "@pureadmin/theme/dist/browser-utils";
export function useDataThemeChange() { export function useDataThemeChange() {
const { layoutTheme, layout } = useLayout(); const { layoutTheme, layout } = useLayout();
@ -54,9 +49,7 @@ export function useDataThemeChange() {
isClick = true isClick = true
) { ) {
layoutTheme.value.theme = theme; layoutTheme.value.theme = theme;
toggleTheme({ document.documentElement.setAttribute("data-theme", theme);
scopeName: `layout-theme-${theme}`
});
// 如果非isClick保留之前的themeColor // 如果非isClick保留之前的themeColor
const storageThemeColor = $storage.layout.themeColor; const storageThemeColor = $storage.layout.themeColor;
$storage.layout = { $storage.layout = {

View File

@ -12,8 +12,8 @@ import { useAppStoreHook } from "@/store/modules/app";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
import { useGlobal, isAllEmpty } from "@pureadmin/utils"; import { useGlobal, isAllEmpty } from "@pureadmin/utils";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill"; import ExitFullscreen from "~icons/ri/fullscreen-exit-fill";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill"; import Fullscreen from "~icons/ri/fullscreen-fill";
const errorInfo = const errorInfo =
"The current routing configuration is incorrect, please check the configuration"; "The current routing configuration is incorrect, please check the configuration";

View File

@ -20,13 +20,13 @@ import {
hasClass hasClass
} from "@pureadmin/utils"; } from "@pureadmin/utils";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill"; import Fullscreen from "~icons/ri/fullscreen-fill";
import CloseAllTags from "@iconify-icons/ri/subtract-line"; import CloseAllTags from "~icons/ri/subtract-line";
import CloseOtherTags from "@iconify-icons/ri/text-spacing"; import CloseOtherTags from "~icons/ri/text-spacing";
import CloseRightTags from "@iconify-icons/ri/text-direction-l"; import CloseRightTags from "~icons/ri/text-direction-l";
import CloseLeftTags from "@iconify-icons/ri/text-direction-r"; import CloseLeftTags from "~icons/ri/text-direction-r";
import RefreshRight from "@iconify-icons/ep/refresh-right"; import RefreshRight from "~icons/ep/refresh-right";
import Close from "@iconify-icons/ep/close"; import Close from "~icons/ep/close";
export function useTags() { export function useTags() {
const route = useRoute(); const route = useRoute();

View File

@ -208,8 +208,8 @@ const LayHeader = defineComponent({
height: 100%; height: 100%;
&::after { &::after {
display: table;
clear: both; clear: both;
display: table;
content: ""; content: "";
} }

View File

@ -1,129 +0,0 @@
/**
* @description 使
*/
import type { multipleScopeVarsOptions } from "@pureadmin/theme";
/** 预设主题色 */
const themeColors = {
/* 亮白色 */
light: {
subMenuActiveText: "#000000d9",
menuBg: "#fff",
menuHover: "#f6f6f6",
subMenuBg: "#fff",
subMenuActiveBg: "#e0ebf6",
menuText: "rgb(0 0 0 / 60%)",
sidebarLogo: "#fff",
menuTitleHover: "#000",
menuActiveBefore: "#4091f7"
},
/* 道奇蓝 */
default: {
subMenuActiveText: "#fff",
menuBg: "#001529",
menuHover: "rgb(64 145 247 / 15%)",
subMenuBg: "#0f0303",
subMenuActiveBg: "#4091f7",
menuText: "rgb(254 254 254 / 65%)",
sidebarLogo: "#002140",
menuTitleHover: "#fff",
menuActiveBefore: "#4091f7"
},
/* 深紫罗兰色 */
saucePurple: {
subMenuActiveText: "#fff",
menuBg: "#130824",
menuHover: "rgb(105 58 201 / 15%)",
subMenuBg: "#000",
subMenuActiveBg: "#693ac9",
menuText: "#7a80b4",
sidebarLogo: "#1f0c38",
menuTitleHover: "#fff",
menuActiveBefore: "#693ac9"
},
/* 深粉色 */
pink: {
subMenuActiveText: "#fff",
menuBg: "#28081a",
menuHover: "rgb(216 68 147 / 15%)",
subMenuBg: "#000",
subMenuActiveBg: "#d84493",
menuText: "#7a80b4",
sidebarLogo: "#3f0d29",
menuTitleHover: "#fff",
menuActiveBefore: "#d84493"
},
/* 猩红色 */
dusk: {
subMenuActiveText: "#fff",
menuBg: "#2a0608",
menuHover: "rgb(225 60 57 / 15%)",
subMenuBg: "#000",
subMenuActiveBg: "#e13c39",
menuText: "rgb(254 254 254 / 65.1%)",
sidebarLogo: "#42090c",
menuTitleHover: "#fff",
menuActiveBefore: "#e13c39"
},
/* 橙红色 */
volcano: {
subMenuActiveText: "#fff",
menuBg: "#2b0e05",
menuHover: "rgb(232 95 51 / 15%)",
subMenuBg: "#0f0603",
subMenuActiveBg: "#e85f33",
menuText: "rgb(254 254 254 / 65%)",
sidebarLogo: "#441708",
menuTitleHover: "#fff",
menuActiveBefore: "#e85f33"
},
/* 绿宝石 */
mingQing: {
subMenuActiveText: "#fff",
menuBg: "#032121",
menuHover: "rgb(89 191 193 / 15%)",
subMenuBg: "#000",
subMenuActiveBg: "#59bfc1",
menuText: "#7a80b4",
sidebarLogo: "#053434",
menuTitleHover: "#fff",
menuActiveBefore: "#59bfc1"
},
/* 酸橙绿 */
auroraGreen: {
subMenuActiveText: "#fff",
menuBg: "#0b1e15",
menuHover: "rgb(96 172 128 / 15%)",
subMenuBg: "#000",
subMenuActiveBg: "#60ac80",
menuText: "#7a80b4",
sidebarLogo: "#112f21",
menuTitleHover: "#fff",
menuActiveBefore: "#60ac80"
}
};
/**
* @description
*/
export const genScssMultipleScopeVars = (): multipleScopeVarsOptions[] => {
const result = [] as multipleScopeVarsOptions[];
Object.keys(themeColors).forEach(key => {
result.push({
scopeName: `layout-theme-${key}`,
varsContent: `
$subMenuActiveText: ${themeColors[key].subMenuActiveText} !default;
$menuBg: ${themeColors[key].menuBg} !default;
$menuHover: ${themeColors[key].menuHover} !default;
$subMenuBg: ${themeColors[key].subMenuBg} !default;
$subMenuActiveBg: ${themeColors[key].subMenuActiveBg} !default;
$menuText: ${themeColors[key].menuText} !default;
$sidebarLogo: ${themeColors[key].sidebarLogo} !default;
$menuTitleHover: ${themeColors[key].menuTitleHover} !default;
$menuActiveBefore: ${themeColors[key].menuActiveBefore} !default;
`
} as multipleScopeVarsOptions);
});
return result;
};

View File

@ -1,4 +1,4 @@
import type { IconifyIcon } from "@iconify/vue"; import type { FunctionalComponent } from "vue";
const { VITE_HIDE_HOME } = import.meta.env; const { VITE_HIDE_HOME } = import.meta.env;
export const routerArrays: Array<RouteConfigs> = export const routerArrays: Array<RouteConfigs> =
@ -8,7 +8,7 @@ export const routerArrays: Array<RouteConfigs> =
path: "/welcome", path: "/welcome",
meta: { meta: {
title: "首页", title: "首页",
icon: "ep:home-filled" icon: "ep/home-filled"
} }
} }
] ]
@ -16,7 +16,7 @@ export const routerArrays: Array<RouteConfigs> =
export type routeMetaType = { export type routeMetaType = {
title?: string; title?: string;
icon?: string | IconifyIcon; icon?: string | FunctionalComponent;
showLink?: boolean; showLink?: boolean;
savedPosition?: boolean; savedPosition?: boolean;
auths?: Array<string>; auths?: Array<string>;
@ -36,7 +36,7 @@ export type multiTagsType = {
}; };
export type tagsViewsType = { export type tagsViewsType = {
icon: string | IconifyIcon; icon: string | FunctionalComponent;
text: string; text: string;
divided: boolean; divided: boolean;
disabled: boolean; disabled: boolean;

View File

@ -6,7 +6,13 @@ import { buildHierarchyTree } from "@/utils/tree";
import remainingRouter from "./modules/remaining"; import remainingRouter from "./modules/remaining";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags"; import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import { isUrl, openLink, storageLocal, isAllEmpty } from "@pureadmin/utils"; import {
isUrl,
openLink,
cloneDeep,
isAllEmpty,
storageLocal
} from "@pureadmin/utils";
import { import {
ascending, ascending,
getTopMenu, getTopMenu,
@ -20,9 +26,9 @@ import {
} from "./utils"; } from "./utils";
import { import {
type Router, type Router,
createRouter,
type RouteRecordRaw, type RouteRecordRaw,
type RouteComponent type RouteComponent,
createRouter
} from "vue-router"; } from "vue-router";
import { import {
type DataInfo, type DataInfo,
@ -54,6 +60,9 @@ export const constantRoutes: Array<RouteRecordRaw> = formatTwoStageRoutes(
formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity)))) formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
); );
/** 初始的静态路由,用于退出登录时重置路由 */
const initConstantRoutes: Array<RouteRecordRaw> = cloneDeep(constantRoutes);
/** 用于渲染菜单,保持原始层级 */ /** 用于渲染菜单,保持原始层级 */
export const constantMenus: Array<RouteComponent> = ascending( export const constantMenus: Array<RouteComponent> = ascending(
routes.flat(Infinity) routes.flat(Infinity)
@ -86,17 +95,13 @@ export const router: Router = createRouter({
/** 重置路由 */ /** 重置路由 */
export function resetRouter() { export function resetRouter() {
router.getRoutes().forEach(route => { router.clearRoutes();
const { name, meta } = route; for (const route of initConstantRoutes.concat(...(remainingRouter as any))) {
if (name && router.hasRoute(name) && meta?.backstage) { router.addRoute(route);
router.removeRoute(name);
router.options.routes = formatTwoStageRoutes(
formatFlatteningRoutes(
buildHierarchyTree(ascending(routes.flat(Infinity)))
)
);
} }
}); router.options.routes = formatTwoStageRoutes(
formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity))))
);
usePermissionStoreHook().clearAllCachePage(); usePermissionStoreHook().clearAllCachePage();
} }

View File

@ -2,7 +2,7 @@ export default {
path: "/error", path: "/error",
redirect: "/error/403", redirect: "/error/403",
meta: { meta: {
icon: "ri:information-line", icon: "ri/information-line",
// showLink: false, // showLink: false,
title: "异常页面", title: "异常页面",
rank: 9 rank: 9

View File

@ -7,7 +7,7 @@ export default {
component: Layout, component: Layout,
redirect: "/welcome", redirect: "/welcome",
meta: { meta: {
icon: "ep:home-filled", icon: "ep/home-filled",
title: "首页", title: "首页",
rank: 0 rank: 0
}, },

View File

@ -172,6 +172,8 @@ function handleAsyncRoutes(routeList) {
const flattenRouters: any = router const flattenRouters: any = router
.getRoutes() .getRoutes()
.find(n => n.path === "/"); .find(n => n.path === "/");
// 保持router.options.routes[0].children与path为"/"的children一致防止数据不一致导致异常
flattenRouters.children = router.options.routes[0].children;
router.addRoute(flattenRouters); router.addRoute(flattenRouters);
} }
} }

View File

@ -8,8 +8,7 @@ import {
responsiveStorageNameSpace responsiveStorageNameSpace
} from "../utils"; } from "../utils";
export const useAppStore = defineStore({ export const useAppStore = defineStore("pure-app", {
id: "pure-app",
state: (): appType => ({ state: (): appType => ({
sidebar: { sidebar: {
opened: opened:
@ -77,9 +76,6 @@ export const useAppStore = defineStore({
}, },
setViewportSize(size) { setViewportSize(size) {
this.viewportSize = size; this.viewportSize = size;
},
setSortSwap(val) {
this.sortSwap = val;
} }
} }
}); });

View File

@ -6,8 +6,7 @@ import {
responsiveStorageNameSpace responsiveStorageNameSpace
} from "../utils"; } from "../utils";
export const useEpThemeStore = defineStore({ export const useEpThemeStore = defineStore("pure-epTheme", {
id: "pure-epTheme",
state: () => ({ state: () => ({
epThemeColor: epThemeColor:
storageLocal().getItem<StorageConfigs>( storageLocal().getItem<StorageConfigs>(

View File

@ -14,8 +14,7 @@ import {
} from "../utils"; } from "../utils";
import { usePermissionStoreHook } from "./permission"; import { usePermissionStoreHook } from "./permission";
export const useMultiTagsStore = defineStore({ export const useMultiTagsStore = defineStore("pure-multiTags", {
id: "pure-multiTags",
state: () => ({ state: () => ({
// 存储标签页信息(路由信息) // 存储标签页信息(路由信息)
multiTags: storageLocal().getItem<StorageConfigs>( multiTags: storageLocal().getItem<StorageConfigs>(
@ -24,12 +23,12 @@ export const useMultiTagsStore = defineStore({
? storageLocal().getItem<StorageConfigs>( ? storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}tags` `${responsiveStorageNameSpace()}tags`
) )
: [ : ([
...routerArrays, ...routerArrays,
...usePermissionStoreHook().flatteningRoutes.filter( ...usePermissionStoreHook().flatteningRoutes.filter(
v => v?.meta?.fixedTag v => v?.meta?.fixedTag
) )
], ] as any),
multiTagsCache: storageLocal().getItem<StorageConfigs>( multiTagsCache: storageLocal().getItem<StorageConfigs>(
`${responsiveStorageNameSpace()}configure` `${responsiveStorageNameSpace()}configure`
)?.multiTagsCache )?.multiTagsCache

View File

@ -12,8 +12,7 @@ import {
} from "../utils"; } from "../utils";
import { useMultiTagsStoreHook } from "./multiTags"; import { useMultiTagsStoreHook } from "./multiTags";
export const usePermissionStore = defineStore({ export const usePermissionStore = defineStore("pure-permission", {
id: "pure-permission",
state: () => ({ state: () => ({
// 静态路由生成的菜单 // 静态路由生成的菜单
constantMenus, constantMenus,
@ -31,7 +30,7 @@ export const usePermissionStore = defineStore({
filterTree(ascending(this.constantMenus.concat(routes))) filterTree(ascending(this.constantMenus.concat(routes)))
); );
this.flatteningRoutes = formatFlatteningRoutes( this.flatteningRoutes = formatFlatteningRoutes(
this.constantMenus.concat(routes) this.constantMenus.concat(routes) as any
); );
}, },
cacheOperate({ mode, name }: cacheType) { cacheOperate({ mode, name }: cacheType) {

View File

@ -1,8 +1,7 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { type setType, store, getConfig } from "../utils"; import { type setType, store, getConfig } from "../utils";
export const useSettingStore = defineStore({ export const useSettingStore = defineStore("pure-setting", {
id: "pure-setting",
state: (): setType => ({ state: (): setType => ({
title: getConfig().Title, title: getConfig().Title,
fixedHeader: getConfig().FixedHeader, fixedHeader: getConfig().FixedHeader,

View File

@ -16,8 +16,7 @@ import {
import { useMultiTagsStoreHook } from "./multiTags"; import { useMultiTagsStoreHook } from "./multiTags";
import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth"; import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth";
export const useUserStore = defineStore({ export const useUserStore = defineStore("pure-user", {
id: "pure-user",
state: (): userType => ({ state: (): userType => ({
// 头像 // 头像
avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "", avatar: storageLocal().getItem<DataInfo<number>>(userKey)?.avatar ?? "",

View File

@ -83,8 +83,8 @@
.el-upload-list__item.is-ready &.el-icon--close { .el-upload-list__item.is-ready &.el-icon--close {
width: 24px; width: 24px;
height: 24px; height: 24px;
border-radius: 4px;
outline: none; outline: none;
border-radius: 4px;
transition: transition:
background-color 0.2s, background-color 0.2s,
color 0.2s; color 0.2s;
@ -117,8 +117,8 @@
} }
& .el-message__closeBtn { & .el-message__closeBtn {
border-radius: 4px;
outline: none; outline: none;
border-radius: 4px;
transition: transition:
background-color 0.2s, background-color 0.2s,
color 0.2s; color 0.2s;
@ -163,9 +163,10 @@
/* 仿 el-scrollbar 滚动条样式支持大多数浏览器如Chrome、Edge、Firefox、Safari等。整体暗色风格在 src/style/dark.scss 文件进行了适配 */ /* 仿 el-scrollbar 滚动条样式支持大多数浏览器如Chrome、Edge、Firefox、Safari等。整体暗色风格在 src/style/dark.scss 文件进行了适配 */
.pure-scrollbar { .pure-scrollbar {
scrollbar-color: rgb(221 222 224) transparent; /* 滑块颜色、轨道颜色 */
/* Firefox */ /* Firefox */
scrollbar-width: thin; /* 可选值为 'auto', 'thin', 'none' */ scrollbar-width: thin; /* 可选值为 'auto', 'thin', 'none' */
scrollbar-color: rgb(221 222 224) transparent; /* 滑块颜色、轨道颜色 */
::-webkit-scrollbar { ::-webkit-scrollbar {
width: 6px; /* 滚动条宽度 */ width: 6px; /* 滚动条宽度 */
} }

View File

@ -1,7 +1,8 @@
@import "./transition"; @use "theme";
@import "./element-plus"; @use "transition";
@import "./sidebar"; @use "element-plus";
@import "./dark"; @use "sidebar";
@use "dark";
/* 自定义全局 CssVar */ /* 自定义全局 CssVar */
:root { :root {
@ -13,6 +14,16 @@
/* switch关闭状态下的color 需要时可取用 */ /* switch关闭状态下的color 需要时可取用 */
--pure-switch-off-color: #a6a6a6; --pure-switch-off-color: #a6a6a6;
/** 主题色 */
--pure-theme-sub-menu-active-text: initial;
--pure-theme-menu-bg: none;
--pure-theme-menu-hover: none;
--pure-theme-sub-menu-bg: transparent;
--pure-theme-menu-text: initial;
--pure-theme-sidebar-logo: none;
--pure-theme-menu-title-hover: initial;
--pure-theme-menu-active-before: transparent;
} }
/* 灰色模式 */ /* 灰色模式 */

View File

@ -1,12 +1,3 @@
*,
::before,
::after {
box-sizing: border-box;
border-color: currentColor;
border-style: solid;
border-width: 0;
}
#app { #app {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -25,7 +16,8 @@ body {
width: 100%; width: 100%;
height: 100%; height: 100%;
margin: 0; margin: 0;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", font-family:
"Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif; "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
line-height: inherit; line-height: inherit;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
@ -57,8 +49,9 @@ code,
kbd, kbd,
samp, samp,
pre { pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, font-family:
"Liberation Mono", "Courier New", monospace; ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
"Courier New", monospace;
font-size: 1em; font-size: 1em;
} }

View File

@ -1,7 +1,5 @@
/* $sideBarWidth: vertical 模式下主体内容距离网页文档左侧的距离 */ /* $sideBarWidth: vertical 模式下主体内容距离网页文档左侧的距离 */
@mixin merge-style($sideBarWidth) { @mixin merge-style($sideBarWidth) {
$menuActiveText: #7a80b4;
@media screen and (width >= 150px) and (width <= 420px) { @media screen and (width >= 150px) and (width <= 420px) {
.app-main-nofixed-header { .app-main-nofixed-header {
overflow-y: hidden; overflow-y: hidden;
@ -94,7 +92,7 @@
height: 100%; height: 100%;
overflow: visible; overflow: visible;
font-size: 0; font-size: 0;
background: $menuBg; background: var(--pure-theme-menu-bg) !important;
border-right: 1px solid var(--pure-border-color); border-right: 1px solid var(--pure-border-color);
/* 展开动画 */ /* 展开动画 */
@ -150,11 +148,11 @@
.el-menu-item, .el-menu-item,
.el-sub-menu__title { .el-sub-menu__title {
height: 50px; height: 50px;
color: $menuText; color: var(--pure-theme-menu-text);
background-color: transparent !important; background-color: transparent !important;
&:hover { &:hover {
color: $menuTitleHover !important; color: var(--pure-theme-menu-title-hover) !important;
} }
div, div,
@ -173,15 +171,15 @@
.is-active > .el-sub-menu__title, .is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown { .is-active.submenu-title-noDropdown {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
i { i {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
} }
} }
.is-active { .is-active {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
transition: color 0.3s; transition: color 0.3s;
} }
@ -193,8 +191,8 @@
.el-menu-item.is-active.nest-menu::before { .el-menu-item.is-active.nest-menu::before {
position: absolute; position: absolute;
inset: 0 8px; inset: 0 8px;
margin: 4px 0;
clear: both; clear: both;
margin: 4px 0;
content: ""; content: "";
background: var(--el-color-primary) !important; background: var(--el-color-primary) !important;
border-radius: 3px; border-radius: 3px;
@ -204,7 +202,7 @@
& .el-sub-menu .el-menu-item { & .el-sub-menu .el-menu-item {
min-width: $sideBarWidth !important; min-width: $sideBarWidth !important;
font-size: 14px; font-size: 14px;
background-color: $subMenuBg !important; background-color: var(--pure-theme-sub-menu-bg) !important;
} }
/* 有子集的激活菜单左侧小竖条 */ /* 有子集的激活菜单左侧小竖条 */
@ -214,13 +212,13 @@
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
clear: both;
width: 2px; width: 2px;
height: 100%; height: 100%;
clear: both;
content: ""; content: "";
background-color: $menuActiveBefore; background-color: var(--pure-theme-menu-active-before);
transition: all var(--pure-transition-duration) ease-in-out;
transform: translateY(0); transform: translateY(0);
transition: all var(--pure-transition-duration) ease-in-out;
} }
.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 {
@ -242,8 +240,8 @@
.is-active.submenu-title-noDropdown.outer-most::before { .is-active.submenu-title-noDropdown.outer-most::before {
position: absolute; position: absolute;
inset: 0 8px; inset: 0 8px;
margin: 4px 0;
clear: both; clear: both;
margin: 4px 0;
content: ""; content: "";
background: var(--el-color-primary) !important; background: var(--el-color-primary) !important;
border-radius: 3px; border-radius: 3px;
@ -253,7 +251,7 @@
/* vertical 菜单折叠 */ /* vertical 菜单折叠 */
.el-menu--vertical { .el-menu--vertical {
.el-menu--popup { .el-menu--popup {
background-color: $subMenuBg !important; background-color: var(--pure-theme-sub-menu-bg) !important;
.el-menu-item { .el-menu-item {
span { span {
@ -271,10 +269,10 @@
.is-active > .el-sub-menu__title, .is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown { .is-active.submenu-title-noDropdown {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
i { i {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
} }
} }
@ -282,23 +280,23 @@
.el-menu .el-sub-menu__title { .el-menu .el-sub-menu__title {
min-width: $sideBarWidth !important; min-width: $sideBarWidth !important;
font-size: 14px; font-size: 14px;
background-color: $subMenuBg !important; background-color: var(--pure-theme-sub-menu-bg) !important;
} }
.el-menu-item, .el-menu-item,
.el-sub-menu__title { .el-sub-menu__title {
height: 50px; height: 50px;
line-height: 50px; line-height: 50px;
color: $menuText; color: var(--pure-theme-menu-text);
background-color: $subMenuBg; background-color: var(--pure-theme-sub-menu-bg);
&:hover { &:hover {
color: $menuTitleHover !important; color: var(--pure-theme-menu-title-hover) !important;
} }
} }
.is-active { .is-active {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
transition: color 0.3s; transition: color 0.3s;
} }
@ -342,15 +340,15 @@
} }
.el-menu--popup { .el-menu--popup {
background-color: $subMenuBg !important; background-color: var(--pure-theme-sub-menu-bg) !important;
a > .is-active.submenu-title-noDropdown { a > .is-active.submenu-title-noDropdown {
border-bottom: none; border-bottom: none;
} }
.el-menu-item { .el-menu-item {
color: $menuText; color: var(--pure-theme-menu-text);
background-color: $subMenuBg; background-color: var(--pure-theme-sub-menu-bg);
span { span {
font-size: 14px; font-size: 14px;
@ -358,7 +356,7 @@
} }
.el-sub-menu__title { .el-sub-menu__title {
color: $menuText; color: var(--pure-theme-menu-text);
} }
} }
@ -366,31 +364,31 @@
.el-menu .el-sub-menu__title { .el-menu .el-sub-menu__title {
min-width: $sideBarWidth !important; min-width: $sideBarWidth !important;
font-size: 14px; font-size: 14px;
background-color: $subMenuBg !important; background-color: var(--pure-theme-sub-menu-bg) !important;
&:hover { &:hover {
color: $menuTitleHover !important; color: var(--pure-theme-menu-title-hover) !important;
} }
} }
.is-active > .el-sub-menu__title, .is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown { .is-active.submenu-title-noDropdown {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
i { i {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
} }
} }
.nest-menu .el-sub-menu > .el-sub-menu__title, .nest-menu .el-sub-menu > .el-sub-menu__title,
.el-menu-item { .el-menu-item {
&:hover { &:hover {
color: $menuTitleHover !important; color: var(--pure-theme-menu-title-hover) !important;
} }
} }
.el-menu-item.is-active { .el-menu-item.is-active {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
transition: color 0.3s; transition: color 0.3s;
} }
@ -415,7 +413,7 @@
justify-content: space-around; justify-content: space-around;
width: 100%; width: 100%;
height: 48px; height: 48px;
background: $menuBg; background: var(--pure-theme-menu-bg) !important;
.horizontal-header-left { .horizontal-header-left {
display: flex; display: flex;
@ -437,11 +435,11 @@
height: 32px; height: 32px;
margin: 2px 0 0 12px; margin: 2px 0 0 12px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
font-size: 18px; font-size: 18px;
font-weight: 600; font-weight: 600;
line-height: 32px; line-height: 32px;
color: $subMenuActiveText; color: var(--pure-theme-sub-menu-active-text);
text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
} }
@ -458,7 +456,7 @@
align-items: center; align-items: center;
justify-content: flex-end; justify-content: flex-end;
min-width: 340px; min-width: 340px;
color: $subMenuActiveText; color: var(--pure-theme-sub-menu-active-text);
/* 搜索 */ /* 搜索 */
.search-container, .search-container,
@ -471,13 +469,13 @@
/* 设置 */ /* 设置 */
.set-icon { .set-icon {
&:hover { &:hover {
background: $menuHover; background: var(--pure-theme-menu-hover);
} }
} }
.dropdown-badge { .dropdown-badge {
height: 48px; height: 48px;
color: $subMenuActiveText; color: var(--pure-theme-sub-menu-active-text);
} }
.el-dropdown-link { .el-dropdown-link {
@ -486,7 +484,7 @@
justify-content: space-around; justify-content: space-around;
height: 48px; height: 48px;
padding: 10px; padding: 10px;
color: $subMenuActiveText; color: var(--pure-theme-sub-menu-active-text);
cursor: pointer; cursor: pointer;
p { p {
@ -511,10 +509,10 @@
.el-menu-item, .el-menu-item,
.el-sub-menu__title { .el-sub-menu__title {
padding-right: var(--el-menu-base-level-padding); padding-right: var(--el-menu-base-level-padding);
color: $menuText; color: var(--pure-theme-menu-text);
&:hover { &:hover {
color: $menuTitleHover !important; color: var(--pure-theme-menu-title-hover) !important;
} }
} }
@ -522,7 +520,7 @@
.el-sub-menu__title { .el-sub-menu__title {
height: 48px; height: 48px;
line-height: 48px; line-height: 48px;
background: $menuBg; background: var(--pure-theme-menu-bg) !important;
svg { svg {
position: static !important; position: static !important;
@ -531,15 +529,15 @@
.is-active > .el-sub-menu__title, .is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown { .is-active.submenu-title-noDropdown {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
i { i {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
} }
} }
.is-active { .is-active {
color: $subMenuActiveText !important; color: var(--pure-theme-sub-menu-active-text) !important;
transition: color 0.3s; transition: color 0.3s;
} }
} }
@ -568,8 +566,8 @@
&.hideSidebar { &.hideSidebar {
.sidebar-container { .sidebar-container {
pointer-events: none; pointer-events: none;
transition-duration: 0.3s;
transform: translate3d(-$sideBarWidth, 0, 0); transform: translate3d(-$sideBarWidth, 0, 0);
transition-duration: 0.3s;
} }
} }
} }
@ -585,7 +583,7 @@ body[layout="vertical"] {
} }
.sidebar-logo-container { .sidebar-logo-container {
background: $sidebarLogo; background: var(--pure-theme-sidebar-logo);
} }
.hideSidebar { .hideSidebar {
@ -612,10 +610,10 @@ body[layout="vertical"] {
.el-sub-menu { .el-sub-menu {
& > .el-sub-menu__title { & > .el-sub-menu__title {
& > span { & > span {
visibility: visible;
width: 100%; width: 100%;
height: 100%; height: 100%;
text-align: center; text-align: center;
visibility: visible;
} }
} }
} }
@ -655,6 +653,10 @@ body[layout="horizontal"] {
@include merge-style($sideBarWidth); @include merge-style($sideBarWidth);
.el-menu {
--el-menu-hover-text-color: var(--pure-theme-menu-text) !important;
}
.fixed-header, .fixed-header,
.main-container { .main-container {
transition: none !important; transition: none !important;
@ -676,6 +678,7 @@ body[layout="mix"] {
.el-menu { .el-menu {
--el-menu-hover-bg-color: transparent !important; --el-menu-hover-bg-color: transparent !important;
--el-menu-hover-text-color: var(--pure-theme-menu-text) !important;
} }
.hideSidebar { .hideSidebar {
@ -704,10 +707,10 @@ body[layout="mix"] {
padding: 0; padding: 0;
& > span { & > span {
visibility: visible;
width: 100%; width: 100%;
height: 100%; height: 100%;
text-align: center; text-align: center;
visibility: visible;
} }
} }
} }

View File

@ -1,21 +1,46 @@
@tailwind base; @layer theme, base, components, utilities;
@tailwind components; @import "tailwindcss/theme.css" layer(theme);
@tailwind utilities; @import "tailwindcss/utilities.css" layer(utilities);
@layer components { @custom-variant dark (&:is(.dark *));
.flex-c {
@theme {
--color-bg_color: var(--el-bg-color);
--color-primary: var(--el-color-primary);
--color-text_color_primary: var(--el-text-color-primary);
--color-text_color_regular: var(--el-text-color-regular);
}
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}
@utility flex-c {
@apply flex justify-center items-center; @apply flex justify-center items-center;
} }
.flex-ac { @utility flex-ac {
@apply flex justify-around items-center; @apply flex justify-around items-center;
} }
.flex-bc { @utility flex-bc {
@apply flex justify-between items-center; @apply flex justify-between items-center;
} }
.navbar-bg-hover { @utility navbar-bg-hover {
@apply dark:text-white dark:hover:!bg-[#242424]; @apply dark:text-white dark:hover:bg-[#242424]!;
}
} }

95
src/style/theme.scss Normal file
View File

@ -0,0 +1,95 @@
/* 亮白色 */
html[data-theme="light"] {
--pure-theme-sub-menu-active-text: #000000d9;
--pure-theme-menu-bg: #fff;
--pure-theme-menu-hover: #f6f6f6;
--pure-theme-sub-menu-bg: #fff;
--pure-theme-menu-text: rgb(0 0 0 / 60%);
--pure-theme-sidebar-logo: #fff;
--pure-theme-menu-title-hover: #000;
--pure-theme-menu-active-before: #4091f7;
}
/* 道奇蓝 */
html[data-theme="default"] {
--pure-theme-sub-menu-active-text: #fff;
--pure-theme-menu-bg: #001529;
--pure-theme-menu-hover: rgb(64 145 247 / 15%);
--pure-theme-sub-menu-bg: #0f0303;
--pure-theme-menu-text: rgb(254 254 254 / 65%);
--pure-theme-sidebar-logo: #002140;
--pure-theme-menu-title-hover: #fff;
--pure-theme-menu-active-before: #4091f7;
}
/* 深紫罗兰色 */
html[data-theme="saucePurple"] {
--pure-theme-sub-menu-active-text: #fff;
--pure-theme-menu-bg: #130824;
--pure-theme-menu-hover: rgb(105 58 201 / 15%);
--pure-theme-sub-menu-bg: #000;
--pure-theme-menu-text: #7a80b4;
--pure-theme-sidebar-logo: #1f0c38;
--pure-theme-menu-title-hover: #fff;
--pure-theme-menu-active-before: #693ac9;
}
/* 深粉色 */
html[data-theme="pink"] {
--pure-theme-sub-menu-active-text: #fff;
--pure-theme-menu-bg: #28081a;
--pure-theme-menu-hover: rgb(216 68 147 / 15%);
--pure-theme-sub-menu-bg: #000;
--pure-theme-menu-text: #7a80b4;
--pure-theme-sidebar-logo: #3f0d29;
--pure-theme-menu-title-hover: #fff;
--pure-theme-menu-active-before: #d84493;
}
/* 猩红色 */
html[data-theme="dusk"] {
--pure-theme-sub-menu-active-text: #fff;
--pure-theme-menu-bg: #2a0608;
--pure-theme-menu-hover: rgb(225 60 57 / 15%);
--pure-theme-sub-menu-bg: #000;
--pure-theme-menu-text: rgb(254 254 254 / 65.1%);
--pure-theme-sidebar-logo: #42090c;
--pure-theme-menu-title-hover: #fff;
--pure-theme-menu-active-before: #e13c39;
}
/* 橙红色 */
html[data-theme="volcano"] {
--pure-theme-sub-menu-active-text: #fff;
--pure-theme-menu-bg: #2b0e05;
--pure-theme-menu-hover: rgb(232 95 51 / 15%);
--pure-theme-sub-menu-bg: #0f0603;
--pure-theme-menu-text: rgb(254 254 254 / 65%);
--pure-theme-sidebar-logo: #441708;
--pure-theme-menu-title-hover: #fff;
--pure-theme-menu-active-before: #e85f33;
}
/* 绿宝石 */
html[data-theme="mingQing"] {
--pure-theme-sub-menu-active-text: #fff;
--pure-theme-menu-bg: #032121;
--pure-theme-menu-hover: rgb(89 191 193 / 15%);
--pure-theme-sub-menu-bg: #000;
--pure-theme-menu-text: #7a80b4;
--pure-theme-sidebar-logo: #053434;
--pure-theme-menu-title-hover: #fff;
--pure-theme-menu-active-before: #59bfc1;
}
/* 酸橙绿 */
html[data-theme="auroraGreen"] {
--pure-theme-sub-menu-active-text: #fff;
--pure-theme-menu-bg: #0b1e15;
--pure-theme-menu-hover: rgb(96 172 128 / 15%);
--pure-theme-sub-menu-bg: #000;
--pure-theme-menu-text: #7a80b4;
--pure-theme-sidebar-logo: #112f21;
--pure-theme-menu-title-hover: #fff;
--pure-theme-menu-active-before: #60ac80;
}

View File

@ -8,6 +8,8 @@ type messageTypes = "info" | "success" | "warning" | "error";
interface MessageParams { interface MessageParams {
/** 消息类型,可选 `info` 、`success` 、`warning` 、`error` ,默认 `info` */ /** 消息类型,可选 `info` 、`success` 、`warning` 、`error` ,默认 `info` */
type?: messageTypes; type?: messageTypes;
/** 是否纯色,默认 `false` */
plain?: boolean;
/** 自定义图标,该属性会覆盖 `type` 的图标 */ /** 自定义图标,该属性会覆盖 `type` 的图标 */
icon?: any; icon?: any;
/** 是否将 `message` 属性作为 `HTML` 片段处理,默认 `false` */ /** 是否将 `message` 属性作为 `HTML` 片段处理,默认 `false` */
@ -18,14 +20,14 @@ interface MessageParams {
duration?: number; duration?: number;
/** 是否显示关闭按钮,默认值 `false` */ /** 是否显示关闭按钮,默认值 `false` */
showClose?: boolean; showClose?: boolean;
/** 文字是否居中,默认值 `false` */ /** `Message` 距离窗口顶部的偏移量,默认 `16` */
center?: boolean;
/** `Message` 距离窗口顶部的偏移量,默认 `20` */
offset?: number; offset?: number;
/** 设置组件的根元素,默认 `document.body` */ /** 设置组件的根元素,默认 `document.body` */
appendTo?: string | HTMLElement; appendTo?: string | HTMLElement;
/** 合并内容相同的消息,不支持 `VNode` 类型的消息,默认值 `false` */ /** 合并内容相同的消息,不支持 `VNode` 类型的消息,默认值 `false` */
grouping?: boolean; grouping?: boolean;
/** 重复次数,类似于 `Badge` 。当和 `grouping` 属性一起使用时作为初始数量使用,默认值 `1` */
repeatNum?: number;
/** 关闭时的回调函数, 参数为被关闭的 `message` 实例 */ /** 关闭时的回调函数, 参数为被关闭的 `message` 实例 */
onClose?: Function | null; onClose?: Function | null;
} }
@ -48,28 +50,30 @@ const message = (
const { const {
icon, icon,
type = "info", type = "info",
plain = false,
dangerouslyUseHTMLString = false, dangerouslyUseHTMLString = false,
customClass = "antd", customClass = "antd",
duration = 2000, duration = 2000,
showClose = false, showClose = false,
center = false, offset = 16,
offset = 20,
appendTo = document.body, appendTo = document.body,
grouping = false, grouping = false,
repeatNum = 1,
onClose onClose
} = params; } = params;
return ElMessage({ return ElMessage({
message, message,
type,
icon, icon,
type,
plain,
dangerouslyUseHTMLString, dangerouslyUseHTMLString,
duration, duration,
showClose, showClose,
center,
offset, offset,
appendTo, appendTo,
grouping, grouping,
repeatNum,
// 全局搜 pure-message 即可知道该类的样式位置 // 全局搜 pure-message 即可知道该类的样式位置
customClass: customClass === "antd" ? "pure-message" : "", customClass: customClass === "antd" ? "pure-message" : "",
onClose: () => (isFunction(onClose) ? onClose() : null) onClose: () => (isFunction(onClose) ? onClose() : null)

View File

@ -4,10 +4,11 @@ import mitt from "mitt";
/** 全局公共事件需要在此处添加类型 */ /** 全局公共事件需要在此处添加类型 */
type Events = { type Events = {
openPanel: string; openPanel: string;
tagViewsChange: string; tagOnClick: string;
tagViewsShowModel: string;
logoChange: boolean; logoChange: boolean;
tagViewsChange: string;
changLayoutRoute: string; changLayoutRoute: string;
tagViewsShowModel: string;
}; };
export const emitter: Emitter<Events> = mitt<Events>(); export const emitter: Emitter<Events> = mitt<Events>();

View File

@ -9,6 +9,7 @@ const Print = function (dom, options?: object): PrintFunction {
options = options || {}; options = options || {};
// @ts-expect-error // @ts-expect-error
if (!(this instanceof Print)) return new Print(dom, options); if (!(this instanceof Print)) return new Print(dom, options);
// @ts-expect-error
this.conf = { this.conf = {
styleStr: "", styleStr: "",
// Elements that need to dynamically get and set the height // Elements that need to dynamically get and set the height
@ -18,19 +19,26 @@ const Print = function (dom, options?: object): PrintFunction {
// Callback after printing // Callback after printing
printDoneCallBack: null printDoneCallBack: null
}; };
// @ts-expect-error
for (const key in this.conf) { for (const key in this.conf) {
if (key && options.hasOwnProperty(key)) { if (key && options.hasOwnProperty(key)) {
// @ts-expect-error
this.conf[key] = options[key]; this.conf[key] = options[key];
} }
} }
if (typeof dom === "string") { if (typeof dom === "string") {
// @ts-expect-error
this.dom = document.querySelector(dom); this.dom = document.querySelector(dom);
} else { } else {
// @ts-expect-error
this.dom = this.isDOM(dom) ? dom : dom.$el; this.dom = this.isDOM(dom) ? dom : dom.$el;
} }
// @ts-expect-error
if (this.conf.setDomHeightArr && this.conf.setDomHeightArr.length) { if (this.conf.setDomHeightArr && this.conf.setDomHeightArr.length) {
// @ts-expect-error
this.setDomHeight(this.conf.setDomHeightArr); this.setDomHeight(this.conf.setDomHeightArr);
} }
// @ts-expect-error
this.init(); this.init();
}; };
@ -132,8 +140,10 @@ Print.prototype = {
"position:absolute;width:0;height:0;top:-10px;left:-10px;" "position:absolute;width:0;height:0;top:-10px;left:-10px;"
); );
// eslint-disable-next-line prefer-const
w = f.contentWindow || f.contentDocument; w = f.contentWindow || f.contentDocument;
// eslint-disable-next-line prefer-const
doc = f.contentDocument || f.contentWindow.document; doc = f.contentDocument || f.contentWindow.document;
doc.open(); doc.open();
doc.write(content); doc.write(content);
@ -172,7 +182,7 @@ Print.prototype = {
if (!frameWindow.document.execCommand("print", false, null)) { if (!frameWindow.document.execCommand("print", false, null)) {
frameWindow.print(); frameWindow.print();
} }
} catch (e) { } catch {
frameWindow.print(); frameWindow.print();
} }
frameWindow.close(); frameWindow.close();

View File

@ -15,7 +15,7 @@ const router = useRouter();
<div class="ml-12"> <div class="ml-12">
<p <p
v-motion v-motion
class="font-medium text-4xl mb-4 dark:text-white" class="font-medium text-4xl mb-4! dark:text-white"
:initial="{ :initial="{
opacity: 0, opacity: 0,
y: 100 y: 100
@ -32,7 +32,7 @@ const router = useRouter();
</p> </p>
<p <p
v-motion v-motion
class="mb-4 text-gray-500" class="mb-4! text-gray-500"
:initial="{ :initial="{
opacity: 0, opacity: 0,
y: 100 y: 100

View File

@ -15,7 +15,7 @@ const router = useRouter();
<div class="ml-12"> <div class="ml-12">
<p <p
v-motion v-motion
class="font-medium text-4xl mb-4 dark:text-white" class="font-medium text-4xl mb-4! dark:text-white"
:initial="{ :initial="{
opacity: 0, opacity: 0,
y: 100 y: 100
@ -32,7 +32,7 @@ const router = useRouter();
</p> </p>
<p <p
v-motion v-motion
class="mb-4 text-gray-500" class="mb-4! text-gray-500"
:initial="{ :initial="{
opacity: 0, opacity: 0,
y: 100 y: 100

View File

@ -15,7 +15,7 @@ const router = useRouter();
<div class="ml-12"> <div class="ml-12">
<p <p
v-motion v-motion
class="font-medium text-4xl mb-4 dark:text-white" class="font-medium text-4xl mb-4! dark:text-white"
:initial="{ :initial="{
opacity: 0, opacity: 0,
y: 100 y: 100
@ -32,7 +32,7 @@ const router = useRouter();
</p> </p>
<p <p
v-motion v-motion
class="mb-4 text-gray-500" class="mb-4! text-gray-500"
:initial="{ :initial="{
opacity: 0, opacity: 0,
y: 100 y: 100

View File

@ -3,26 +3,30 @@ import Motion from "./utils/motion";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import { loginRules } from "./utils/rule"; import { loginRules } from "./utils/rule";
import { ref, reactive, toRaw } from "vue";
import { debounce } from "@pureadmin/utils";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { useEventListener } from "@vueuse/core";
import type { FormInstance } from "element-plus"; import type { FormInstance } from "element-plus";
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 { initRouter, getTopMenu } from "@/router/utils";
import { bg, avatar, illustration } from "./utils/static"; import { bg, avatar, illustration } from "./utils/static";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
import { ref, reactive, toRaw, onMounted, onBeforeUnmount } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import dayIcon from "@/assets/svg/day.svg?component"; import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component"; import darkIcon from "@/assets/svg/dark.svg?component";
import Lock from "@iconify-icons/ri/lock-fill"; import Lock from "~icons/ri/lock-fill";
import User from "@iconify-icons/ri/user-3-fill"; import User from "~icons/ri/user-3-fill";
defineOptions({ defineOptions({
name: "Login" name: "Login"
}); });
const router = useRouter(); const router = useRouter();
const loading = ref(false); const loading = ref(false);
const disabled = ref(false);
const ruleFormRef = ref<FormInstance>(); const ruleFormRef = ref<FormInstance>();
const { initStorage } = useLayout(); const { initStorage } = useLayout();
@ -39,18 +43,25 @@ const ruleForm = reactive({
const onLogin = async (formEl: FormInstance | undefined) => { const onLogin = async (formEl: FormInstance | undefined) => {
if (!formEl) return; if (!formEl) return;
await formEl.validate((valid, fields) => { await formEl.validate(valid => {
if (valid) { if (valid) {
loading.value = true; loading.value = true;
useUserStoreHook() useUserStoreHook()
.loginByUsername({ username: ruleForm.username, password: "admin123" }) .loginByUsername({
username: ruleForm.username,
password: ruleForm.password
})
.then(res => { .then(res => {
if (res.success) { if (res.success) {
// //
return initRouter().then(() => { return initRouter().then(() => {
router.push(getTopMenu(true).path).then(() => { disabled.value = true;
router
.push(getTopMenu(true).path)
.then(() => {
message("登录成功", { type: "success" }); message("登录成功", { type: "success" });
}); })
.finally(() => (disabled.value = false));
}); });
} else { } else {
message("登录失败", { type: "error" }); message("登录失败", { type: "error" });
@ -61,19 +72,19 @@ const onLogin = async (formEl: FormInstance | undefined) => {
}); });
}; };
/** 使用公共函数,避免`removeEventListener`失效 */ const immediateDebounce: any = debounce(
function onkeypress({ code }: KeyboardEvent) { formRef => onLogin(formRef),
if (["Enter", "NumpadEnter"].includes(code)) { 1000,
onLogin(ruleFormRef.value); true
} );
}
onMounted(() => { useEventListener(document, "keydown", ({ code }) => {
window.document.addEventListener("keypress", onkeypress); if (
}); ["Enter", "NumpadEnter"].includes(code) &&
!disabled.value &&
onBeforeUnmount(() => { !loading.value
window.document.removeEventListener("keypress", onkeypress); )
immediateDebounce(ruleFormRef.value);
}); });
</script> </script>
@ -98,7 +109,7 @@ onBeforeUnmount(() => {
<div class="login-form"> <div class="login-form">
<avatar class="avatar" /> <avatar class="avatar" />
<Motion> <Motion>
<h2 class="outline-none">{{ title }}</h2> <h2 class="outline-hidden">{{ title }}</h2>
</Motion> </Motion>
<el-form <el-form
@ -141,10 +152,11 @@ onBeforeUnmount(() => {
<Motion :delay="250"> <Motion :delay="250">
<el-button <el-button
class="w-full mt-4" class="w-full mt-4!"
size="default" size="default"
type="primary" type="primary"
:loading="loading" :loading="loading"
:disabled="disabled"
@click="onLogin(ruleFormRef)" @click="onLogin(ruleFormRef)"
> >
登录 登录

View File

@ -6,7 +6,7 @@ export const REGEXP_PWD =
/^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/; /^(?![0-9]+$)(?![a-z]+$)(?![A-Z]+$)(?!([^(0-9a-zA-Z)]|[()])+$)(?!^.*[\u4E00-\u9FA5].*$)([^(0-9a-zA-Z)]|[()]|[a-z]|[A-Z]|[0-9]){8,18}$/;
/** 登录校验 */ /** 登录校验 */
const loginRules = reactive(<FormRules>{ const loginRules = reactive<FormRules>({
password: [ password: [
{ {
validator: (rule, value, callback) => { validator: (rule, value, callback) => {

View File

@ -8,7 +8,7 @@ defineOptions({
<template> <template>
<div> <div>
<p class="mb-2">当前拥有的code列表{{ getAuths() }}</p> <p class="mb-2!">当前拥有的code列表{{ getAuths() }}</p>
<el-card shadow="never" class="mb-2"> <el-card shadow="never" class="mb-2">
<template #header> <template #header>

View File

@ -11,8 +11,8 @@ defineOptions({
<template> <template>
<div> <div>
<p class="mb-2">当前拥有的code列表{{ permissions }}</p> <p class="mb-2!">当前拥有的code列表{{ permissions }}</p>
<p v-show="permissions?.[0] === '*:*:*'" class="mb-2"> <p v-show="permissions?.[0] === '*:*:*'" class="mb-2!">
*:*:* 代表拥有全部按钮级别权限 *:*:* 代表拥有全部按钮级别权限
</p> </p>

View File

@ -44,7 +44,7 @@ function onChange() {
<template> <template>
<div> <div>
<p class="mb-2"> <p class="mb-2!">
模拟后台根据不同角色返回对应路由观察左侧菜单变化管理员角色可查看系统管理菜单普通角色不可查看系统管理菜单 模拟后台根据不同角色返回对应路由观察左侧菜单变化管理员角色可查看系统管理菜单普通角色不可查看系统管理菜单
</p> </p>
<el-card shadow="never" :style="elStyle"> <el-card shadow="never" :style="elStyle">
@ -53,7 +53,7 @@ function onChange() {
<span>当前角色{{ username }}</span> <span>当前角色{{ username }}</span>
</div> </div>
</template> </template>
<el-select v-model="username" class="!w-[160px]" @change="onChange"> <el-select v-model="username" class="w-[160px]!" @change="onChange">
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.value" :key="item.value"

View File

@ -1,19 +0,0 @@
import type { Config } from "tailwindcss";
export default {
darkMode: "class",
corePlugins: {
preflight: false
},
content: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"],
theme: {
extend: {
colors: {
bg_color: "var(--el-bg-color)",
primary: "var(--el-color-primary)",
text_color_primary: "var(--el-text-color-primary)",
text_color_regular: "var(--el-text-color-regular)"
}
}
}
} satisfies Config;

View File

@ -4,10 +4,11 @@
"module": "ESNext", "module": "ESNext",
"moduleResolution": "bundler", "moduleResolution": "bundler",
"strict": false, "strict": false,
"strictFunctionTypes": false,
"noImplicitThis": true,
"jsx": "preserve", "jsx": "preserve",
"importHelpers": true, "importHelpers": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"strictFunctionTypes": false,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": true, "esModuleInterop": true,
"isolatedModules": true, "isolatedModules": true,
@ -34,6 +35,7 @@
"vite/client", "vite/client",
"element-plus/global", "element-plus/global",
"@pureadmin/table/volar", "@pureadmin/table/volar",
"unplugin-icons/types/vue",
"@pureadmin/descriptions/volar" "@pureadmin/descriptions/volar"
] ]
}, },

View File

@ -120,6 +120,7 @@ declare module "vue" {
} }
interface ComponentCustomProperties { interface ComponentCustomProperties {
$storage: ResponsiveStorage;
$message: (typeof import("element-plus"))["ElMessage"]; $message: (typeof import("element-plus"))["ElMessage"];
$notify: (typeof import("element-plus"))["ElNotification"]; $notify: (typeof import("element-plus"))["ElNotification"];
$msgbox: (typeof import("element-plus"))["ElMessageBox"]; $msgbox: (typeof import("element-plus"))["ElMessageBox"];

8
types/global.d.ts vendored
View File

@ -61,7 +61,7 @@ declare global {
/** /**
* *
* @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#%E5%85%B7%E4%BD%93%E9%85%8D%E7%BD%AE} * @see {@link https://pure-admin.cn/pages/config/#%E5%85%B7%E4%BD%93%E9%85%8D%E7%BD%AE}
*/ */
interface ViteEnv { interface ViteEnv {
VITE_PORT: number; VITE_PORT: number;
@ -75,11 +75,11 @@ declare global {
/** /**
* `@pureadmin/table` `TableColumns` 便 * `@pureadmin/table` `TableColumns` 便
*/ */
interface TableColumnList extends Array<TableColumns> {} type TableColumnList = Array<TableColumns>;
/** /**
* `public/platform-config.json` * `public/platform-config.json`
* @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json} * @see {@link https://pure-admin.cn/pages/config/#platform-config-json}
*/ */
interface PlatformConfigs { interface PlatformConfigs {
Version?: string; Version?: string;
@ -112,7 +112,7 @@ declare global {
/** /**
* `PlatformConfigs` * `PlatformConfigs`
* @see {@link https://pure-admin.github.io/pure-admin-doc/pages/config/#platform-config-json} * @see {@link https://pure-admin.cn/pages/config/#platform-config-json}
*/ */
interface StorageConfigs { interface StorageConfigs {
version?: string; version?: string;

2
types/index.d.ts vendored
View File

@ -75,8 +75,6 @@ interface ComponentElRef<T extends HTMLElement = HTMLDivElement> {
$el: T; $el: T;
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function parseInt(s: string | number, radix?: number): number; function parseInt(s: string | number, radix?: number): number;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function parseFloat(string: string | number): number; function parseFloat(string: string | number): number;

7
types/router.d.ts vendored
View File

@ -15,9 +15,9 @@ declare global {
/** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加) `必填` */ /** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加) `必填` */
title: string; title: string;
/** 菜单图标 `可选` */ /** 菜单图标 `可选` */
icon?: string | FunctionalComponent | IconifyIcon; icon?: string | FunctionalComponent;
/** 菜单名称右侧的额外图标 */ /** 菜单名称右侧的额外图标 */
extraIcon?: string | FunctionalComponent | IconifyIcon; extraIcon?: string | FunctionalComponent;
/** 是否在菜单中显示(默认`true``可选` */ /** 是否在菜单中显示(默认`true``可选` */
showLink?: boolean; showLink?: boolean;
/** 是否显示父级菜单 `可选` */ /** 是否显示父级菜单 `可选` */
@ -91,7 +91,7 @@ declare global {
/** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加)`必填` */ /** 菜单名称(兼容国际化、非国际化,如何用国际化的写法就必须在根目录的`locales`文件夹下对应添加)`必填` */
title: string; title: string;
/** 菜单图标 `可选` */ /** 菜单图标 `可选` */
icon?: string | FunctionalComponent | IconifyIcon; icon?: string | FunctionalComponent;
/** 是否在菜单中显示(默认`true``可选` */ /** 是否在菜单中显示(默认`true``可选` */
showLink?: boolean; showLink?: boolean;
/** 菜单升序排序,值越高排的越后(只针对顶级路由)`可选` */ /** 菜单升序排序,值越高排的越后(只针对顶级路由)`可选` */
@ -104,5 +104,6 @@ declare global {
// https://router.vuejs.org/zh/guide/advanced/meta.html#typescript // https://router.vuejs.org/zh/guide/advanced/meta.html#typescript
declare module "vue-router" { declare module "vue-router" {
// eslint-disable-next-line
interface RouteMeta extends CustomizeRouteMeta {} interface RouteMeta extends CustomizeRouteMeta {}
} }

View File

@ -1,4 +1,4 @@
import Vue, { VNode } from "vue"; import type Vue, { type VNode } from "vue";
declare module "*.tsx" { declare module "*.tsx" {
import Vue from "compatible-vue"; import Vue from "compatible-vue";
@ -7,7 +7,9 @@ declare module "*.tsx" {
declare global { declare global {
namespace JSX { namespace JSX {
// eslint-disable-next-line
interface Element extends VNode {} interface Element extends VNode {}
// eslint-disable-next-line
interface ElementClass extends Vue {} interface ElementClass extends Vue {}
interface ElementAttributesProperty { interface ElementAttributesProperty {
$props: any; $props: any;

View File

@ -1,5 +1,6 @@
declare module "*.vue" { declare module "*.vue" {
import type { DefineComponent } from "vue"; import type { DefineComponent } from "vue";
// eslint-disable-next-line
const component: DefineComponent<{}, {}, any>; const component: DefineComponent<{}, {}, any>;
export default component; export default component;
} }