Merge remote-tracking branch 'origin/main' into gitee

This commit is contained in:
xiaoxian521 2024-01-02 19:52:43 +08:00
commit 4fb186424c
162 changed files with 4345 additions and 4437 deletions

View File

@ -3,11 +3,11 @@ dist
*.d.ts
/src/assets
package.json
.eslintrc.js
eslint.config.js
.prettierrc.js
commitlint.config.js
postcss.config.js
tailwind.config.js
tailwind.config.ts
stylelint.config.js
src/components/ReSplitPane/iconfont
src/components/ReFlowChart/src/assets/iconfont

View File

@ -1,120 +0,0 @@
module.exports = {
root: true,
env: {
node: true
},
globals: {
// Ref sugar (take 2)
$: "readonly",
$$: "readonly",
$ref: "readonly",
$shallowRef: "readonly",
$computed: "readonly",
// index.d.ts
// global.d.ts
Fn: "readonly",
PromiseFn: "readonly",
RefType: "readonly",
LabelValueOptions: "readonly",
EmitType: "readonly",
TargetContext: "readonly",
ComponentElRef: "readonly",
ComponentRef: "readonly",
ElRef: "readonly",
global: "readonly",
ForDataType: "readonly",
ComponentRoutes: "readonly",
// script setup
defineProps: "readonly",
defineEmits: "readonly",
defineExpose: "readonly",
withDefaults: "readonly"
},
extends: [
"plugin:vue/vue3-essential",
"eslint:recommended",
"@vue/typescript/recommended",
"@vue/prettier",
"@vue/eslint-config-typescript"
],
parser: "vue-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
ecmaVersion: 2020,
sourceType: "module",
jsxPragma: "React",
ecmaFeatures: {
jsx: true
}
},
overrides: [
{
files: ["*.ts", "*.vue"],
rules: {
"no-undef": "off"
}
},
{
files: ["*.vue"],
parser: "vue-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
extraFileExtensions: [".vue"],
ecmaVersion: "latest",
ecmaFeatures: {
jsx: true
}
},
rules: {
"no-undef": "off"
}
}
],
rules: {
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
"vue/require-explicit-emits": "off",
"vue/multi-word-component-names": "off",
"@typescript-eslint/no-explicit-any": "off", // any
"no-debugger": "off",
"@typescript-eslint/explicit-module-boundary-types": "off", // setup()
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "always",
component: "always"
},
svg: "always",
math: "always"
}
],
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
],
"no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
],
"prettier/prettier": [
"error",
{
endOfLine: "auto"
}
]
}
}

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ node_modules
*.local
.eslintcache
report.html
vite.config.*.timestamp*
yarn.lock
npm-debug.log*

View File

@ -3,7 +3,7 @@
"prettier --cache --ignore-unknown --write",
"eslint --cache --fix"
],
"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
"{!(package)*.json,*.code-snippets,.!({browserslist,nvm})*rc}": [
"prettier --cache --write--parser json"
],
"package.json": ["prettier --cache --write"],

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
v20.10.0

View File

@ -1,4 +1,7 @@
module.exports = {
// @ts-check
/** @type {import("prettier").Config} */
export default {
bracketSpacing: true,
singleQuote: false,
arrowParens: "avoid",

View File

@ -25,7 +25,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"i18n-ally.localesPaths": "locales",
"i18n-ally.keystyle": "nested",

View File

@ -1,8 +1,8 @@
FROM node:16-alpine as build-stage
FROM node:18-alpine as build-stage
WORKDIR /app
RUN corepack enable
RUN corepack prepare pnpm@7.32.1 --activate
RUN corepack prepare pnpm@8.6.10 --activate
RUN npm config set registry https://registry.npmmirror.com

View File

@ -8,7 +8,7 @@
## Introduction
`vue-pure-admin` is an open source free and out-of-the-box middle and background management system template. Developed using the latest mainstream technologies such as `Vue3`, `Vite`, `Element-Plus`, `TypeScript`, `Pinia`, `Tailwindcss`
`vue-pure-admin` is an open source, free and out-of-the-box middle and backend management system template. Completely adopts `ECMAScript` module (`ESM`) specifications to write and organize code, using the latest `Vue3`, `Vite`, `Element-Plus`, `TypeScript`, `Pinia`, `Tailwindcss` and other mainstream technologies develop
## Thin version (offering non-internationalized and internationalized versions)
@ -109,7 +109,7 @@ After operating the above two commands, open `http://localhost:8080` in the brow
Of course, you can also operate the `docker` project through the [Docker Desktop](https://www.docker.com/products/docker-desktop/) visual interface, as shown below
<p align="center">
<img alt="docker" width="100%" src="https://yiming_chang.gitee.io/pure-admin-doc/img/docker/1.jpg">
<img alt="docker" width="100%" src="https://xiaoxian521.github.io/hyperlink/img/docker-desktop.jpg">
</p>
## Change Log
@ -162,7 +162,7 @@ Support modern browsers, not IE
## License
In principle, no fees and copyrights are charged, and it is commercially available, but if you need secondary open source (such as using this platform for secondary development and open source, the front-end code must be open source and free), please contact the author for permission! (Free, just take a record)
Completely free and open source
[MIT © 2020-present, pure-admin](./LICENSE)
@ -170,10 +170,10 @@ In principle, no fees and copyrights are charged, and it is commercially availab
Many thanks to the kind individuals who leave a star. Your support is much appreciated :heart:
[![Stargazers for vue-pure-admin](https://reporoster.com/stars/pure-admin/vue-pure-admin)](https://github.com/pure-admin/vue-pure-admin/stargazers)
[![Stargazers repo roster for @pure-admin/vue-pure-admin](https://bytecrank.com/nastyox/reporoster/php/stargazersSVG.php?user=pure-admin&repo=vue-pure-admin)](https://github.com/pure-admin/vue-pure-admin/stargazers)
## `Fork`
It's so cool that you study hard :heart:
[![Forkers repo roster for vue-pure-admin](https://reporoster.com/forks/pure-admin/vue-pure-admin)](https://github.com/pure-admin/vue-pure-admin/network/members)
[![Forkers repo roster for @pure-admin/vue-pure-admin](https://bytecrank.com/nastyox/reporoster/php/forkersSVG.php?user=pure-admin&repo=vue-pure-admin)](https://github.com/pure-admin/vue-pure-admin/network/members)

View File

@ -8,7 +8,7 @@
## 简介
`vue-pure-admin` 是一款开源免费且开箱即用的中后台管理系统模版。使用了最新的 `Vue3``Vite``Element-Plus``TypeScript``Pinia``Tailwindcss` 等主流技术开发
`vue-pure-admin` 是一款开源免费且开箱即用的中后台管理系统模版。完全采用 `ECMAScript` 模块(`ESM`)规范来编写和组织代码,使用了最新的 `Vue3``Vite``Element-Plus``TypeScript``Pinia``Tailwindcss` 等主流技术开发
## 精简版(实际项目开发请用精简版,提供 `非国际化``国际化` 两个版本选择)
@ -109,7 +109,7 @@ docker run -dp 8080:80 --name pure-admin vue-pure-admin
当然也可以通过 [Docker Desktop](https://www.docker.com/products/docker-desktop/) 可视化界面去操作 `docker` 项目,如下图
<p align="center">
<img alt="docker" width="100%" src="https://yiming_chang.gitee.io/pure-admin-doc/img/docker/1.jpg">
<img alt="docker" width="100%" src="https://xiaoxian521.github.io/hyperlink/img/docker-desktop.jpg">
</p>
## 更新日志
@ -162,7 +162,7 @@ docker run -dp 8080:80 --name pure-admin vue-pure-admin
## 许可证
原则上不收取任何费用及版权,可商用,不过如需二次开源(比如用此平台二次开发并开源,要求前端代码必须开源免费)请联系作者获取许可!(免费,走个记录而已)
完全免费开源
[MIT © 2020-present, pure-admin](./LICENSE)
@ -170,10 +170,10 @@ docker run -dp 8080:80 --name pure-admin vue-pure-admin
非常感谢留下星星的好心人,感谢您的支持 :heart:
[![Stargazers for vue-pure-admin](https://reporoster.com/stars/pure-admin/vue-pure-admin)](https://github.com/pure-admin/vue-pure-admin/stargazers)
[![Stargazers repo roster for @pure-admin/vue-pure-admin](https://bytecrank.com/nastyox/reporoster/php/stargazersSVG.php?user=pure-admin&repo=vue-pure-admin)](https://github.com/pure-admin/vue-pure-admin/stargazers)
## `Fork`
瞧,那些 `小哥哥``小姐姐` 认真 `学习` 的样子真滴是 `哎呦不错哦` :heart:
[![Forkers repo roster for vue-pure-admin](https://reporoster.com/forks/pure-admin/vue-pure-admin)](https://github.com/pure-admin/vue-pure-admin/network/members)
[![Forkers repo roster for @pure-admin/vue-pure-admin](https://bytecrank.com/nastyox/reporoster/php/forkersSVG.php?user=pure-admin&repo=vue-pure-admin)](https://github.com/pure-admin/vue-pure-admin/network/members)

View File

@ -3,7 +3,6 @@ import { Plugin as importToCDN } from "vite-plugin-cdn-import";
/**
* @description `cdn`使cdn模式 .env.production VITE_CDN true
* cdnhttps://www.bootcdn.cn当然你也可以选择 https://unpkg.com 或者 https://www.jsdelivr.com
* mockjs不能用cdn模式引入mockjs使
* 使jscss文件cdn
*/
export const cdn = importToCDN({

View File

@ -1,7 +1,7 @@
import type { Plugin } from "vite";
import picocolors from "picocolors";
import dayjs, { Dayjs } from "dayjs";
import { getPackageSize } from "./utils";
import dayjs, { type Dayjs } from "dayjs";
import duration from "dayjs/plugin/duration";
dayjs.extend(duration);

View File

@ -3,36 +3,45 @@ import { resolve } from "path";
import vue from "@vitejs/plugin-vue";
import { viteBuildInfo } from "./info";
import svgLoader from "vite-svg-loader";
import type { PluginOption } from "vite";
import vueJsx from "@vitejs/plugin-vue-jsx";
import { viteMockServe } from "vite-plugin-mock";
import { configCompressPlugin } from "./compress";
import removeNoMatch from "vite-plugin-router-warn";
import { visualizer } from "rollup-plugin-visualizer";
import removeConsole from "vite-plugin-remove-console";
import { themePreprocessorPlugin } from "@pureadmin/theme";
import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
import { genScssMultipleScopeVars } from "../src/layout/theme";
import { vitePluginFakeServer } from "vite-plugin-fake-server";
export function getPluginsList(
command: string,
VITE_CDN: boolean,
VITE_COMPRESSION: ViteCompression
) {
const prodMock = true;
): PluginOption[] {
const lifecycle = process.env.npm_lifecycle_event;
return [
vue(),
// jsx、tsx语法支持
vueJsx(),
VueI18nPlugin({
runtimeOnly: true,
compositionOnly: true,
include: [resolve("locales/**")]
}),
// jsx、tsx语法支持
vueJsx(),
VITE_CDN ? cdn : null,
configCompressPlugin(VITE_COMPRESSION),
// 线上环境删除console
removeConsole({ external: ["src/assets/iconfont/iconfont.js"] }),
viteBuildInfo(),
/**
* vue-router动态路由警告No match found for location with path
* https://github.com/vuejs/router/issues/521 和 https://github.com/vuejs/router/issues/359
* vite-plugin-router-warn只在开发环境下启用vue-router文件并且只在服务启动或重启时运行一次
*/
removeNoMatch(),
// mock支持
vitePluginFakeServer({
logger: false,
include: "mock",
infixName: false,
enableProd: true
}),
// 自定义主题
themePreprocessorPlugin({
scss: {
@ -42,20 +51,13 @@ export function getPluginsList(
}),
// svg组件化支持
svgLoader(),
// mock支持
viteMockServe({
mockPath: "mock",
localEnabled: command === "serve",
prodEnabled: command !== "serve" && prodMock,
injectCode: `
import { setupProdMockServer } from './mockProdServer';
setupProdMockServer();
`,
logger: false
}),
VITE_CDN ? cdn : null,
configCompressPlugin(VITE_COMPRESSION),
// 线上环境删除console
removeConsole({ external: ["src/assets/iconfont/iconfont.js"] }),
// 打包分析
lifecycle === "report"
? visualizer({ open: true, brotliSize: true, filename: "report.html" })
: null
: (null as any)
];
}

View File

@ -1,4 +1,7 @@
module.exports = {
// @ts-check
/** @type {import("@commitlint/types").UserConfig} */
export default {
ignores: [commit => commit.includes("init")],
extends: ["@commitlint/config-conventional"],
rules: {

174
eslint.config.js Normal file
View File

@ -0,0 +1,174 @@
import js from "@eslint/js";
import pluginVue from "eslint-plugin-vue";
import * as parserVue from "vue-eslint-parser";
import configPrettier from "eslint-config-prettier";
import pluginPrettier from "eslint-plugin-prettier";
import { defineFlatConfig } from "eslint-define-config";
import * as parserTypeScript from "@typescript-eslint/parser";
import pluginTypeScript from "@typescript-eslint/eslint-plugin";
export default defineFlatConfig([
{
...js.configs.recommended,
ignores: ["src/assets/**", "src/**/iconfont/**"],
languageOptions: {
globals: {
// index.d.ts
RefType: "readonly",
EmitType: "readonly",
TargetContext: "readonly",
ComponentRef: "readonly",
ElRef: "readonly",
ForDataType: "readonly",
AnyFunction: "readonly",
PropType: "readonly",
Writable: "readonly",
Nullable: "readonly",
NonNullable: "readonly",
Recordable: "readonly",
ReadonlyRecordable: "readonly",
Indexable: "readonly",
DeepPartial: "readonly",
Without: "readonly",
Exclusive: "readonly",
TimeoutHandle: "readonly",
IntervalHandle: "readonly",
Effect: "readonly",
ChangeEvent: "readonly",
WheelEvent: "readonly",
ImportMetaEnv: "readonly",
Fn: "readonly",
PromiseFn: "readonly",
ComponentElRef: "readonly",
parseInt: "readonly",
parseFloat: "readonly"
}
},
plugins: {
prettier: pluginPrettier
},
rules: {
...configPrettier.rules,
...pluginPrettier.configs.recommended.rules,
"no-debugger": "off",
"no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
],
"prettier/prettier": [
"error",
{
endOfLine: "auto"
}
]
}
},
{
files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"],
languageOptions: {
parser: parserTypeScript,
parserOptions: {
sourceType: "module"
}
},
plugins: {
"@typescript-eslint": pluginTypeScript
},
rules: {
...pluginTypeScript.configs.strict.rules,
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-redeclare": "error",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/prefer-as-const": "warn",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-import-type-side-effects": "error",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/consistent-type-imports": [
"error",
{ disallowTypeAnnotations: false, fixStyle: "inline-type-imports" }
],
"@typescript-eslint/prefer-literal-enum-member": [
"error",
{ allowBitwiseExpressions: true }
],
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_"
}
]
}
},
{
files: ["**/*.d.ts"],
rules: {
"eslint-comments/no-unlimited-disable": "off",
"import/no-duplicates": "off",
"unused-imports/no-unused-vars": "off"
}
},
{
files: ["**/*.?([cm])js"],
rules: {
"@typescript-eslint/no-require-imports": "off",
"@typescript-eslint/no-var-requires": "off"
}
},
{
files: ["**/*.vue"],
languageOptions: {
globals: {
$: "readonly",
$$: "readonly",
$computed: "readonly",
$customRef: "readonly",
$ref: "readonly",
$shallowRef: "readonly",
$toRef: "readonly"
},
parser: parserVue,
parserOptions: {
ecmaFeatures: {
jsx: true
},
extraFileExtensions: [".vue"],
parser: "@typescript-eslint/parser",
sourceType: "module"
}
},
plugins: {
vue: pluginVue
},
processor: pluginVue.processors[".vue"],
rules: {
...pluginVue.configs.base.rules,
...pluginVue.configs["vue3-essential"].rules,
...pluginVue.configs["vue3-recommended"].rules,
"no-undef": "off",
"no-unused-vars": "off",
"vue/no-v-html": "off",
"vue/require-default-prop": "off",
"vue/require-explicit-emits": "off",
"vue/multi-word-component-names": "off",
"vue/no-setup-props-reactivity-loss": "off",
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "always",
component: "always"
},
svg: "always",
math: "always"
}
]
}
}
]);

View File

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>

View File

@ -1,5 +1,5 @@
// 模拟后端动态生成路由
import { MockMethod } from "vite-plugin-mock";
import { defineFakeRoute } from "vite-plugin-fake-server/client";
import { system, permission, frame, tabs } from "@/router/enums";
/**
@ -198,7 +198,7 @@ const tabsRouter = {
]
};
export default [
export default defineFakeRoute([
{
url: "/get-async-routes",
method: "get",
@ -209,4 +209,4 @@ export default [
};
}
}
] as MockMethod[];
]);

View File

@ -1,6 +1,6 @@
import { MockMethod } from "vite-plugin-mock";
import { defineFakeRoute } from "vite-plugin-fake-server/client";
export default [
export default defineFakeRoute([
{
url: "/get-card-list",
method: "post",
@ -676,4 +676,4 @@ export default [
};
}
}
] as MockMethod[];
]);

View File

@ -1,7 +1,7 @@
// 根据角色动态生成路由
import { MockMethod } from "vite-plugin-mock";
import { defineFakeRoute } from "vite-plugin-fake-server/client";
export default [
export default defineFakeRoute([
{
url: "/login",
method: "post",
@ -33,4 +33,4 @@ export default [
}
}
}
] as MockMethod[];
]);

View File

@ -1,29 +1,33 @@
import { MockMethod } from "vite-plugin-mock";
import { defineFakeRoute } from "vite-plugin-fake-server/client";
import { faker } from "@faker-js/faker/locale/zh_CN";
type mapType = {
plateNumber: string;
driver: string;
"orientation|1-360": number;
"lng|113-114.1-10": number;
"lat|34-35.1-10": number;
orientation: number;
lng: number;
lat: number;
};
// http://mockjs.com/examples.html#Object
const mapList = (): Array<mapType> => {
const result: Array<mapType> = [];
for (let index = 0; index < 200; index++) {
result.push({
plateNumber: "豫A@natural(11111, 99999)@character('upper')",
driver: "@cname()",
"orientation|1-360": 100,
"lng|113-114.1-10": 1,
"lat|34-35.1-10": 1
plateNumber: `豫A${faker.string.numeric({
length: 5
})}${faker.string.alphanumeric({
casing: "upper"
})}`,
driver: faker.person.firstName(),
orientation: faker.number.int({ min: 1, max: 360 }),
lng: faker.location.latitude({ max: 114.1, min: 113 }),
lat: faker.location.latitude({ max: 35.1, min: 34 })
});
}
return result;
};
export default [
export default defineFakeRoute([
{
url: "/get-map-info",
method: "get",
@ -34,4 +38,4 @@ export default [
};
}
}
] as MockMethod[];
]);

View File

@ -1,7 +1,7 @@
import { MockMethod } from "vite-plugin-mock";
import { defineFakeRoute } from "vite-plugin-fake-server/client";
// 模拟刷新token接口
export default [
export default defineFakeRoute([
{
url: "/refresh-token",
method: "post",
@ -24,4 +24,4 @@ export default [
}
}
}
] as MockMethod[];
]);

View File

@ -1,6 +1,7 @@
import { MockMethod } from "vite-plugin-mock";
import { defineFakeRoute } from "vite-plugin-fake-server/client";
import { faker } from "@faker-js/faker/locale/zh_CN";
export default [
export default defineFakeRoute([
// 用户管理
{
url: "/user",
@ -12,7 +13,7 @@ export default [
nickname: "admin",
avatar: "https://avatars.githubusercontent.com/u/44761321",
phone: "15888886789",
email: "@email",
email: faker.internet.email(),
sex: 0,
id: 1,
status: 1,
@ -30,7 +31,7 @@ export default [
nickname: "common",
avatar: "https://avatars.githubusercontent.com/u/52823142",
phone: "18288882345",
email: "@email",
email: faker.internet.email(),
sex: 1,
id: 2,
status: 1,
@ -153,12 +154,12 @@ export default [
id: 100,
sort: 0,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 1, // 状态 1 启用 0 停用
type: 1, // 1 公司 2 分公司 3 部门
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "郑州分公司",
@ -166,12 +167,12 @@ export default [
id: 101,
sort: 1,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 1,
type: 2,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "研发部门",
@ -179,12 +180,12 @@ export default [
id: 103,
sort: 1,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 1,
type: 3,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "市场部门",
@ -192,12 +193,12 @@ export default [
id: 108,
sort: 1,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 1,
type: 3,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "深圳分公司",
@ -205,12 +206,12 @@ export default [
id: 102,
sort: 2,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 1,
type: 2,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "市场部门",
@ -218,12 +219,12 @@ export default [
id: 104,
sort: 2,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 1,
type: 3,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "财务部门",
@ -231,12 +232,12 @@ export default [
id: 109,
sort: 2,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 1,
type: 3,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "测试部门",
@ -244,12 +245,12 @@ export default [
id: 105,
sort: 3,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 0,
type: 3,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "财务部门",
@ -257,12 +258,12 @@ export default [
id: 106,
sort: 4,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 1,
type: 3,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
},
{
name: "运维部门",
@ -270,15 +271,15 @@ export default [
id: 107,
sort: 5,
phone: "15888888888",
principal: "@cname()",
email: "@email",
principal: faker.person.firstName(),
email: faker.internet.email(),
status: 0,
type: 3,
createTime: 1605456000000,
remark: "@cparagraph(1, 3)"
remark: "这里是备注信息这里是备注信息这里是备注信息这里是备注信息"
}
]
};
}
}
] as MockMethod[];
]);

View File

@ -2,30 +2,7 @@
"name": "vue-pure-admin",
"version": "4.5.0",
"private": true,
"keywords": [
"vue-pure-admin",
"element-plus",
"tailwindcss",
"pure-admin",
"typescript",
"pinia",
"vue3",
"vite"
],
"homepage": "https://github.com/pure-admin/vue-pure-admin",
"bugs": {
"url": "https://github.com/pure-admin/vue-pure-admin/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/pure-admin/vue-pure-admin.git"
},
"license": "MIT",
"author": {
"name": "xiaoxian521",
"email": "pureadmin@163.com",
"url": "https://github.com/xiaoxian521"
},
"type": "module",
"scripts": {
"dev": "NODE_OPTIONS=--max-old-space-size=4096 vite",
"serve": "pnpm dev",
@ -37,146 +14,166 @@
"typecheck": "tsc --noEmit && vue-tsc --noEmit --skipLibCheck",
"svgo": "svgo -f src/assets/svg -o src/assets/svg",
"cloc": "NODE_OPTIONS=--max-old-space-size=4096 cloc . --exclude-dir=node_modules --exclude-lang=YAML",
"clean:cache": "rimraf .eslintcache && rimraf node_modules && pnpm install",
"clean:cache": "rimraf .eslintcache && rimraf pnpm-lock.yaml && rimraf node_modules && pnpm store prune && pnpm install",
"lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock,build}/**/*.{vue,js,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,ts,json,tsx,css,scss,vue,html,md}\"",
"lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache --cache-location node_modules/.cache/stylelint/",
"lint:stylelint": "stylelint --cache --fix \"**/*.{html,vue,css,scss}\" --cache-location node_modules/.cache/stylelint/",
"lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint",
"prepare": "husky install",
"preinstall": "npx only-allow pnpm"
},
"keywords": [
"vue-pure-admin",
"element-plus",
"tailwindcss",
"pure-admin",
"typescript",
"pinia",
"vue3",
"vite",
"esm"
],
"homepage": "https://github.com/pure-admin/vue-pure-admin",
"repository": {
"type": "git",
"url": "git+https://github.com/pure-admin/vue-pure-admin.git"
},
"bugs": {
"url": "https://github.com/pure-admin/vue-pure-admin/issues"
},
"license": "MIT",
"author": {
"name": "xiaoxian521",
"email": "pureadmin@163.com",
"url": "https://github.com/xiaoxian521"
},
"dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1",
"@howdyjs/mouse-menu": "2.0.9",
"@logicflow/core": "^1.2.17",
"@logicflow/extension": "^1.2.18",
"@logicflow/core": "^1.2.18",
"@logicflow/extension": "^1.2.19",
"@pureadmin/descriptions": "^1.2.0",
"@pureadmin/table": "^2.3.4",
"@pureadmin/utils": "^1.9.10",
"@vueuse/core": "^10.5.0",
"@pureadmin/table": "^3.0.0",
"@pureadmin/utils": "^2.1.1",
"@vueuse/core": "^10.7.1",
"@vueuse/motion": "^2.0.0",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"@zxcvbn-ts/core": "^3.0.4",
"animate.css": "^4.1.1",
"axios": "^1.6.0",
"axios": "^1.6.3",
"china-area-data": "^5.0.1",
"cropperjs": "^1.6.1",
"dayjs": "^1.11.10",
"echarts": "^5.4.3",
"el-table-infinite-scroll": "^3.0.3",
"element-plus": "^2.4.1",
"element-plus": "^2.4.4",
"intro.js": "^7.2.0",
"js-cookie": "^3.0.5",
"jsbarcode": "^3.11.5",
"jsbarcode": "^3.11.6",
"localforage": "^1.10.0",
"md-editor-v3": "2.7.2",
"mint-filter": "^4.0.3",
"mitt": "^3.0.1",
"mockjs": "^1.1.0",
"nprogress": "^0.2.0",
"path": "^0.12.7",
"pinia": "^2.1.7",
"pinyin-pro": "^3.17.0",
"pinyin-pro": "^3.18.6",
"qrcode": "^1.5.3",
"qs": "^6.11.2",
"responsive-storage": "^2.2.0",
"sortablejs": "^1.15.0",
"swiper": "^11.0.3",
"typeit": "^8.7.1",
"sortablejs": "^1.15.1",
"swiper": "^11.0.5",
"typeit": "^8.8.0",
"v-contextmenu": "3.0.0",
"v3-infinite-loading": "^1.3.1",
"version-rocket": "^1.7.0",
"vue": "^3.3.7",
"vue-i18n": "^9.6.2",
"vue-json-pretty": "^2.2.4",
"version-rocket": "^1.7.1",
"vue": "^3.4.3",
"vue-i18n": "^9.8.0",
"vue-json-pretty": "^2.3.0",
"vue-pdf-embed": "^1.2.1",
"vue-router": "^4.2.5",
"vue-tippy": "^6.3.1",
"vue-tippy": "^6.4.1",
"vue-types": "^5.1.1",
"vue-virtual-scroller": "2.0.0-beta.8",
"vue-waterfall-plugin-next": "^2.2.4",
"vue-waterfall-plugin-next": "^2.3.1",
"vue3-danmaku": "^1.6.0",
"vuedraggable": "^4.1.0",
"wavesurfer.js": "^7.4.2",
"xgplayer": "^3.0.9",
"wavesurfer.js": "^7.6.0",
"xgplayer": "^3.0.11",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@commitlint/cli": "^17.7.2",
"@commitlint/config-conventional": "^17.7.0",
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"@commitlint/types": "^18.4.3",
"@eslint/js": "^8.56.0",
"@faker-js/faker": "^8.3.1",
"@iconify-icons/ep": "^1.2.12",
"@iconify-icons/ri": "^1.2.10",
"@iconify/vue": "^4.1.1",
"@intlify/unplugin-vue-i18n": "^1.4.0",
"@intlify/unplugin-vue-i18n": "^2.0.0",
"@pureadmin/theme": "^3.2.0",
"@types/intro.js": "^5.1.2",
"@types/js-cookie": "^3.0.4",
"@types/mockjs": "^1.0.8",
"@types/node": "^20.8.2",
"@types/nprogress": "0.2.0",
"@types/qrcode": "^1.5.2",
"@types/qs": "^6.9.8",
"@types/sortablejs": "^1.15.3",
"@typescript-eslint/eslint-plugin": "^6.7.4",
"@typescript-eslint/parser": "^6.7.4",
"@vitejs/plugin-vue": "^4.4.0",
"@vitejs/plugin-vue-jsx": "^3.0.2",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^12.0.0",
"@types/intro.js": "^5.1.5",
"@types/js-cookie": "^3.0.6",
"@types/node": "^20.10.6",
"@types/nprogress": "^0.2.3",
"@types/qrcode": "^1.5.5",
"@types/qs": "^6.9.11",
"@types/sortablejs": "^1.15.7",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"@typescript-eslint/parser": "^6.17.0",
"@vitejs/plugin-vue": "^5.0.2",
"@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.16",
"cloc": "^2.11.0",
"cssnano": "^6.0.1",
"eslint": "^8.50.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.18.1",
"cssnano": "^6.0.2",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-define-config": "^2.1.0",
"eslint-plugin-prettier": "^5.1.2",
"eslint-plugin-vue": "^9.19.2",
"husky": "^8.0.3",
"lint-staged": "^14.0.1",
"lint-staged": "^15.2.0",
"picocolors": "^1.0.0",
"postcss": "^8.4.31",
"postcss": "^8.4.32",
"postcss-html": "^1.5.0",
"postcss-import": "^15.1.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.0.3",
"prettier": "^3.1.1",
"rimraf": "^5.0.5",
"rollup-plugin-visualizer": "^5.9.2",
"sass": "^1.69.0",
"sass-loader": "^13.3.2",
"stylelint": "^15.10.3",
"stylelint-config-html": "^1.1.0",
"stylelint-config-recess-order": "^4.3.0",
"stylelint-config-recommended": "^13.0.0",
"stylelint-config-recommended-scss": "^13.0.0",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.69.6",
"stylelint": "^16.1.0",
"stylelint-config-recess-order": "^4.4.0",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^34.0.0",
"stylelint-config-standard-scss": "^11.0.0",
"stylelint-order": "^6.0.3",
"stylelint-prettier": "^4.0.2",
"stylelint-scss": "^5.2.1",
"svgo": "^3.0.2",
"tailwindcss": "^3.3.5",
"terser": "^5.24.0",
"typescript": "^5.2.2",
"vite": "^4.5.0",
"stylelint-config-standard-scss": "^12.0.0",
"stylelint-prettier": "^5.0.0",
"svgo": "^3.2.0",
"tailwindcss": "^3.4.0",
"typescript": "^5.3.3",
"vite": "^5.0.10",
"vite-plugin-cdn-import": "^0.3.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-mock": "2.9.6",
"vite-plugin-remove-console": "^2.1.1",
"vite-svg-loader": "^4.0.0",
"vite-plugin-fake-server": "^2.0.1",
"vite-plugin-remove-console": "^2.2.0",
"vite-plugin-router-warn": "^1.0.0",
"vite-svg-loader": "^5.1.0",
"vue-eslint-parser": "^9.3.2",
"vue-tsc": "^1.8.22"
"vue-tsc": "^1.8.27"
},
"engines": {
"node": "^18.12.0 || >=20.0.0",
"pnpm": ">=8.6.10"
},
"packageManager": "pnpm@8.6.10",
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"rollup",
"webpack",
"core-js"
]
},
"allowedDeprecatedVersions": {
"sourcemap-codec": "*",
"domexception": "*",
"w3c-hr-time": "*",
"stable": "*"
"stable": "*",
"abab": "*"
}
}
}
}

6402
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,7 @@
module.exports = {
// @ts-check
/** @type {import('postcss-load-config').Config} */
export default {
plugins: {
"postcss-import": {},
"tailwindcss/nesting": {},

View File

@ -7,7 +7,7 @@
"KeepAlive": true,
"Locale": "zh",
"Layout": "vertical",
"Theme": "default",
"Theme": "light",
"DarkMode": false,
"Grey": false,
"Weak": false,

View File

@ -7,15 +7,8 @@ defineOptions({
name: "ReAnimateSelector"
});
const props = defineProps({
modelValue: {
require: false,
type: String
}
});
const emit = defineEmits<{ (e: "update:modelValue", v: string) }>();
const inputValue = defineModel({ type: String });
const inputValue = toRef(props, "modelValue");
const animatesList = ref(animates);
const copyAnimatesList = cloneDeep(animatesList);
@ -47,11 +40,11 @@ const animateStyle = computed(
);
function onChangeIcon(animate: string) {
emit("update:modelValue", animate);
inputValue.value = animate;
}
function onClear() {
emit("update:modelValue", "");
inputValue.value = "";
}
function filterMethod(value: any) {
@ -81,8 +74,8 @@ function onMouseleave() {
placeholder="请选择动画"
clearable
filterable
@clear="onClear"
:filter-method="filterMethod"
@clear="onClear"
>
<template #empty>
<div class="w-[280px]">

View File

@ -1,4 +1,4 @@
import { PropType } from "vue";
import type { PropType } from "vue";
import propTypes from "@/utils/propTypes";
export const countToProps = {
startVal: propTypes.number.def(0),

View File

@ -1,4 +1,4 @@
import { PropType } from "vue";
import type { PropType } from "vue";
import propTypes from "@/utils/propTypes";
export const reboundProps = {
delay: propTypes.number.def(1),

View File

@ -1,7 +1,5 @@
@import "cropperjs/dist/cropper.css";
@import "tippy.js/dist/tippy.css";
@import "tippy.js/themes/light.css";
@import "tippy.js/animations/perspective.css";
@import "cropperjs/dist/cropper.css";
.re-circled {
.cropper-view-box,

View File

@ -1,16 +1,16 @@
import "./circled.css";
import Cropper from "cropperjs";
import { useTippy } from "vue-tippy";
import { ElUpload } from "element-plus";
import type { CSSProperties } from "vue";
import { useResizeObserver } from "@vueuse/core";
import { longpress } from "@/directives/longpress";
import { useTippy, directive as tippy } from "vue-tippy";
import { delay, debounce, isArray, downloadByBase64 } from "@pureadmin/utils";
import {
ref,
unref,
computed,
PropType,
type PropType,
onMounted,
onUnmounted,
defineComponent
@ -233,7 +233,6 @@ export default defineComponent({
const menuContent = defineComponent({
directives: {
tippy,
longpress
},
setup() {

View File

@ -27,8 +27,11 @@ const addDialog = (options: DialogOptions) => {
/** 关闭弹框 */
const closeDialog = (options: DialogOptions, index: number, args?: any) => {
dialogStore.value.splice(index, 1);
dialogStore.value[index].visible = false;
options.closeCallBack && options.closeCallBack({ options, index, args });
useTimeoutFn(() => {
dialogStore.value.splice(index, 1);
}, 200);
};
/**

View File

@ -84,11 +84,11 @@ function handleClose(
<template>
<el-dialog
class="pure-dialog"
v-for="(options, index) in dialogStore"
:key="index"
v-bind="options"
v-model="options.visible"
class="pure-dialog"
:fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
@close="handleClose(options, index)"
@opened="eventsCallBack('open', options, index)"
@ -116,15 +116,15 @@ function handleClose(
options?.fullscreen
? ExitFullscreen
: fullscreen
? ExitFullscreen
: Fullscreen
? ExitFullscreen
: Fullscreen
"
/>
</i>
</div>
<component
v-else
:is="options?.headerRenderer({ close, titleId, titleClass })"
v-else
/>
</template>
<component

View File

@ -1,5 +1,5 @@
import "./index.css";
import { h, defineComponent, Component } from "vue";
import { h, defineComponent, type Component } from "vue";
export interface attrsType {
width?: string;

View File

@ -18,7 +18,6 @@ export default defineComponent({
name: "ReFlop",
props,
setup(props) {
// eslint-disable-next-line vue/no-setup-props-destructure
const { frontText, backText, duration } = props;
const isFlipping = ref(false);
const flipType = ref("down");

View File

@ -35,9 +35,9 @@ const nodeDragNode = item => {
<!-- 左侧bpmn元素选择器 -->
<div class="node-panel">
<div
class="node-item dark:text-bg_color"
v-for="item in props.nodeList"
:key="item.text"
class="node-item dark:text-bg_color"
@mousedown="nodeDragNode(item)"
>
<div class="node-item-icon" :class="item.class">

View File

@ -10,15 +10,8 @@ defineOptions({
name: "IconSelect"
});
const props = defineProps({
modelValue: {
require: false,
type: String
}
});
const emit = defineEmits<{ (e: "update:modelValue", v: string) }>();
const inputValue = defineModel({ type: String });
const inputValue = toRef(props, "modelValue");
const iconList = ref(IconJson);
const icon = ref();
const currentActiveType = ref("ep:");
@ -68,11 +61,11 @@ const iconItemStyle = computed((): ParameterCSSProperties => {
});
function setVal() {
currentActiveType.value = props.modelValue.substring(
currentActiveType.value = inputValue.value.substring(
0,
props.modelValue.indexOf(":") + 1
inputValue.value.indexOf(":") + 1
);
icon.value = props.modelValue.substring(props.modelValue.indexOf(":") + 1);
icon.value = inputValue.value.substring(inputValue.value.indexOf(":") + 1);
}
function onBeforeEnter() {
@ -96,7 +89,7 @@ function handleClick({ props }) {
function onChangeIcon(item) {
icon.value = item;
emit("update:modelValue", currentActiveType.value + item);
inputValue.value = currentActiveType.value + item;
}
function onCurrentChange(page) {
@ -105,7 +98,7 @@ function onCurrentChange(page) {
function onClear() {
icon.value = "";
emit("update:modelValue", "");
inputValue.value = "";
}
watch(
@ -117,7 +110,7 @@ watch(
{ immediate: true }
);
watch(
() => props.modelValue,
() => inputValue.value,
val => val && setVal(),
{ immediate: true }
);
@ -151,8 +144,8 @@ watch(
</template>
<el-input
class="px-2 pt-2"
v-model="filterValue"
class="px-2 pt-2"
placeholder="搜索图标"
clearable
/>

View File

@ -1,5 +1,5 @@
import { iconType } from "./types";
import { h, defineComponent, Component } from "vue";
import type { iconType } from "./types";
import { h, defineComponent, type Component } from "vue";
import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index";
/**
@ -33,7 +33,7 @@ export function useRenderIcon(icon: any, attrs?: iconType): Component {
});
} else if (typeof icon === "function" || typeof icon?.render === "function") {
// svg
return icon;
return attrs ? h(icon, { ...attrs }) : icon;
} else if (typeof icon === "object") {
return defineComponent({
name: "OfflineIcon",

View File

@ -13,7 +13,8 @@ export interface iconType {
align?: string;
onLoad?: Function;
includes?: Function;
// all icon
// svg 需要什么SVG属性自行添加
fill?: string;
// all icon
style?: object;
}

View File

@ -4,13 +4,13 @@ import {
watch,
nextTick,
computed,
PropType,
type PropType,
defineComponent
} from "vue";
import "./index.scss";
import propTypes from "@/utils/propTypes";
import { isString, cloneDeep } from "@pureadmin/utils";
import QRCode, { QRCodeRenderersOptions } from "qrcode";
import QRCode, { type QRCodeRenderersOptions } from "qrcode";
import RefreshRight from "@iconify-icons/ep/refresh-right";
interface QrcodeLogo {

View File

@ -498,16 +498,16 @@ defineExpose({
<template>
<div :ref="'wrap' + props.classOption['key']">
<div
:style="leftSwitch"
v-if="navigation"
:style="leftSwitch"
:class="leftSwitchClass"
@click="leftSwitchClick"
>
<slot name="left-switch" />
</div>
<div
:style="rightSwitch"
v-if="navigation"
:style="rightSwitch"
:class="rightSwitchClass"
@click="rightSwitchClick"
>
@ -526,7 +526,7 @@ defineExpose({
<div :ref="'slotList' + props.classOption['key']" :style="float">
<slot />
</div>
<div v-html="copyHtml" :style="float" />
<div :style="float" v-html="copyHtml" />
</div>
</div>
</template>

View File

@ -6,7 +6,6 @@
color: rgba(0, 0, 0, 0.65);
background-color: rgb(0 0 0 / 4%);
border-radius: 2px;
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.pure-segmented-group {
@ -43,7 +42,7 @@
text-align: center;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: all 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.pure-segmented-item > div {

View File

@ -121,7 +121,11 @@ export default defineComponent({
class="pure-segmented-item-icon"
style={{ marginRight: option.label ? "6px" : 0 }}
>
{h(useRenderIcon(option.icon))}
{h(
useRenderIcon(option.icon, {
...option?.iconAttrs
})
)}
</span>
) : null}
{option.label ? (

View File

@ -1,4 +1,5 @@
import type { VNode, Component } from "vue";
import type { iconType } from "@/components/ReIcon/src/types.ts";
export interface OptionsType {
/** 文字 */
@ -8,6 +9,8 @@ export interface OptionsType {
* @see {@link https://yiming_chang.gitee.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks }
*/
icon?: string | Component;
/** 图标属性、样式配置 */
iconAttrs?: iconType;
/** 值 */
value?: string | number;
/** 是否禁用 */

View File

@ -55,7 +55,6 @@ export default defineComponent({
emits: ["selectedVal"],
setup(props, { emit }) {
const instance = getCurrentInstance();
// eslint-disable-next-line vue/no-setup-props-destructure
const currentValue = props.value;
const rateDisabled = computed(() => {

View File

@ -1,4 +1,4 @@
import { defineComponent, ref, unref, computed, PropType } from "vue";
import { defineComponent, ref, unref, computed, type PropType } from "vue";
import resizer from "./resizer";
import "./index.css";

View File

@ -1,4 +1,4 @@
import { App } from "vue";
import type { App } from "vue";
import axios from "axios";
let config: object = {};

View File

@ -50,6 +50,12 @@ const getSectionStyle = computed(() => {
});
const transitionMain = defineComponent({
props: {
route: {
type: undefined,
required: true
}
},
render() {
const transitionName =
transitions.value(this.route)?.name || "fade-transform";
@ -72,12 +78,6 @@ const transitionMain = defineComponent({
default: () => [this.$slots.default()]
}
);
},
props: {
route: {
type: undefined,
required: true
}
}
});
</script>
@ -92,7 +92,8 @@ const transitionMain = defineComponent({
<el-scrollbar
v-if="props.fixedHeader"
:wrap-style="{
display: 'flex'
display: 'flex',
'flex-wrap': 'wrap'
}"
:view-style="{
display: 'flex',
@ -117,8 +118,8 @@ const transitionMain = defineComponent({
/>
</keep-alive>
<component
v-else
:is="Component"
v-else
:key="route.fullPath"
class="main-content"
/>
@ -139,8 +140,8 @@ const transitionMain = defineComponent({
/>
</keep-alive>
<component
v-else
:is="Component"
v-else
:key="route.fullPath"
class="main-content"
/>

View File

@ -62,8 +62,8 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
@click="translationCh"
>
<IconifyIconOffline
class="check-zh"
v-show="locale === 'zh'"
class="check-zh"
:icon="Check"
/>
简体中文
@ -73,7 +73,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn"
>
<span class="check-en" v-show="locale === 'en'">
<span v-show="locale === 'en'" class="check-en">
<IconifyIconOffline :icon="Check" />
</span>
English

View File

@ -23,8 +23,8 @@ notices.value.map(v => (noticesNum.value += v.list.length));
<template #dropdown>
<el-dropdown-menu>
<el-tabs
:stretch="true"
v-model="activeKey"
:stretch="true"
class="dropdown-tabs"
:style="{ width: notices.length === 0 ? '200px' : '330px' }"
>

View File

@ -15,8 +15,8 @@ const props = defineProps({
<div v-if="props.list.length">
<NoticeItem
v-for="(item, index) in props.list"
:noticeItem="item"
:key="index"
:noticeItem="item"
/>
</div>
<el-empty v-else description="暂无数据" />

View File

@ -2,6 +2,7 @@
import { emitter } from "@/utils/mitt";
import { onClickOutside } from "@vueuse/core";
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import Close from "@iconify-icons/ep/close";
const target = ref(null);
@ -9,7 +10,6 @@ const show = ref<Boolean>(false);
const iconClass = computed(() => {
return [
"mr-[20px]",
"outline-none",
"width-[20px]",
"height-[20px]",
@ -22,6 +22,8 @@ const iconClass = computed(() => {
];
});
const { onReset } = useDataThemeChange();
onClickOutside(target, (event: any) => {
if (event.clientX > target.value.offsetLeft) return;
show.value = false;
@ -43,23 +45,47 @@ onBeforeUnmount(() => {
<div :class="{ show: show }" class="right-panel-container">
<div class="right-panel-background" />
<div ref="target" class="right-panel bg-bg_color">
<div class="right-panel-items">
<div class="project-configuration">
<h4 class="dark:text-white">项目配置</h4>
<span title="关闭配置" :class="iconClass">
<IconifyIconOffline
class="dark:text-white"
width="20px"
height="20px"
:icon="Close"
@click="show = !show"
/>
</span>
</div>
<div
class="border-b-[1px] border-solid border-[#dcdfe6] dark:border-[#303030]"
/>
<div
class="project-configuration border-b-[1px] border-solid border-[var(--pure-border-color)]"
>
<h4 class="dark:text-white">项目配置</h4>
<span
v-tippy="{
content: '关闭配置',
placement: 'bottom-start',
zIndex: 41000
}"
:class="iconClass"
>
<IconifyIconOffline
class="dark:text-white"
width="20px"
height="20px"
:icon="Close"
@click="show = !show"
/>
</span>
</div>
<el-scrollbar>
<slot />
</el-scrollbar>
<div
class="flex justify-end p-3 border-t-[1px] border-solid border-[var(--pure-border-color)]"
>
<el-button
v-tippy="{
content: '清空缓存并返回登录页',
placement: 'left-start',
zIndex: 41000
}"
type="danger"
text
bg
@click="onReset"
>
清空缓存
</el-button>
</div>
</div>
</div>
@ -74,6 +100,10 @@ onBeforeUnmount(() => {
</style>
<style lang="scss" scoped>
:deep(.el-scrollbar) {
height: calc(100vh - 110px);
}
.right-panel-background {
position: fixed;
top: 0;
@ -90,7 +120,7 @@ onBeforeUnmount(() => {
right: 0;
z-index: 40000;
width: 100%;
max-width: 315px;
max-width: 280px;
height: 100vh;
box-shadow: 0 0 15px 0 rgb(0 0 0 / 5%);
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
@ -112,47 +142,10 @@ onBeforeUnmount(() => {
}
}
.handle-button {
position: absolute;
top: 45%;
left: -48px;
z-index: 0;
width: 48px;
height: 48px;
font-size: 24px;
line-height: 48px;
color: #fff;
text-align: center;
pointer-events: auto;
cursor: pointer;
background: rgb(24 144 255);
border-radius: 6px 0 0 6px !important;
i {
font-size: 24px;
line-height: 48px;
}
}
.right-panel-items {
height: calc(100vh - 60px);
margin-top: 60px;
overflow-y: auto;
}
.project-configuration {
position: fixed;
top: 15px;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 30px;
margin-left: 10px;
}
:deep(.el-divider--horizontal) {
width: 90%;
margin: 20px auto 0;
padding: 14px 20px;
}
</style>

View File

@ -146,9 +146,9 @@ onKeyStroke("ArrowDown", handleDown);
<template>
<el-dialog
v-model="show"
top="5vh"
class="pure-search-dialog"
v-model="show"
:show-close="false"
:width="device === 'mobile' ? '80vw' : '40vw'"
:before-close="handleClose"
@ -161,8 +161,8 @@ onKeyStroke("ArrowDown", handleDown);
>
<el-input
ref="inputRef"
size="large"
v-model="keyword"
size="large"
clearable
placeholder="搜索菜单(中文模式下支持拼音搜索)"
@input="handleSearch"

View File

@ -8,28 +8,22 @@ import {
nextTick,
onBeforeMount
} from "vue";
import { getConfig } from "@/config";
import { useRouter } from "vue-router";
import panel from "../panel/index.vue";
import { emitter } from "@/utils/mitt";
import { resetRouter } from "@/router";
import { removeToken } from "@/utils/auth";
import { routerArrays } from "@/layout/types";
import { useNav } from "@/layout/hooks/useNav";
import { useAppStoreHook } from "@/store/modules/app";
import { useDark, debounce, useGlobal } from "@pureadmin/utils";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import Segmented, { type OptionsType } from "@/components/ReSegmented";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import { useDark, debounce, useGlobal, storageLocal } from "@pureadmin/utils";
import Check from "@iconify-icons/ep/check";
import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
import Check from "@iconify-icons/ep/check";
import Logout from "@iconify-icons/ri/logout-circle-r-line";
const router = useRouter();
const { device } = useNav();
const { isDark } = useDark();
const { device, tooltipEffect } = useNav();
const { $storage } = useGlobal<GlobalPropertiesApi>();
const mixRef = ref();
@ -40,8 +34,8 @@ const {
dataTheme,
layoutTheme,
themeColors,
toggleClass,
dataThemeChange,
setEpThemeColor,
setLayoutThemeColor
} = useDataThemeChange();
@ -89,13 +83,6 @@ function storageConfigureChange<T>(key: string, val: T): void {
$storage.configure = storageConfigure;
}
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
const targetEl = target || document.body;
let { className } = targetEl;
className = className.replace(clsName, "").trim();
targetEl.className = flag ? `${className} ${clsName} ` : className;
}
/** 灰色模式设置 */
const greyChange = (value): void => {
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
@ -132,24 +119,11 @@ const multiTagsCacheChange = () => {
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
};
/** 清空缓存并返回登录页 */
function onReset() {
removeToken();
storageLocal().clear();
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout);
setEpThemeColor(EpThemeColor);
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
toggleClass(Grey, "html-grey", document.querySelector("html"));
toggleClass(Weak, "html-weakness", document.querySelector("html"));
router.push("/login");
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
resetRouter();
}
function onChange(label) {
storageConfigureChange("showModel", label);
emitter.emit("tagViewsShowModel", label);
function onChange({ option }) {
const { value } = option;
markValue.value = value;
storageConfigureChange("showModel", value);
emitter.emit("tagViewsShowModel", value);
}
/** 侧边栏Logo */
@ -185,6 +159,32 @@ const getThemeColor = computed(() => {
};
});
const themeOptions = computed<Array<OptionsType>>(() => {
return [
{
label: "亮色",
icon: dayIcon,
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
},
{
label: "暗色",
icon: darkIcon,
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
}
];
});
const markOptions: Array<OptionsType> = [
{
label: "灵动",
value: "smart"
},
{
label: "卡片",
value: "card"
}
];
/** 设置导航模式 */
function setLayoutModel(layout: string) {
layoutTheme.value.layout = layout;
@ -194,7 +194,8 @@ function setLayoutModel(layout: string) {
theme: layoutTheme.value.theme,
darkMode: $storage.layout?.darkMode,
sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: $storage.layout?.epThemeColor
epThemeColor: $storage.layout?.epThemeColor,
themeColor: layoutTheme.value.theme
};
useAppStoreHook().setLayout(layout);
}
@ -234,185 +235,153 @@ onBeforeMount(() => {
<template>
<panel>
<el-divider>主题</el-divider>
<el-switch
v-model="dataTheme"
inline-prompt
class="pure-datatheme"
:active-icon="dayIcon"
:inactive-icon="darkIcon"
@change="dataThemeChange"
/>
<div class="p-6">
<p class="mb-3 font-medium text-sm dark:text-white">整体风格</p>
<Segmented
:modelValue="dataTheme ? 1 : 0"
:options="themeOptions"
@change="
{
dataTheme = !dataTheme;
dataThemeChange();
}
"
/>
<el-divider>导航栏模式</el-divider>
<ul class="pure-theme">
<el-tooltip
:effect="tooltipEffect"
class="item"
content="左侧模式"
placement="bottom"
popper-class="pure-tooltip"
>
<p class="mt-5 mb-3 font-medium text-sm dark:text-white">主题色</p>
<ul class="theme-color">
<li
v-for="(item, index) in themeColors"
v-show="showThemeColors(item.themeColor)"
:key="index"
:style="getThemeColorStyle(item.color)"
@click="setLayoutThemeColor(item.themeColor)"
>
<el-icon
style="margin: 0.1em 0.1em 0 0"
:size="17"
:color="getThemeColor(item.themeColor)"
>
<IconifyIconOffline :icon="Check" />
</el-icon>
</li>
</ul>
<p class="mt-5 mb-3 font-medium text-sm dark:text-white">导航模式</p>
<ul class="pure-theme">
<li
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
ref="verticalRef"
v-tippy="{
content: '左侧菜单',
zIndex: 41000
}"
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
@click="setLayoutModel('vertical')"
>
<div />
<div />
</li>
</el-tooltip>
<el-tooltip
v-if="device !== 'mobile'"
:effect="tooltipEffect"
class="item"
content="顶部模式"
placement="bottom"
popper-class="pure-tooltip"
>
<li
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
v-if="device !== 'mobile'"
ref="horizontalRef"
v-tippy="{
content: '顶部菜单',
zIndex: 41000
}"
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
@click="setLayoutModel('horizontal')"
>
<div />
<div />
</li>
</el-tooltip>
<el-tooltip
v-if="device !== 'mobile'"
:effect="tooltipEffect"
class="item"
content="混合模式"
placement="bottom"
popper-class="pure-tooltip"
>
<li
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
v-if="device !== 'mobile'"
ref="mixRef"
v-tippy="{
content: '混合菜单',
zIndex: 41000
}"
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
@click="setLayoutModel('mix')"
>
<div />
<div />
</li>
</el-tooltip>
</ul>
</ul>
<el-divider>主题色</el-divider>
<ul class="theme-color">
<li
v-for="(item, index) in themeColors"
:key="index"
v-show="showThemeColors(item.themeColor)"
:style="getThemeColorStyle(item.color)"
@click="setLayoutThemeColor(item.themeColor)"
>
<el-icon
style="margin: 0.1em 0.1em 0 0"
:size="17"
:color="getThemeColor(item.themeColor)"
>
<IconifyIconOffline :icon="Check" />
</el-icon>
</li>
</ul>
<el-divider>界面显示</el-divider>
<ul class="setting">
<li>
<span class="dark:text-white">灰色模式</span>
<el-switch
v-model="settings.greyVal"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="greyChange"
/>
</li>
<li>
<span class="dark:text-white">色弱模式</span>
<el-switch
v-model="settings.weakVal"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="weekChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏标签页</span>
<el-switch
v-model="settings.tabsVal"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="tagsChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏页脚</span>
<el-switch
v-model="settings.hideFooter"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="hideFooterChange"
/>
</li>
<li>
<span class="dark:text-white">侧边栏Logo</span>
<el-switch
v-model="logoVal"
inline-prompt
:active-value="true"
:inactive-value="false"
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="logoChange"
/>
</li>
<li>
<span class="dark:text-white">标签页持久化</span>
<el-switch
v-model="settings.multiTagsCache"
inline-prompt
inactive-color="#a6a6a6"
active-text="开"
inactive-text="关"
@change="multiTagsCacheChange"
/>
</li>
<li>
<span class="dark:text-white">标签风格</span>
<el-radio-group v-model="markValue" size="small" @change="onChange">
<el-radio label="card">卡片</el-radio>
<el-radio label="smart">灵动</el-radio>
</el-radio-group>
</li>
</ul>
<el-divider />
<el-button
type="danger"
style="width: 90%; margin: 24px 15px"
@click="onReset"
>
<IconifyIconOffline
:icon="Logout"
width="15"
height="15"
style="margin-right: 4px"
<p class="mt-5 mb-3 font-medium text-base dark:text-white">页签风格</p>
<Segmented
:modelValue="markValue === 'smart' ? 0 : 1"
:options="markOptions"
@change="onChange"
/>
清空缓存并返回登录页
</el-button>
<p class="mt-5 mb-1 font-medium text-sm dark:text-white">界面显示</p>
<ul class="setting">
<li>
<span class="dark:text-white">灰色模式</span>
<el-switch
v-model="settings.greyVal"
inline-prompt
active-text="开"
inactive-text="关"
@change="greyChange"
/>
</li>
<li>
<span class="dark:text-white">色弱模式</span>
<el-switch
v-model="settings.weakVal"
inline-prompt
active-text="开"
inactive-text="关"
@change="weekChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏标签页</span>
<el-switch
v-model="settings.tabsVal"
inline-prompt
active-text="开"
inactive-text="关"
@change="tagsChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏页脚</span>
<el-switch
v-model="settings.hideFooter"
inline-prompt
active-text="开"
inactive-text="关"
@change="hideFooterChange"
/>
</li>
<li>
<span class="dark:text-white">Logo</span>
<el-switch
v-model="logoVal"
inline-prompt
:active-value="true"
:inactive-value="false"
active-text="开"
inactive-text="关"
@change="logoChange"
/>
</li>
<li>
<span class="dark:text-white">页签持久化</span>
<el-switch
v-model="settings.multiTagsCache"
inline-prompt
active-text="开"
inactive-text="关"
@change="multiTagsCacheChange"
/>
</li>
</ul>
</div>
</panel>
</template>
@ -422,41 +391,41 @@ onBeforeMount(() => {
font-weight: 700;
}
.is-select {
border: 2px solid var(--el-color-primary);
:deep(.el-switch__core) {
--el-switch-off-color: var(--pure-switch-off-color);
min-width: 36px;
height: 18px;
}
.setting {
width: 100%;
:deep(.el-switch__core .el-switch__action) {
height: 14px;
}
.theme-color {
height: 20px;
li {
display: flex;
align-items: center;
justify-content: space-between;
margin: 25px;
}
}
float: left;
height: 20px;
margin-right: 8px;
cursor: pointer;
border-radius: 4px;
.pure-datatheme {
display: block;
width: 100%;
height: 50px;
padding-top: 25px;
text-align: center;
&:nth-child(1) {
border: 1px solid #ddd;
}
}
}
.pure-theme {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
width: 100%;
height: 50px;
margin-top: 25px;
gap: 12px;
li {
position: relative;
width: 18%;
height: 45px;
width: 46px;
height: 36px;
overflow: hidden;
cursor: pointer;
background: #f0f2f5;
@ -517,27 +486,17 @@ onBeforeMount(() => {
}
}
.theme-color {
display: flex;
justify-content: center;
width: 100%;
height: 40px;
margin-top: 20px;
.is-select {
border: 2px solid var(--el-color-primary);
}
.setting {
li {
float: left;
width: 20px;
height: 20px;
margin-top: 8px;
margin-right: 8px;
font-weight: 700;
text-align: center;
cursor: pointer;
border-radius: 2px;
&:nth-child(2) {
border: 1px solid #ddd;
}
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 0;
font-size: 14px;
}
}
</style>

View File

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

View File

@ -48,8 +48,8 @@ nextTick(() => {
<span>{{ title }}</span>
</div>
<el-menu
router
ref="menuRef"
router
mode="horizontal"
class="horizontal-header-menu"
:default-active="defaultActive"
@ -78,7 +78,7 @@ nextTick(() => {
:class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh"
>
<span class="check-zh" v-show="locale === 'zh'">
<span v-show="locale === 'zh'" class="check-zh">
<IconifyIconOffline :icon="Check" />
</span>
简体中文
@ -88,7 +88,7 @@ nextTick(() => {
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn"
>
<span class="check-en" v-show="locale === 'en'">
<span v-show="locale === 'en'" class="check-en">
<IconifyIconOffline :icon="Check" />
</span>
English

View File

@ -66,6 +66,6 @@ const toggleClick = () => {
width: 100%;
height: 40px;
line-height: 40px;
box-shadow: 0 0 6px -2px var(--el-color-primary);
box-shadow: 0 0 6px -3px var(--el-color-primary);
}
</style>

View File

@ -61,12 +61,12 @@ watch(
<template>
<div
v-if="device !== 'mobile'"
class="horizontal-header"
v-loading="usePermissionStoreHook().wholeMenus.length === 0"
class="horizontal-header"
>
<el-menu
router
ref="menuRef"
router
mode="horizontal"
class="horizontal-header-menu"
:default-active="defaultActive"
@ -111,7 +111,7 @@ watch(
:class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh"
>
<span class="check-zh" v-show="locale === 'zh'">
<span v-show="locale === 'zh'" class="check-zh">
<IconifyIconOffline :icon="Check" />
</span>
简体中文
@ -121,7 +121,7 @@ watch(
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn"
>
<span class="check-en" v-show="locale === 'en'">
<span v-show="locale === 'en'" class="check-en">
<IconifyIconOffline :icon="Check" />
</span>
English

View File

@ -60,8 +60,8 @@ const getsubMenuIconStyle = computed((): CSSProperties => {
layout.value === "horizontal"
? "0 5px 0 0"
: isCollapse.value
? "0 auto"
: "0 5px 0 0"
? "0 auto"
: "0 5px 0 0"
};
});
@ -96,8 +96,8 @@ const getSubMenuDivStyle = computed((): any => {
item?.parentId === null
? "center"
: layout.value === "mix" && item?.pathList?.length === 2
? "center"
: ""
? "center"
: ""
};
};
});
@ -275,7 +275,6 @@ function resolvePath(routePath) {
{{ transformI18n(props.item.meta.title) }}
</span>
<div
:style="getSubMenuDivStyle(props.item)"
v-if="
!(
isCollapse &&
@ -283,6 +282,7 @@ function resolvePath(routePath) {
props.item.parentId === null
)
"
:style="getSubMenuDivStyle(props.item)"
>
<el-tooltip
v-if="layout !== 'horizontal'"

View File

@ -518,15 +518,15 @@ onBeforeUnmount(() => {
</script>
<template>
<div ref="containerDom" class="tags-view" v-if="!showTags">
<div v-if="!showTags" ref="containerDom" class="tags-view">
<span v-show="isShowArrow" class="arrow-left">
<IconifyIconOffline :icon="ArrowLeftSLine" @click="handleScroll(200)" />
</span>
<div ref="scrollbarDom" class="scroll-container">
<div class="tab select-none" ref="tabDom" :style="getTabStyle">
<div ref="tabDom" class="tab select-none" :style="getTabStyle">
<div
:ref="'dynamic' + index"
v-for="(item, index) in multiTags"
:ref="'dynamic' + index"
:key="index"
:class="['scroll-item is-closable', linkIsActive(item)]"
@contextmenu.prevent="openMenu(item, $event)"
@ -550,8 +550,8 @@ onBeforeUnmount(() => {
<IconifyIconOffline :icon="CloseBold" />
</span>
<div
:ref="'schedule' + index"
v-if="showModel !== 'card'"
:ref="'schedule' + index"
:class="[scheduleIsActive(item)]"
/>
</div>

View File

@ -46,11 +46,11 @@ onMounted(() => {
<template>
<div
class="frame"
v-loading="loading"
class="frame"
:element-loading-text="t('status.hsLoad')"
>
<iframe :src="frameSrc" class="frame-iframe" ref="frameRef" />
<iframe ref="frameRef" :src="frameSrc" class="frame-iframe" />
</div>
</template>

View File

@ -1,9 +1,14 @@
import { ref } from "vue";
import { getConfig } from "@/config";
import { useLayout } from "./useLayout";
import { themeColorsType } from "../types";
import { useGlobal } from "@pureadmin/utils";
import { removeToken } from "@/utils/auth";
import { routerArrays } from "@/layout/types";
import { router, resetRouter } from "@/router";
import type { themeColorsType } from "../types";
import { useAppStoreHook } from "@/store/modules/app";
import { useGlobal, storageLocal } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import {
darken,
lighten,
@ -13,42 +18,53 @@ import {
export function useDataThemeChange() {
const { layoutTheme, layout } = useLayout();
const themeColors = ref<Array<themeColorsType>>([
/* 道奇蓝(默认) */
{ color: "#1b2a47", themeColor: "default" },
/* 亮白色 */
{ color: "#ffffff", themeColor: "light" },
/* 道奇蓝 */
{ color: "#1b2a47", themeColor: "default" },
/* 深紫罗兰色 */
{ color: "#722ed1", themeColor: "saucePurple" },
/* 深粉色 */
{ color: "#eb2f96", themeColor: "pink" },
/* 猩红色 */
{ color: "#f5222d", themeColor: "dusk" },
/* 橙红色 */
{ color: "#fa541c", themeColor: "volcano" },
/* 金色 */
{ color: "#fadb14", themeColor: "yellow" },
/* 绿宝石 */
{ color: "#13c2c2", themeColor: "mingQing" },
/* 酸橙绿 */
{ color: "#52c41a", themeColor: "auroraGreen" },
/* 深粉色 */
{ color: "#eb2f96", themeColor: "pink" },
/* 深紫罗兰色 */
{ color: "#722ed1", themeColor: "saucePurple" }
{ color: "#52c41a", themeColor: "auroraGreen" }
]);
const { $storage } = useGlobal<GlobalPropertiesApi>();
const dataTheme = ref<boolean>($storage?.layout?.darkMode);
const body = document.documentElement as HTMLElement;
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
const targetEl = target || document.body;
let { className } = targetEl;
className = className.replace(clsName, "").trim();
targetEl.className = flag ? `${className} ${clsName} ` : className;
}
/** 设置导航主题色 */
function setLayoutThemeColor(theme = getConfig().Theme ?? "default") {
function setLayoutThemeColor(
theme = getConfig().Theme ?? "light",
isClick = true
) {
layoutTheme.value.theme = theme;
toggleTheme({
scopeName: `layout-theme-${theme}`
});
// 如果非isClick保留之前的themeColor
const storageThemeColor = $storage.layout.themeColor;
$storage.layout = {
layout: layout.value,
theme,
darkMode: dataTheme.value,
sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: $storage.layout?.epThemeColor
epThemeColor: $storage.layout?.epThemeColor,
themeColor: isClick ? theme : storageThemeColor
};
if (theme === "default" || theme === "light") {
@ -78,27 +94,46 @@ export function useDataThemeChange() {
}
};
/** 日间、夜间主题切换 */
/** 亮色、暗色整体风格切换 */
function dataThemeChange() {
/* 如果当前是light夜间主题默认切换到default主题 */
if (useEpThemeStoreHook().epTheme === "light" && dataTheme.value) {
setLayoutThemeColor("default");
setLayoutThemeColor("default", false);
} else {
setLayoutThemeColor(useEpThemeStoreHook().epTheme);
setLayoutThemeColor(useEpThemeStoreHook().epTheme, false);
}
if (dataTheme.value) {
document.documentElement.classList.add("dark");
} else {
if ($storage.layout.themeColor === "light") {
setLayoutThemeColor("light", false);
}
document.documentElement.classList.remove("dark");
}
}
/** 清空缓存并返回登录页 */
function onReset() {
removeToken();
storageLocal().clear();
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout);
setEpThemeColor(EpThemeColor);
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
toggleClass(Grey, "html-grey", document.querySelector("html"));
toggleClass(Weak, "html-weakness", document.querySelector("html"));
router.push("/login");
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
resetRouter();
}
return {
body,
dataTheme,
layoutTheme,
themeColors,
onReset,
toggleClass,
dataThemeChange,
setEpThemeColor,
setLayoutThemeColor

View File

@ -24,10 +24,11 @@ export function useLayout() {
if (!$storage.layout) {
$storage.layout = {
layout: $config?.Layout ?? "vertical",
theme: $config?.Theme ?? "default",
theme: $config?.Theme ?? "light",
darkMode: $config?.DarkMode ?? false,
sidebarStatus: $config?.SidebarStatus ?? true,
epThemeColor: $config?.EpThemeColor ?? "#409EFF"
epThemeColor: $config?.EpThemeColor ?? "#409EFF",
themeColor: $config?.Theme ?? "light"
};
}
/** 灰色模式、色弱模式、隐藏标签页 */

View File

@ -2,10 +2,10 @@ import { storeToRefs } from "pinia";
import { getConfig } from "@/config";
import { useRouter } from "vue-router";
import { emitter } from "@/utils/mitt";
import { routeMetaType } from "../types";
import userAvatar from "@/assets/user.jpg";
import { getTopMenu } from "@/router/utils";
import { useGlobal } from "@pureadmin/utils";
import type { routeMetaType } from "../types";
import { transformI18n } from "@/plugins/i18n";
import { router, remainingPaths } from "@/router";
import { computed, type CSSProperties } from "vue";

View File

@ -4,10 +4,10 @@ import {
computed,
reactive,
onMounted,
CSSProperties,
type CSSProperties,
getCurrentInstance
} from "vue";
import { tagsViewsType } from "../types";
import type { tagsViewsType } from "../types";
import { useRoute, useRouter } from "vue-router";
import { transformI18n, $t } from "@/plugins/i18n";
import { responsiveStorageNameSpace } from "@/config";

View File

@ -68,7 +68,8 @@ function setTheme(layoutModel: string) {
theme: $storage.layout?.theme,
darkMode: $storage.layout?.darkMode,
sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: $storage.layout?.epThemeColor
epThemeColor: $storage.layout?.epThemeColor,
themeColor: $storage.layout?.themeColor
};
}

View File

@ -2,10 +2,23 @@
* @description 使
*/
import { type multipleScopeVarsOptions } from "@pureadmin/theme";
import type { multipleScopeVarsOptions } from "@pureadmin/theme";
/** 预设主题色 */
const themeColors = {
/* 亮白色 */
light: {
subMenuActiveText: "#000000d9",
menuBg: "#fff",
menuHover: "#e0ebf6",
subMenuBg: "#fff",
subMenuActiveBg: "#e0ebf6",
menuText: "rgb(0 0 0 / 60%)",
sidebarLogo: "#fff",
menuTitleHover: "#000",
menuActiveBefore: "#4091f7"
},
/* 道奇蓝 */
default: {
subMenuActiveText: "#fff",
menuBg: "#001529",
@ -17,72 +30,19 @@ const themeColors = {
menuTitleHover: "#fff",
menuActiveBefore: "#4091f7"
},
light: {
subMenuActiveText: "#409eff",
menuBg: "#fff",
menuHover: "#e0ebf6",
subMenuBg: "#fff",
subMenuActiveBg: "#e0ebf6",
menuText: "#7a80b4",
sidebarLogo: "#fff",
menuTitleHover: "#000",
menuActiveBefore: "#4091f7"
},
dusk: {
/* 深紫罗兰色 */
saucePurple: {
subMenuActiveText: "#fff",
menuBg: "#2a0608",
menuHover: "#e13c39",
menuBg: "#130824",
menuHover: "#693ac9",
subMenuBg: "#000",
subMenuActiveBg: "#e13c39",
menuText: "rgb(254 254 254 / 65.1%)",
sidebarLogo: "#42090c",
menuTitleHover: "#fff",
menuActiveBefore: "#e13c39"
},
volcano: {
subMenuActiveText: "#fff",
menuBg: "#2b0e05",
menuHover: "#e85f33",
subMenuBg: "#0f0603",
subMenuActiveBg: "#e85f33",
menuText: "rgb(254 254 254 / 65%)",
sidebarLogo: "#441708",
menuTitleHover: "#fff",
menuActiveBefore: "#e85f33"
},
yellow: {
subMenuActiveText: "#d25f00",
menuBg: "#2b2503",
menuHover: "#f6da4d",
subMenuBg: "#0f0603",
subMenuActiveBg: "#f6da4d",
menuText: "rgb(254 254 254 / 65%)",
sidebarLogo: "#443b05",
menuTitleHover: "#fff",
menuActiveBefore: "#f6da4d"
},
mingQing: {
subMenuActiveText: "#fff",
menuBg: "#032121",
menuHover: "#59bfc1",
subMenuBg: "#000",
subMenuActiveBg: "#59bfc1",
subMenuActiveBg: "#693ac9",
menuText: "#7a80b4",
sidebarLogo: "#053434",
sidebarLogo: "#1f0c38",
menuTitleHover: "#fff",
menuActiveBefore: "#59bfc1"
},
auroraGreen: {
subMenuActiveText: "#fff",
menuBg: "#0b1e15",
menuHover: "#60ac80",
subMenuBg: "#000",
subMenuActiveBg: "#60ac80",
menuText: "#7a80b4",
sidebarLogo: "#112f21",
menuTitleHover: "#fff",
menuActiveBefore: "#60ac80"
menuActiveBefore: "#693ac9"
},
/* 深粉色 */
pink: {
subMenuActiveText: "#fff",
menuBg: "#28081a",
@ -94,16 +54,53 @@ const themeColors = {
menuTitleHover: "#fff",
menuActiveBefore: "#d84493"
},
saucePurple: {
/* 猩红色 */
dusk: {
subMenuActiveText: "#fff",
menuBg: "#130824",
menuHover: "#693ac9",
menuBg: "#2a0608",
menuHover: "#e13c39",
subMenuBg: "#000",
subMenuActiveBg: "#693ac9",
menuText: "#7a80b4",
sidebarLogo: "#1f0c38",
subMenuActiveBg: "#e13c39",
menuText: "rgb(254 254 254 / 65.1%)",
sidebarLogo: "#42090c",
menuTitleHover: "#fff",
menuActiveBefore: "#693ac9"
menuActiveBefore: "#e13c39"
},
/* 橙红色 */
volcano: {
subMenuActiveText: "#fff",
menuBg: "#2b0e05",
menuHover: "#e85f33",
subMenuBg: "#0f0603",
subMenuActiveBg: "#e85f33",
menuText: "rgb(254 254 254 / 65%)",
sidebarLogo: "#441708",
menuTitleHover: "#fff",
menuActiveBefore: "#e85f33"
},
/* 绿宝石 */
mingQing: {
subMenuActiveText: "#fff",
menuBg: "#032121",
menuHover: "#59bfc1",
subMenuBg: "#000",
subMenuActiveBg: "#59bfc1",
menuText: "#7a80b4",
sidebarLogo: "#053434",
menuTitleHover: "#fff",
menuActiveBefore: "#59bfc1"
},
/* 酸橙绿 */
auroraGreen: {
subMenuActiveText: "#fff",
menuBg: "#0b1e15",
menuHover: "#60ac80",
subMenuBg: "#000",
subMenuActiveBg: "#60ac80",
menuText: "#7a80b4",
sidebarLogo: "#112f21",
menuTitleHover: "#fff",
menuActiveBefore: "#60ac80"
}
};

View File

@ -4,9 +4,9 @@ import { setupStore } from "@/store";
import ElementPlus from "element-plus";
import { useI18n } from "@/plugins/i18n";
import { getPlatformConfig } from "./config";
import { createApp, Directive } from "vue";
import { MotionPlugin } from "@vueuse/motion";
import { useEcharts } from "@/plugins/echarts";
import { createApp, type Directive } from "vue";
import { injectResponsiveStorage } from "@/utils/responsive";
import Table from "@pureadmin/table";
@ -45,6 +45,14 @@ app.component("FontIcon", FontIcon);
import { Auth } from "@/components/ReAuth";
app.component("Auth", Auth);
// 全局注册`vue-tippy`
import "tippy.js/dist/tippy.css";
import "tippy.js/animations/perspective.css";
import VueTippy from "vue-tippy";
app.use(VueTippy, {
defaultProps: { animation: "perspective" }
});
getPlatformConfig(app).then(async config => {
setupStore(app);
app.use(router);

View File

@ -1,14 +0,0 @@
import { createProdMockServer } from "vite-plugin-mock/es/createProdMockServer";
const modules: Record<string, any> = import.meta.glob("../mock/*.ts", {
eager: true
});
const mockModules = [];
Object.keys(modules).forEach(key => {
mockModules.push(...modules[key].default);
});
export function setupProdMockServer() {
createProdMockServer(mockModules);
}

View File

@ -1,4 +1,4 @@
import { App, Component } from "vue";
import type { App, Component } from "vue";
import {
ElTag,
ElAffix,

View File

@ -1,6 +1,6 @@
// 多组件库的国际化和本地项目国际化兼容
import { App, WritableComputedRef } from "vue";
import { type I18n, createI18n } from "vue-i18n";
import type { App, WritableComputedRef } from "vue";
import { responsiveStorageNameSpace } from "@/config";
import { storageLocal, isObject } from "@pureadmin/utils";
@ -8,16 +8,20 @@ import { storageLocal, isObject } from "@pureadmin/utils";
import enLocale from "element-plus/dist/locale/en.mjs";
import zhLocale from "element-plus/dist/locale/zh-cn.mjs";
function siphonI18n(prefix = "zh-CN") {
return Object.fromEntries(
const siphonI18n = (function () {
// 仅初始化一次国际化配置
let cache = Object.fromEntries(
Object.entries(
import.meta.glob("../../locales/*.y(a)?ml", { eager: true })
).map(([key, value]: any) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)[1];
return [matched, value.default];
})
)[prefix];
}
);
return (prefix = "zh-CN") => {
return cache[prefix];
};
})();
export const localesConfigs = {
zh: {
@ -33,7 +37,7 @@ export const localesConfigs = {
/** 获取对象中所有嵌套对象的key键并将它们用点号分割组成字符串 */
function getObjectKeys(obj) {
const stack = [];
const keys = [];
const keys: Set<string> = new Set();
stack.push({ obj, key: "" });
@ -46,7 +50,7 @@ function getObjectKeys(obj) {
if (obj[k] && isObject(obj[k])) {
stack.push({ obj: obj[k], key: newKey });
} else {
keys.push(newKey);
keys.add(newKey);
}
}
}
@ -54,6 +58,17 @@ function getObjectKeys(obj) {
return keys;
}
/** 将展开的key缓存 */
const keysCache: Map<string, Set<string>> = new Map();
const flatI18n = (prefix = "zh-CN") => {
let cache = keysCache.get(prefix);
if (!cache) {
cache = getObjectKeys(siphonI18n(prefix));
keysCache.set(prefix, cache);
}
return cache;
};
/**
* locales文件夹下文件进行国际化匹配
* @param message message
@ -73,9 +88,9 @@ export function transformI18n(message: any = "") {
const key = message.match(/(\S*)\./)?.input;
if (key && getObjectKeys(siphonI18n("zh-CN")).find(item => item === key)) {
if (key && flatI18n("zh-CN").has(key)) {
return i18n.global.t.call(i18n.global.locale, message);
} else if (!key && Object.keys(siphonI18n("zh-CN")).includes(message)) {
} else if (!key && Object.hasOwn(siphonI18n("zh-CN"), message)) {
// 兼容非嵌套形式的国际化写法
return i18n.global.t.call(i18n.global.locale, message);
} else {

View File

@ -20,10 +20,10 @@ import {
formatFlatteningRoutes
} from "./utils";
import {
Router,
type Router,
createRouter,
RouteRecordRaw,
RouteComponent
type RouteRecordRaw,
type RouteComponent
} from "vue-router";
import {
type DataInfo,

View File

@ -183,4 +183,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -18,4 +18,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -171,4 +171,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -20,4 +20,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -36,4 +36,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -19,4 +19,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -22,4 +22,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -19,4 +19,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -24,4 +24,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -21,4 +21,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -19,4 +19,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -79,4 +79,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -21,4 +21,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -21,4 +21,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -21,4 +21,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -39,4 +39,4 @@ export default [
rank: 103
}
}
] as Array<RouteConfigsTable>;
] satisfies Array<RouteConfigsTable>;

View File

@ -27,4 +27,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -28,4 +28,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@ -1,7 +1,7 @@
import {
RouterHistory,
RouteRecordRaw,
RouteComponent,
type RouterHistory,
type RouteRecordRaw,
type RouteComponent,
createWebHistory,
createWebHashHistory
} from "vue-router";
@ -17,7 +17,7 @@ import {
isIncludeAllChildren
} from "@pureadmin/utils";
import { getConfig } from "@/config";
import { menuType } from "@/layout/types";
import type { menuType } from "@/layout/types";
import { buildHierarchyTree } from "@/utils/tree";
import { userKey, type DataInfo } from "@/utils/auth";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";

View File

@ -1,6 +1,6 @@
import { store } from "@/store";
import { appType } from "./types";
import { defineStore } from "pinia";
import type { appType } from "./types";
import { getConfig, responsiveStorageNameSpace } from "@/config";
import { deviceDetection, storageLocal } from "@pureadmin/utils";

View File

@ -23,8 +23,6 @@ export const useEpThemeStore = defineStore({
fill(state) {
if (state.epTheme === "light") {
return "#409eff";
} else if (state.epTheme === "yellow") {
return "#d25f00";
} else {
return "#fff";
}

View File

@ -1,8 +1,8 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { routerArrays } from "@/layout/types";
import { multiType, positionType } from "./types";
import { responsiveStorageNameSpace } from "@/config";
import type { multiType, positionType } from "./types";
import { isEqual, isBoolean, isUrl, storageLocal } from "@pureadmin/utils";
export const useMultiTagsStore = defineStore({

View File

@ -1,6 +1,6 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { cacheType } from "./types";
import type { cacheType } from "./types";
import { constantMenus } from "@/router";
import { useMultiTagsStoreHook } from "./multiTags";
import { debounce, getKeyList } from "@pureadmin/utils";

View File

@ -1,7 +1,7 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { setType } from "./types";
import { getConfig } from "@/config";
import type { setType } from "./types";
export const useSettingStore = defineStore({
id: "pure-setting",

View File

@ -1,4 +1,4 @@
import { RouteRecordName } from "vue-router";
import type { RouteRecordName } from "vue-router";
export type cacheType = {
mode: string;

View File

@ -1,11 +1,11 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { userType } from "./types";
import type { userType } from "./types";
import { routerArrays } from "@/layout/types";
import { router, resetRouter } from "@/router";
import { storageLocal } from "@pureadmin/utils";
import { getLogin, refreshTokenApi } from "@/api/user";
import { UserResult, RefreshTokenResult } from "@/api/user";
import type { UserResult, RefreshTokenResult } from "@/api/user";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth";

View File

@ -2,11 +2,18 @@
/* 暗黑模式适配 */
html.dark {
/* 自定义深色背景颜色 */
// --el-bg-color: #020409;
$border-style: #303030;
$color-white: #fff;
/* 自定义深色背景颜色 */
// --el-bg-color: #020409;
/* 常用border-color 需要时可取用 */
--pure-border-color: rgb(253 253 253 / 12%);
/* switch关闭状态下的color 需要时可取用 */
--pure-switch-off-color: #ffffff3f;
.navbar,
.tags-view,
.contextmenu,

View File

@ -50,12 +50,6 @@
padding: 0 !important;
}
/* 自定义 tooltip 的类名 */
.pure-tooltip {
// 右侧操作面板right-panel类名的z-index为40000tooltip需要大于它才能显示
z-index: 41000 !important;
}
/* nprogress 适配 element-plus 的主题色 */
#nprogress {
& .bar {

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