Compare commits

..

7 Commits

Author SHA1 Message Date
xiaoxian521
fa915fe4c3 chore: update 2025-03-24 13:33:18 +08:00
xiaoxian521
b2d486928d chore: 继续使用addIcon添加离线图标,因为addCollection添加图标后无法Tree-shaking 2025-03-24 13:25:55 +08:00
xiaoxian521
30781df972 chore: update 2025-03-23 14:41:59 +08:00
xiaoxian521
4984127318 chore: update 2025-03-23 09:03:00 +08:00
xiaoxian521
6ab64a9906 chore: update 2025-03-22 13:26:02 +08:00
xiaoxian521
2d06dc1dd8 chore: update 2025-03-20 15:57:13 +08:00
xiaoxian521
818f12d1ea refactor: 使用@iconify-json/*包替换不再维护的@iconify-icons/*依赖,确保图标库可持续更新并优化使用体验 2025-03-16 18:46:16 +08:00
167 changed files with 3739 additions and 4456 deletions

3
.gitignore vendored
View File

@@ -1,9 +1,10 @@
node_modules node_modules
.DS_Store .DS_Store
dist
dist-ssr
*.local *.local
.eslintcache .eslintcache
report.html report.html
dist
vite.config.*.timestamp* vite.config.*.timestamp*
yarn.lock yarn.lock

8
.husky/commit-msg Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
# shellcheck source=./_/husky.sh
. "$(dirname "$0")/_/husky.sh"
PATH="/usr/local/bin:$PATH"
npx --no-install commitlint --edit "$1"

9
.husky/common.sh Normal file
View File

@@ -0,0 +1,9 @@
#!/bin/sh
command_exists () {
command -v "$1" >/dev/null 2>&1
}
# Workaround for Windows 10, Git Bash and Pnpm
if command_exists winpty && test -t 1; then
exec < /dev/tty
fi

10
.husky/pre-commit Executable file
View File

@@ -0,0 +1,10 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
. "$(dirname "$0")/common.sh"
[ -n "$CI" ] && exit 0
PATH="/usr/local/bin:$PATH"
# Perform lint check on files in the staging area through .lintstagedrc configuration
pnpm exec lint-staged

2
.nvmrc
View File

@@ -1 +1 @@
v22.20.0 v22.14.0

View File

@@ -1,5 +1,4 @@
{ {
"tailwindCSS.experimental.configFile": "src/style/tailwind.css",
"editor.formatOnType": true, "editor.formatOnType": true,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"[vue]": { "[vue]": {

View File

@@ -1,74 +1,8 @@
# 6.2.0 (2025-10-16)
### 🎫 Features
- Added full-screen `403`, `404`, and `500` error pages. These full-screen error pages are clear and secure, improving the user experience.
### 🐞 Bug Fixes
- Fixed an issue where the built-in homepage did not have a `name` configured, causing cache invalidation after setting the page cache.
- Fixed an issue where, in an embedded same-origin `iframe` page, when the `beforeunload` event was registered, right-clicking a tab and reloading it would cause the browser to prompt two confirmation blocks.
- Fixed an issue where pages with `keepAlive: true` set to cache invalidation when the initial load was slow.
- Fixed an issue where multiple tabs could be activated simultaneously when using the same parameters in different routes.
- Fixed an issue where the right-click menu on a tab displayed incorrectly when passing `params` parameters.
### 🍏 Perf
- Optimized the `nprogress` progress bar. It no longer displays when reloading a page or requesting an interface, improving the user experience.
- Optimized the timing of capturing all unmatched routes and redirecting to a full-screen `404` page.
- Explicitly configured the `Tailwind CSS` entry file path to improve the contextual recognition and prompting performance of the `Tailwind CSS IntelliSense` plugin
# 6.1.0 (2025-07-31)
### ✔️ Refactor
- Upgrade to `vite7`, update dependencies, and related compatibility processing
### 🐞 Bug fixes
- Fixed a flickering issue in the `ReSegmented` segmented controller component when switching between light and dark styles
- Fixed an issue where `resetRouter` did not clear all routing data
- Fixed an issue where closing the left tab in the tabs window did not work properly
### 🍏 Perf
- Optimized navigation styles
- Upgraded `@pureadmin/table` to be compatible with all `el-table` APIs in the latest `element-plus` version.
# 6.0.0 (2025-04-10)
### ✔️ Refactor
- Refactor the icon module, use `@iconify/json` to replace the `@iconify-icons/*` dependency that is no longer maintained and updated, optimize the user experience, ensure that the icon library can be continuously updated and support `Tree-shaking`
- Upgrade `tailwindcss` to `v4` version, bringing faster build speed, simpler installation and configuration, and providing a dedicated `vite` plug-in
### 🎫 Feat
- Add `Ai` chat component example
- Add `tagOnClick` tag to switch global public events
- Add code editor example
- Add `Markdown` example
- Add slider example
### 🐞 Bug fixes
- Fix `aria-hidden` error
- Fix the problem that code hints cannot be displayed when using `this` syntax and update `pinia` related syntax
### 🍏 Perf
- Fix broken links in the waterfall infinite scrolling example
- Update `vue-flow`, related compatibility processing
- Delete the deprecated dependency package `eslint-define-config`, upgrade `eslint` to the latest version, related compatibility processing
- Optimize `src/style/dark.scss` syntax
- Optimize login parameter transfer
- Use `keydown` to replace `keypress`, the `keypress` event has been deprecated
# 5.9.0 (2024-12-10) # 5.9.0 (2024-12-10)
### ✔Refactor ### ✔Refactor
- Upgrade `vite` to `v6` version, upgrade `sass` to the latest version, reconstruct the theme writing method, and deprecate [@pureadmin/theme](https://www.npmjs.com/package/@pureadmin/theme) , click to view [Related optimization point details](https://github.com/pure-admin/vue-pure-admin/pull/1188#issue-2630095115). For users who have the [Max version](https://pure-admin.cn/pages/service/#max-%E7%89%88%E6%9C%AC), it is strongly recommended to upgrade. Subsequent Max version users will enjoy a more modern, beautiful and highly customized theme color - Upgrade `vite` to `v6` version, upgrade `sass` to the latest version, reconstruct the theme writing method, and deprecate [@pureadmin/theme](https://www.npmjs.com/package/@pureadmin/theme) , click to view [Related optimization point details](https://github.com/pure-admin/vue-pure-admin/pull/1188#issue-2630095115). For users who have the [Max version](https://pure-admin.cn/pages/max/), it is strongly recommended to upgrade. Subsequent Max version users will enjoy a more modern, beautiful and highly customized theme color
- Use [code-inspector-plugin](https://www.npmjs.com/package/code-inspector-plugin) to replace [vite-plugin-vue-inspector](https://www.npmjs.com/package/vite-plugin-vue-inspector) - Use [code-inspector-plugin](https://www.npmjs.com/package/code-inspector-plugin) to replace [vite-plugin-vue-inspector](https://www.npmjs.com/package/vite-plugin-vue-inspector)
### 🎫Feat ### 🎫Feat

View File

@@ -1,74 +1,8 @@
# 6.2.0 (2025-10-16)
### 🎫 Features
- Added full-screen `403`, `404`, and `500` error pages. These full-screen error pages are clear and secure, improving the user experience.
### 🐞 Bug Fixes
- Fixed an issue where the built-in homepage did not have a `name` configured, causing cache invalidation after setting the page cache.
- Fixed an issue where, in an embedded same-origin `iframe` page, when the `beforeunload` event was registered, right-clicking a tab and reloading it would cause the browser to prompt two confirmation blocks.
- Fixed an issue where pages with `keepAlive: true` set to cache invalidation when the initial load was slow.
- Fixed an issue where multiple tabs could be activated simultaneously when using the same parameters in different routes.
- Fixed an issue where the right-click menu on a tab displayed incorrectly when passing `params` parameters.
### 🍏 Perf
- Optimized the `nprogress` progress bar. It no longer displays when reloading a page or requesting an interface, improving the user experience.
- Optimized the timing of capturing all unmatched routes and redirecting to a full-screen `404` page.
- Explicitly configured the `Tailwind CSS` entry file path to improve the contextual recognition and prompting performance of the `Tailwind CSS IntelliSense` plugin
# 6.1.0 (2025-07-31)
### ✔️ Refactor
- Upgrade to `vite7`, update dependencies, and related compatibility processing
### 🐞 Bug fixes
- Fixed a flickering issue in the `ReSegmented` segmented controller component when switching between light and dark styles
- Fixed an issue where `resetRouter` did not clear all routing data
- Fixed an issue where closing the left tab in the tabs window did not work properly
### 🍏 Perf
- Optimized navigation styles
- Upgraded `@pureadmin/table` to be compatible with all `el-table` APIs in the latest `element-plus` version.
# 6.0.0 (2025-04-10)
### ✔️ Refactor
- Refactor the icon module, use `@iconify/json` to replace the `@iconify-icons/*` dependency that is no longer maintained and updated, optimize the user experience, ensure that the icon library can be continuously updated and support `Tree-shaking`
- Upgrade `tailwindcss` to `v4` version, bringing faster build speed, simpler installation and configuration, and providing a dedicated `vite` plug-in
### 🎫 Feat
- Add `Ai` chat component example
- Add `tagOnClick` tag to switch global public events
- Add code editor example
- Add `Markdown` example
- Add slider example
### 🐞 Bug fixes
- Fix `aria-hidden` error
- Fix the problem that code hints cannot be displayed when using `this` syntax and update `pinia` related syntax
### 🍏 Perf
- Fix broken links in the waterfall infinite scrolling example
- Update `vue-flow`, related compatibility processing
- Delete the deprecated dependency package `eslint-define-config`, upgrade `eslint` to the latest version, related compatibility processing
- Optimize `src/style/dark.scss` syntax
- Optimize login parameter transfer
- Use `keydown` to replace `keypress`, the `keypress` event has been deprecated
# 5.9.0 (2024-12-10) # 5.9.0 (2024-12-10)
### ✔Refactor ### ✔Refactor
- Upgrade `vite` to `v6` version, upgrade `sass` to the latest version, reconstruct the theme writing method, and deprecate [@pureadmin/theme](https://www.npmjs.com/package/@pureadmin/theme) , click to view [Related optimization point details](https://github.com/pure-admin/vue-pure-admin/pull/1188#issue-2630095115). For users who have the [Max version](https://pure-admin.cn/pages/service/#max-%E7%89%88%E6%9C%AC), it is strongly recommended to upgrade. Subsequent Max version users will enjoy a more modern, beautiful and highly customized theme color - Upgrade `vite` to `v6` version, upgrade `sass` to the latest version, reconstruct the theme writing method, and deprecate [@pureadmin/theme](https://www.npmjs.com/package/@pureadmin/theme) , click to view [Related optimization point details](https://github.com/pure-admin/vue-pure-admin/pull/1188#issue-2630095115). For users who have the [Max version](https://pure-admin.cn/pages/max/), it is strongly recommended to upgrade. Subsequent Max version users will enjoy a more modern, beautiful and highly customized theme color
- Use [code-inspector-plugin](https://www.npmjs.com/package/code-inspector-plugin) to replace [vite-plugin-vue-inspector](https://www.npmjs.com/package/vite-plugin-vue-inspector) - Use [code-inspector-plugin](https://www.npmjs.com/package/code-inspector-plugin) to replace [vite-plugin-vue-inspector](https://www.npmjs.com/package/vite-plugin-vue-inspector)
### 🎫Feat ### 🎫Feat

View File

@@ -1,74 +1,8 @@
# 6.2.0 (2025-10-16)
### 🎫 Feat
- 添加全屏`403``404``500`页面,全屏错误页面清晰且安全,提升用户体验
### 🐞 Bug fixes
- 修复内置的首页未设置`name`导致设置页面缓存后缓存无效的问题
- 修复在内嵌同源`iframe`页面中,当其注册了`beforeunload`事件时,右键标签页点击重新加载导致浏览器弹出两次确认拦截的问题
- 修复设置了`keepAlive: true`的页面在初次加载缓慢的情况下出现的页面缓存失效的问题
- 修复不同路由使用相同参数时,多个标签页会同时被激活的问题
- 修复`params`传参模式下标签页右键菜单显示不正确的问题
### 🍏 Perf
- 优化`nprogress`进度条,页面重进或接口请求时不再显示进度条,提升用户体验
- 优化当捕获所有未匹配路由并跳转全屏`404`页面的时机
- 显式配置`Tailwind CSS`入口文件路径,优化`Tailwind CSS IntelliSense`插件的上下文识别与提示性能
# 6.1.0 (2025-07-31)
### ✔️ Refactor
- 升级至`vite7`,更新依赖,相关兼容处理
### 🐞 Bug fixes
- 修复`ReSegmented`分段控制器组件在浅色和深色整体风格切换时的闪烁问题
- 修复`resetRouter`未清空全部路由数据问题
- 修复标签页-关闭左侧标签页关闭异常问题
### 🍏 Perf
- 优化导航样式
- 升级`@pureadmin/table`兼容最新版`element-plus``el-table`的所有`API`
# 6.0.0 (2025-04-10)
### ✔️ Refactor
- 重构图标模块,使用`@iconify/json`替换不再维护更新的`@iconify-icons/*`依赖,优化使用体验,确保图标库可持续更新并支持`Tree-shaking`
- 升级`tailwindcss``v4`版本,带来更快的构建速度、更简化的安装和配置、提供专属`vite`插件
### 🎫 Feat
- 添加`Ai`聊天组件示例
- 添加`tagOnClick`标签切换全局公共事件
- 添加代码编辑器示例
- 添加`Markdown`示例
- 添加滑块示例
### 🐞 Bug fixes
- 修复`aria-hidden`报错
- 修复使用`this`语法时无法显示代码提示的问题并更新`pinia`相关语法
### 🍏 Perf
- 修复组件-瀑布流无限滚动示例中失效的链接
- 更新`vue-flow`,相关兼容处理
- 删除已弃用的依赖包`eslint-define-config`,升级`eslint`至最新版本,相关兼容处理
- 优化`src/style/dark.scss`语法
- 优化登录传参
- 使用`keydown`替换`keypress``keypress`事件已弃用
# 5.9.0 (2024-12-10) # 5.9.0 (2024-12-10)
### ✔️ Refactor ### ✔️ Refactor
- 升级`vite``v6`版本,升级`sass`至最新版,重构主题写法,弃用 [@pureadmin/theme](https://www.npmjs.com/package/@pureadmin/theme),点击查看 [相关优化点细节](https://github.com/pure-admin/vue-pure-admin/pull/1188#issue-2630095115)。对于拥有 [Max版本](https://pure-admin.cn/pages/service/#max-%E7%89%88%E6%9C%AC) 的用户平台强烈建议升级,后续`Max版本用户`会享有一套更现代、美观且自定义程度高的主题色 - 升级`vite``v6`版本,升级`sass`至最新版,重构主题写法,弃用 [@pureadmin/theme](https://www.npmjs.com/package/@pureadmin/theme),点击查看 [相关优化点细节](https://github.com/pure-admin/vue-pure-admin/pull/1188#issue-2630095115)。对于拥有 [Max版本](https://pure-admin.cn/pages/max/) 的用户平台强烈建议升级,后续`Max版本用户`会享有一套更现代、美观且自定义程度高的主题色
- 使用 [code-inspector-plugin](https://www.npmjs.com/package/code-inspector-plugin) 替换 [vite-plugin-vue-inspector](https://www.npmjs.com/package/vite-plugin-vue-inspector) - 使用 [code-inspector-plugin](https://www.npmjs.com/package/code-inspector-plugin) 替换 [vite-plugin-vue-inspector](https://www.npmjs.com/package/vite-plugin-vue-inspector)
### 🎫 Feat ### 🎫 Feat

View File

@@ -31,10 +31,18 @@ The simplified version is based on the shelf extracted from [vue-pure-admin](htt
[Click me to view vue-pure-admin documentation](https://pure-admin.cn/) [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)
## Premium service ## Quality service, software outsourcing, sponsorship support
[Click me for details](https://pure-admin.cn/pages/service/) [Click me for details](https://pure-admin.cn/pages/service/)
## `js` version
[Click me to view js version](https://pure-admin.cn/pages/js/)
## `max` version
[Click me to view the max version](https://pure-admin.cn/pages/max/)
## Tauri ## Tauri
[Click Watch Tauri](https://github.com/pure-admin/tauri-pure-admin) [Click Watch Tauri](https://github.com/pure-admin/tauri-pure-admin)
@@ -184,7 +192,6 @@ Thank you very much for your in-depth understanding of the source code and your
| [edgexie](https://github.com/edgexie) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=edgexie) | | [edgexie](https://github.com/edgexie) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=edgexie) |
| [way-jm](https://github.com/way-jm) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=way-jm) | | [way-jm](https://github.com/way-jm) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=way-jm) |
| [simple-hui](https://github.com/simple-hui) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=simple-hui) | | [simple-hui](https://github.com/simple-hui) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=simple-hui) |
| [tinysimple](https://github.com/tinysimple) | [code](https://github.com/pure-admin/vue-pure-admin/commits?author=tinysimple) |
## Git Contribution submission specification ## Git Contribution submission specification

View File

@@ -32,10 +32,18 @@
[点我查看 vue-pure-admin 文档](https://pure-admin.cn/) [点我查看 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.cn/pages/service/) [点我查看详情](https://pure-admin.cn/pages/service/)
## `js` 版本
[点我查看 js 版本](https://pure-admin.cn/pages/js/)
## `max` 版本
[点我查看 max 版本](https://pure-admin.cn/pages/max/)
## `Tauri` 版本 ## `Tauri` 版本
[点我查看 Tauri 版本](https://github.com/pure-admin/tauri-pure-admin) [点我查看 Tauri 版本](https://github.com/pure-admin/tauri-pure-admin)
@@ -185,7 +193,6 @@ docker run -dp 8080:80 --name pure-admin vue-pure-admin
| [edgexie](https://github.com/edgexie) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=edgexie) | | [edgexie](https://github.com/edgexie) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=edgexie) |
| [way-jm](https://github.com/way-jm) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=way-jm) | | [way-jm](https://github.com/way-jm) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=way-jm) |
| [simple-hui](https://github.com/simple-hui) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=simple-hui) | | [simple-hui](https://github.com/simple-hui) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=simple-hui) |
| [tinysimple](https://github.com/tinysimple) | [代码](https://github.com/pure-admin/vue-pure-admin/commits?author=tinysimple) |
## `Git` 贡献提交规范 ## `Git` 贡献提交规范

View File

@@ -6,7 +6,6 @@ import svgLoader from "vite-svg-loader";
import Icons from "unplugin-icons/vite"; import Icons from "unplugin-icons/vite";
import type { PluginOption } from "vite"; import type { PluginOption } from "vite";
import vueJsx from "@vitejs/plugin-vue-jsx"; import vueJsx from "@vitejs/plugin-vue-jsx";
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";
@@ -21,7 +20,6 @@ export function getPluginsList(
): PluginOption[] { ): PluginOption[] {
const lifecycle = process.env.npm_lifecycle_event; const lifecycle = process.env.npm_lifecycle_event;
return [ return [
tailwindcss(),
vue({ vue({
template: { template: {
compilerOptions: { compilerOptions: {

View File

@@ -1,25 +1,26 @@
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 { defineConfig, globalIgnores } from "eslint/config"; import { defineFlatConfig } from "eslint-define-config";
import * as parserTypeScript from "@typescript-eslint/parser";
import pluginTypeScript from "@typescript-eslint/eslint-plugin";
export default defineConfig([ export default defineFlatConfig([
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: {
// types/index.d.ts // index.d.ts
RefType: "readonly", RefType: "readonly",
EmitType: "readonly", EmitType: "readonly",
TargetContext: "readonly", TargetContext: "readonly",
@@ -72,10 +73,21 @@ export default defineConfig([
] ]
} }
}, },
...tseslint.config({ {
extends: [...tseslint.configs.recommended],
files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"], files: ["**/*.?([cm])ts", "**/*.?([cm])tsx"],
languageOptions: {
parser: parserTypeScript,
parserOptions: {
sourceType: "module",
warnOnUnsupportedTypeScriptVersion: false
}
},
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",
@@ -102,20 +114,20 @@ export default defineConfig([
} }
] ]
} }
}), },
{ {
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"
} }
}, },
{ {
@@ -136,19 +148,18 @@ export default defineConfig([
jsx: true jsx: true
}, },
extraFileExtensions: [".vue"], extraFileExtensions: [".vue"],
parser: tseslint.parser, parser: "@typescript-eslint/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.essential.rules, ...pluginVue.configs["vue3-essential"].rules,
...pluginVue.configs.recommended.rules, ...pluginVue.configs["vue3-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,23 +1,15 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" /> <meta name="renderer" content="webkit" />
<meta name="viewport" <meta
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0" /> name="viewport"
content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=0"
/>
<title>vue-pure-admin</title> <title>vue-pure-admin</title>
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<script>
var _hmt = _hmt || [];
(function () {
var hm = document.createElement("script")
hm.src = "https://hm.baidu.com/hm.js?d3824e75ef2bf6d851035f74ae1aec68"
var s = document.getElementsByTagName("script")[0]
s.parentNode.insertBefore(hm, s)
})();
</script>
</head> </head>
<body> <body>
@@ -74,7 +66,6 @@
} }
@keyframes load-animation { @keyframes load-animation {
0%, 0%,
80%, 80%,
100% { 100% {
@@ -90,5 +81,4 @@
</div> </div>
<script type="module" src="/src/main.ts"></script> <script type="module" src="/src/main.ts"></script>
</body> </body>
</html> </html>

View File

@@ -80,9 +80,9 @@ menus:
pureMarkdown: Markdown pureMarkdown: Markdown
pureEditor: Editor pureEditor: Editor
pureAbnormal: Abnormal Page pureAbnormal: Abnormal Page
purePageNotFound: "404" pureFourZeroFour: "404"
pureAccessDenied: "403" pureFourZeroOne: "403"
pureServerError: "500" pureFive: "500"
pureComponents: Components pureComponents: Components
pureDialog: Dialog pureDialog: Dialog
pureDrawer: Drawer pureDrawer: Drawer

View File

@@ -80,9 +80,9 @@ menus:
pureMarkdown: Markdown pureMarkdown: Markdown
pureEditor: 编辑器 pureEditor: 编辑器
pureAbnormal: 异常页面 pureAbnormal: 异常页面
purePageNotFound: "404" pureFourZeroFour: "404"
pureAccessDenied: "403" pureFourZeroOne: "403"
pureServerError: "500" pureFive: "500"
pureComponents: 组件 pureComponents: 组件
pureDialog: 函数式弹框 pureDialog: 函数式弹框
pureDrawer: 函数式抽屉 pureDrawer: 函数式抽屉

View File

@@ -25,7 +25,7 @@ export default defineFakeRoute([
url: "/mine-logs", url: "/mine-logs",
method: "get", method: "get",
response: () => { response: () => {
const list = [ let list = [
{ {
id: 1, id: 1,
ip: faker.internet.ipv4(), ip: faker.internet.ipv4(),

View File

@@ -1,6 +1,6 @@
{ {
"name": "vue-pure-admin", "name": "vue-pure-admin",
"version": "6.2.0", "version": "5.9.0",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {
@@ -48,141 +48,142 @@
}, },
"dependencies": { "dependencies": {
"@amap/amap-jsapi-loader": "^1.0.1", "@amap/amap-jsapi-loader": "^1.0.1",
"@howdyjs/mouse-menu": "^2.1.7", "@howdyjs/mouse-menu": "^2.1.6",
"@infectoone/vue-ganttastic": "^2.3.2", "@infectoone/vue-ganttastic": "^2.3.2",
"@logicflow/core": "^1.2.28", "@logicflow/core": "^1.2.28",
"@logicflow/extension": "^1.2.28", "@logicflow/extension": "^1.2.28",
"@pureadmin/descriptions": "^1.2.1", "@pureadmin/descriptions": "^1.2.1",
"@pureadmin/table": "^3.3.0", "@pureadmin/table": "^3.2.1",
"@pureadmin/utils": "^2.6.2", "@pureadmin/utils": "^2.6.0",
"@vue-flow/background": "^1.3.2", "@vue-flow/background": "^1.3.2",
"@vue-flow/core": "^1.47.0", "@vue-flow/core": "^1.42.1",
"@vueuse/core": "^13.9.0", "@vueuse/core": "^12.5.0",
"@vueuse/motion": "^3.0.3", "@vueuse/motion": "^2.2.6",
"@wangeditor/editor": "^5.1.23", "@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12", "@wangeditor/editor-for-vue": "^5.1.12",
"@zxcvbn-ts/core": "^3.0.4", "@zxcvbn-ts/core": "^3.0.4",
"animate.css": "^4.1.1", "animate.css": "^4.1.1",
"axios": "^1.12.2", "axios": "^1.7.9",
"china-area-data": "^5.0.1", "china-area-data": "^5.0.1",
"codemirror": "^5.65.20", "codemirror": "^5.65.18",
"codemirror-editor-vue3": "^2.8.0", "codemirror-editor-vue3": "^2.8.0",
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
"dayjs": "^1.11.18", "dayjs": "^1.11.13",
"deep-chat": "^2.2.2", "deep-chat": "^2.1.1",
"echarts": "^6.0.0", "echarts": "^5.6.0",
"el-table-infinite-scroll": "^3.0.7", "el-table-infinite-scroll": "^3.0.6",
"element-plus": "^2.11.7", "element-plus": "^2.9.4",
"highlight.js": "^11.11.1", "highlight.js": "^11.11.1",
"intro.js": "^7.2.0", "intro.js": "^7.2.0",
"js-cookie": "^3.0.5", "js-cookie": "^3.0.5",
"jsbarcode": "^3.12.1", "jsbarcode": "^3.11.6",
"localforage": "^1.10.0", "localforage": "^1.10.0",
"mint-filter": "^4.0.3", "mint-filter": "^4.0.3",
"mitt": "^3.0.1", "mitt": "^3.0.1",
"mqtt": "4.3.7", "mqtt": "4.3.7",
"nprogress": "^0.2.0", "nprogress": "^0.2.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"pinia": "^3.0.3", "pinia": "^3.0.1",
"pinyin-pro": "^3.27.0", "pinyin-pro": "^3.26.0",
"plus-pro-components": "^0.1.29", "plus-pro-components": "^0.1.20",
"qrcode": "^1.5.4", "qrcode": "^1.5.4",
"qs": "^6.14.0", "qs": "^6.14.0",
"responsive-storage": "^2.2.0", "responsive-storage": "^2.2.0",
"sortablejs": "^1.15.6", "sortablejs": "^1.15.6",
"swiper": "^12.0.2", "swiper": "^11.2.3",
"typeit": "^8.8.7", "typeit": "^8.8.7",
"v-contextmenu": "^3.2.0", "v-contextmenu": "^3.2.0",
"v3-infinite-loading": "^1.3.2", "v3-infinite-loading": "^1.3.2",
"vditor": "^3.11.2", "vditor": "^3.10.9",
"version-rocket": "^1.7.4", "version-rocket": "^1.7.4",
"vue": "^3.5.22", "vue": "^3.5.13",
"vue-i18n": "^11.1.12", "vue-i18n": "^11.1.1",
"vue-json-pretty": "^2.5.0", "vue-json-pretty": "^2.4.0",
"vue-pdf-embed": "^2.1.3", "vue-pdf-embed": "^2.1.2",
"vue-router": "^4.5.1", "vue-router": "^4.5.0",
"vue-tippy": "^6.7.1", "vue-tippy": "^6.6.0",
"vue-types": "^6.0.0", "vue-types": "^5.1.3",
"vue-virtual-scroller": "2.0.0-beta.8", "vue-virtual-scroller": "2.0.0-beta.8",
"vue-waterfall-plugin-next": "^2.6.9", "vue-waterfall-plugin-next": "^2.6.5",
"vue3-danmaku": "^1.6.6", "vue3-danmaku": "^1.6.1",
"vue3-puzzle-vcode": "^1.1.7", "vue3-puzzle-vcode": "^1.1.7",
"vuedraggable": "^4.1.0", "vuedraggable": "^4.1.0",
"vxe-table": "4.6.25", "vxe-table": "4.6.25",
"wavesurfer.js": "^7.11.0", "wavesurfer.js": "^7.9.1",
"xgplayer": "^3.0.23", "xgplayer": "^3.0.20",
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "^20.1.0", "@commitlint/cli": "^19.7.1",
"@commitlint/config-conventional": "^20.0.0", "@commitlint/config-conventional": "^19.7.1",
"@commitlint/types": "^20.0.0", "@commitlint/types": "^19.5.0",
"@eslint/js": "^9.37.0", "@eslint/js": "^9.20.0",
"@faker-js/faker": "^10.0.0", "@faker-js/faker": "^9.5.0",
"@iconify/json": "^2.2.393", "@iconify/json": "^2.2.319",
"@iconify/vue": "4.2.0", "@iconify/vue": "4.2.0",
"@intlify/unplugin-vue-i18n": "^11.0.1", "@intlify/unplugin-vue-i18n": "^6.0.3",
"@tailwindcss/vite": "^4.1.14", "@types/codemirror": "^5.60.15",
"@types/codemirror": "^5.60.16", "@types/dagre": "^0.7.52",
"@types/dagre": "^0.7.53",
"@types/intro.js": "^5.1.5", "@types/intro.js": "^5.1.5",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/node": "^20.19.19", "@types/node": "^20.17.19",
"@types/nprogress": "^0.2.3", "@types/nprogress": "^0.2.3",
"@types/path-browserify": "^1.0.3", "@types/path-browserify": "^1.0.3",
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"@types/qs": "^6.14.0", "@types/qs": "^6.9.18",
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@vitejs/plugin-vue": "^6.0.1", "@typescript-eslint/eslint-plugin": "^8.24.0",
"@vitejs/plugin-vue-jsx": "^5.1.1", "@typescript-eslint/parser": "^8.24.0",
"@vitejs/plugin-vue": "^5.2.1",
"@vitejs/plugin-vue-jsx": "^4.1.1",
"autoprefixer": "^10.4.20",
"boxen": "^8.0.1", "boxen": "^8.0.1",
"code-inspector-plugin": "^1.2.10", "code-inspector-plugin": "^0.20.0",
"cssnano": "^7.1.1", "cssnano": "^7.0.6",
"dagre": "^0.8.5", "dagre": "^0.8.5",
"eslint": "^9.37.0", "eslint": "^9.20.1",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.5.4", "eslint-define-config": "^2.1.0",
"eslint-plugin-vue": "^10.5.0", "eslint-plugin-prettier": "^5.2.3",
"eslint-plugin-vue": "^9.32.0",
"gradient-string": "^3.0.0", "gradient-string": "^3.0.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"lint-staged": "^16.2.3", "lint-staged": "^15.4.3",
"postcss": "^8.5.6", "postcss": "^8.5.2",
"postcss-html": "^1.8.0", "postcss-html": "^1.8.0",
"postcss-load-config": "^6.0.1", "postcss-import": "^16.1.0",
"postcss-scss": "^4.0.9", "postcss-scss": "^4.0.9",
"prettier": "^3.6.2", "prettier": "^3.5.1",
"rimraf": "^6.0.1", "rimraf": "^6.0.1",
"rollup-plugin-visualizer": "^6.0.4", "rollup-plugin-visualizer": "^5.14.0",
"sass": "^1.93.2", "sass": "^1.85.0",
"stylelint": "^16.25.0", "stylelint": "^16.14.1",
"stylelint-config-recess-order": "^7.3.0", "stylelint-config-recess-order": "^6.0.0",
"stylelint-config-recommended-vue": "^1.6.1", "stylelint-config-recommended-vue": "^1.6.0",
"stylelint-config-standard-scss": "^14.0.0", "stylelint-config-standard-scss": "^14.0.0",
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^4.0.0", "svgo": "^3.3.2",
"tailwindcss": "^4.1.14", "tailwindcss": "3.4.17",
"typescript": "^5.9.3", "typescript": "^5.7.3",
"typescript-eslint": "^8.46.0", "unplugin-icons": "^22.1.0",
"unplugin-icons": "^22.4.2", "vite": "^6.1.0",
"vite": "^7.1.9",
"vite-plugin-cdn-import": "^1.0.1", "vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-compression": "^0.5.1", "vite-plugin-compression": "^0.5.1",
"vite-plugin-fake-server": "^2.2.0", "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-svg-loader": "^5.1.0", "vite-svg-loader": "^5.1.0",
"vue-eslint-parser": "^10.2.0", "vue-eslint-parser": "^9.4.3",
"vue-tsc": "^3.1.1" "vue-tsc": "^2.2.0"
}, },
"engines": { "engines": {
"node": "^20.19.0 || >=22.13.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": "*",
@@ -193,6 +194,11 @@
"abab": "*", "abab": "*",
"glob": "*" "glob": "*"
}, },
"peerDependencyRules": {
"allowedVersions": {
"eslint": "9"
}
},
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"@parcel/watcher", "@parcel/watcher",
"core-js", "core-js",
@@ -200,10 +206,6 @@
"esbuild", "esbuild",
"typeit", "typeit",
"vue-demi" "vue-demi"
],
"ignoredBuiltDependencies": [
"@tailwindcss/oxide",
"vue3-danmaku"
] ]
} }
} }

6084
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,10 @@
/** @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": "6.2.0", "Version": "5.9.0",
"Title": "PureAdmin", "Title": "PureAdmin",
"FixedHeader": true, "FixedHeader": true,
"HiddenSideBar": false, "HiddenSideBar": false,

View File

@@ -16,7 +16,6 @@ import en from "element-plus/es/locale/lang/en";
import zhCn from "element-plus/es/locale/lang/zh-cn"; import zhCn from "element-plus/es/locale/lang/zh-cn";
import plusEn from "plus-pro-components/es/locale/lang/en"; import plusEn from "plus-pro-components/es/locale/lang/en";
import plusZhCn from "plus-pro-components/es/locale/lang/zh-cn"; import plusZhCn from "plus-pro-components/es/locale/lang/zh-cn";
import { ElNotification } from "element-plus";
export default defineComponent({ export default defineComponent({
name: "app", name: "app",
@@ -55,31 +54,5 @@ export default defineComponent({
); );
} }
} }
// mounted() {
// ElNotification({
// title: "平台最新活动与动态",
// duration: 0,
// customClass: "fullpage-notification",
// // @ts-expect-error
// style: { width: "260px" },
// position: "bottom-right",
// dangerouslyUseHTMLString: true,
// message: `
// <a target='_blank' class='block text-base text-center border mt-4 rounded hover:text-[red]!' href='https://pure-admin.cn/pages/service/#%E6%9C%80%E6%96%B0%E6%B4%BB%E5%8A%A8%E4%B8%8E%E5%8A%A8%E6%80%81'>
// 点我查看
// </a>
// `
// });
// }
}); });
</script> </script>
<style>
.fullpage-notification > .el-notification__group > .el-notification__closeBtn {
top: 15px;
}
.fullpage-notification > div > h2 {
color: red;
font-size: 18px;
}
</style>

File diff suppressed because one or more lines are too long

View File

@@ -95,7 +95,7 @@ function onMouseleave() {
:view-style="{ overflow: 'hidden' }" :view-style="{ overflow: 'hidden' }"
class="border-t border-[#e5e7eb]" class="border-t border-[#e5e7eb]"
> >
<ul class="flex flex-wrap justify-around mb-1!"> <ul class="flex flex-wrap justify-around mb-1">
<li <li
v-for="(animate, index) in animatesList" v-for="(animate, index) in animatesList"
:key="index" :key="index"

View File

@@ -119,7 +119,7 @@ export default defineComponent({
"p-[6px]", "p-[6px]",
"h-[30px]", "h-[30px]",
"w-[30px]", "w-[30px]",
"outline-hidden", "outline-none",
"rounded-[4px]", "rounded-[4px]",
"cursor-pointer", "cursor-pointer",
"hover:bg-[rgba(0,0,0,0.06)]" "hover:bg-[rgba(0,0,0,0.06)]"

View File

@@ -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

@@ -35,20 +35,8 @@ type DialogProps = {
top?: string; top?: string;
/** 是否需要遮罩层,默认 `true` */ /** 是否需要遮罩层,默认 `true` */
modal?: boolean; modal?: boolean;
/** 是否允许穿透遮罩层,默认 `false`。使用时需将 `modal` 属性设置为 `false` */
modalPenetrable?: boolean;
/** 遮罩的自定义类名 */
modalClass?: string;
/** `header` 部分的自定义 `class` 名 */
headerClass?: string;
/** `body` 部分的自定义 `class` 名 */
bodyClass?: string;
/** `footer` 部分的自定义 `class` 名 */
footerClass?: string;
/** `Dialog` 自身是否插入至 `body` 元素上。嵌套的 `Dialog` 必须指定该属性并赋值为 `true`,默认 `false` */ /** `Dialog` 自身是否插入至 `body` 元素上。嵌套的 `Dialog` 必须指定该属性并赋值为 `true`,默认 `false` */
appendToBody?: boolean; appendToBody?: boolean;
/** `Dialog` 挂载到哪个 `DOM` 元素,该属性会覆盖 `append-to-body` 属性,默认 `body` */
appendTo?: string | HTMLElement;
/** 是否在 `Dialog` 出现时将 `body` 滚动锁定,默认 `true` */ /** 是否在 `Dialog` 出现时将 `body` 滚动锁定,默认 `true` */
lockScroll?: boolean; lockScroll?: boolean;
/** `Dialog` 的自定义类名 */ /** `Dialog` 的自定义类名 */
@@ -69,22 +57,12 @@ type DialogProps = {
beforeClose?: (done: DoneFn) => void; beforeClose?: (done: DoneFn) => void;
/** 为 `Dialog` 启用可拖拽功能,默认 `false` */ /** 为 `Dialog` 启用可拖拽功能,默认 `false` */
draggable?: boolean; draggable?: boolean;
/** 拖动范围可以超出可视区,默认 `false` */
overflow?: boolean;
/** 是否让 `Dialog` 的 `header` 和 `footer` 部分居中排列,默认 `false` */ /** 是否让 `Dialog` 的 `header` 和 `footer` 部分居中排列,默认 `false` */
center?: boolean; center?: boolean;
/** 是否水平垂直对齐对话框,默认 `false` */ /** 是否水平垂直对齐对话框,默认 `false` */
alignCenter?: boolean; alignCenter?: boolean;
/** 当关闭 `Dialog` 时,销毁其中的元素,默认 `false` */ /** 当关闭 `Dialog` 时,销毁其中的元素,默认 `false` */
destroyOnClose?: boolean; destroyOnClose?: boolean;
/** 自定义关闭图标,默认 `Close` */
closeIcon?: string | Component;
/** 和原生的 `CSS` 的 `z-index` 相同,改变 `z` 轴的顺序 */
zIndex?: number;
/** `header` 的 `aria-level` 属性 */
headerAriaLevel?: string;
/** 对话框动画的自定义过渡配置。可以是一个字符串(过渡名称),也可以是一个包含 `Vue` 过渡属性的对象,默认 `dialog-fade` */
transition?: string | object;
}; };
//element-plus.org/zh-CN/component/popconfirm.html#attributes //element-plus.org/zh-CN/component/popconfirm.html#attributes

View File

@@ -6,6 +6,7 @@ type ArgsType = {
/** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了 `esc` 键 */ /** `cancel` 点击取消按钮、`sure` 点击确定按钮、`close` 点击右上角关闭按钮或空白页或按下了 `esc` 键 */
command: "cancel" | "sure" | "close"; command: "cancel" | "sure" | "close";
}; };
type ButtonType = type ButtonType =
| "primary" | "primary"
| "success" | "success"
@@ -19,11 +20,11 @@ type DrawerProps = {
visible?: boolean; visible?: boolean;
/** `Drawer` 自身是否插入至 `body` 元素上。嵌套的 `Drawer` 必须指定该属性并赋值为 `true`,默认 `false` */ /** `Drawer` 自身是否插入至 `body` 元素上。嵌套的 `Drawer` 必须指定该属性并赋值为 `true`,默认 `false` */
appendToBody?: boolean; appendToBody?: boolean;
/** 挂载到哪个 `DOM` 元素,会覆盖 `appendToBody` 属性,默认 `body` */ /** 挂载到哪个 `DOM` 元素覆盖 `appendToBody` */
appendTo?: string; appendTo?: string;
/** 是否在 `Drawer` 出现时将 `body` 滚动锁定,默认 `true` */ /** 是否在 `Drawer` 出现时将 `body` 滚动锁定,默认 `true` */
lockScroll?: boolean; lockScroll?: boolean;
/** 关闭前的回调,会暂停 `Drawer` 的关闭回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */ /** 关闭前的回调,会暂停 `Drawer` 的关闭 回调函数内执行 `done` 参数方法的时候才是真正关闭对话框的时候 */
beforeClose?: (done: DoneFn) => void; beforeClose?: (done: DoneFn) => void;
/** 是否可以通过点击 `modal` 关闭 `Drawer` ,默认 `true` */ /** 是否可以通过点击 `modal` 关闭 `Drawer` ,默认 `true` */
closeOnClickModal?: boolean; closeOnClickModal?: boolean;
@@ -43,13 +44,9 @@ type DrawerProps = {
destroyOnClose?: boolean; destroyOnClose?: boolean;
/** 是否需要遮罩层,默认 `true` */ /** 是否需要遮罩层,默认 `true` */
modal?: boolean; modal?: boolean;
/** 是否允许穿透遮罩层,默认 `false`。使用时需将 `modal` 属性设置为 `false` */
modalPenetrable?: boolean;
/** `Drawer` 打开的方向,默认 `rtl` */ /** `Drawer` 打开的方向,默认 `rtl` */
direction?: "rtl" | "ltr" | "ttb" | "btt"; direction?: "rtl" | "ltr" | "ttb" | "btt";
/** 是否启用可调整大小的功能,默认 `false` */ /** `Drawer` 窗体的大小, 当使用 `number` 类型时, 以像素为单位, 当使用 `string` 类型时, 请传入 `'x%'`, 否则便会以 `number` 类型解释 */
resizable?: boolean;
/** `Drawer` 窗体的大小, 当使用 `number` 类型时, 以像素为单位, 当使用 `string` 类型时, 请传入 `'x%'`, 否则便会以 `number` 类型解释,默认 `30%` */
size?: string | number; size?: string | number;
/** `Drawer` 的标题 */ /** `Drawer` 的标题 */
title?: string; title?: string;
@@ -57,12 +54,6 @@ type DrawerProps = {
withHeader?: boolean; withHeader?: boolean;
/** 遮罩层的自定义类名 */ /** 遮罩层的自定义类名 */
modalClass?: string; modalClass?: string;
/** `header` 部分的自定义 `class` 名 */
headerClass?: string;
/** `body` 部分的自定义 `class` 名 */
bodyClass?: string;
/** `footer` 部分的自定义 `class` 名 */
footerClass?: string;
/** 设置 `z-index` */ /** 设置 `z-index` */
zIndex?: number; zIndex?: number;
/** `header` 的 `aria-level` 属性,默认 `2` */ /** `header` 的 `aria-level` 属性,默认 `2` */

View File

@@ -3,7 +3,7 @@ import { ref, unref, onMounted } from "vue";
import { LogicFlow } from "@logicflow/core"; import { LogicFlow } from "@logicflow/core";
interface Props { interface Props {
lf?: LogicFlow; lf: LogicFlow;
catTurboData?: boolean; catTurboData?: boolean;
} }
@@ -114,8 +114,7 @@ onMounted(() => {
:style="{ :style="{
cursor: item.disabled === false ? 'pointer' : 'not-allowed', cursor: item.disabled === false ? 'pointer' : 'not-allowed',
color: item.disabled === false ? '' : '#00000040', color: item.disabled === false ? '' : '#00000040',
background: 'transparent', background: 'transparent'
border: 'none'
}" }"
@click="onControl(item, key)" @click="onControl(item, key)"
> >

View File

@@ -9,8 +9,8 @@ type nodeListType = {
}; };
interface Props { interface Props {
lf?: LogicFlow; lf: LogicFlow;
nodeList?: Array<nodeListType>; nodeList: Array<nodeListType>;
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {

View File

@@ -23,9 +23,9 @@
function h() { function h() {
n || ((n = !0), o()); n || ((n = !0), o());
} }
((t = function () { (t = function () {
var c, t, e, o; var c, t, e, o;
(((o = document.createElement("div")).innerHTML = i), ((o = document.createElement("div")).innerHTML = i),
(i = null), (i = null),
(e = o.getElementsByTagName("svg")[0]) && (e = o.getElementsByTagName("svg")[0]) &&
((e.style.position = "absolute"), ((e.style.position = "absolute"),
@@ -35,13 +35,13 @@
(c = e), (c = e),
(t = document.body).firstChild (t = document.body).firstChild
? ((o = c), (e = t.firstChild).parentNode.insertBefore(o, e)) ? ((o = c), (e = t.firstChild).parentNode.insertBefore(o, e))
: t.appendChild(c))); : t.appendChild(c));
}), }),
document.addEventListener document.addEventListener
? ~["complete", "loaded", "interactive"].indexOf(document.readyState) ? ~["complete", "loaded", "interactive"].indexOf(document.readyState)
? setTimeout(t, 0) ? setTimeout(t, 0)
: ((e = function () { : ((e = function () {
(document.removeEventListener("DOMContentLoaded", e, !1), t()); document.removeEventListener("DOMContentLoaded", e, !1), t();
}), }),
document.addEventListener("DOMContentLoaded", e, !1)) document.addEventListener("DOMContentLoaded", e, !1))
: document.attachEvent && : document.attachEvent &&
@@ -58,5 +58,5 @@
})(), })(),
(a.onreadystatechange = function () { (a.onreadystatechange = function () {
"complete" == a.readyState && ((a.onreadystatechange = null), h()); "complete" == a.readyState && ((a.onreadystatechange = null), h());
}))); }));
})(window); })(window);

View File

@@ -158,7 +158,7 @@ watch(
:name="pane.name" :name="pane.name"
> >
<el-scrollbar height="220px"> <el-scrollbar height="220px">
<ul class="flex flex-wrap px-2! ml-2!"> <ul class="flex flex-wrap px-2 ml-2">
<li <li
v-for="(item, key) in pageList" v-for="(item, key) in pageList"
:key="key" :key="key"
@@ -198,7 +198,7 @@ watch(
@current-change="onCurrentChange" @current-change="onCurrentChange"
/> />
<el-button <el-button
class="justify-end mx-2!" class="justify-end mr-2 ml-2"
type="danger" type="danger"
size="small" size="small"
text text

View File

@@ -49,10 +49,8 @@ export function useRenderIcon(icon: any, attrs?: iconType): Component {
return defineComponent({ return defineComponent({
name: "Icon", name: "Icon",
render() { render() {
if (!icon) return; const IconifyIcon =
const IconifyIcon = icon.includes(":") icon && icon.includes(":") ? IconifyIconOnline : IconifyIconOffline;
? IconifyIconOnline
: IconifyIconOffline;
return h(IconifyIcon, { return h(IconifyIcon, {
icon, icon,
...attrs ...attrs

View File

@@ -54,8 +54,8 @@ const icons = [
// Remix Icon: https://github.com/Remix-Design/RemixIcon // Remix Icon: https://github.com/Remix-Design/RemixIcon
["ri/mind-map", RiMindMap], ["ri/mind-map", RiMindMap],
["ri/admin-fill", RiAdminFill], ["ri/admin-fill", RiAdminFill],
["ri/table-line", RiTableLine], ["ri/links-fill", RiTableLine],
["ri/links-fill", RiLinksFill], ["ri/table-line", RiLinksFill],
["ri/admin-line", RiAdminLine], ["ri/admin-line", RiAdminLine],
["ri/list-check", RiListCheck], ["ri/list-check", RiListCheck],
["ri/search-line", RiSearchLine], ["ri/search-line", RiSearchLine],

View File

@@ -87,9 +87,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-hidden" "outline-none"
]; ];
}); });
@@ -100,7 +100,7 @@ export default defineComponent({
"pt-[3px]", "pt-[3px]",
"px-[11px]", "px-[11px]",
"border-b-[1px]", "border-b-[1px]",
"border-b-solid", "border-solid",
"border-[#dcdfe6]", "border-[#dcdfe6]",
"dark:border-[#303030]" "dark:border-[#303030]"
]; ];
@@ -255,12 +255,12 @@ export default defineComponent({
<div <div
{...attrs} {...attrs}
class={[ class={[
"w-full", "w-[99/100]",
"px-2", "px-2",
"pb-2", "pb-2",
"bg-bg_color", "bg-bg_color",
isFullscreen.value isFullscreen.value
? ["h-full!", "z-2002", "fixed", "inset-0"] ? ["!w-full", "!h-full", "z-[2002]", "fixed", "inset-0"]
: "mt-2" : "mt-2"
]} ]}
> >
@@ -317,7 +317,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}
@@ -347,8 +347,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;

View File

@@ -60,15 +60,12 @@ export function copyObj() {
copyIsArray, copyIsArray,
clone, clone,
i = 1, i = 1,
// eslint-disable-next-line prefer-rest-params
target = arguments[0] || {}, // 使用||运算符排除隐式强制类型转换为false的数据类型 target = arguments[0] || {}, // 使用||运算符排除隐式强制类型转换为false的数据类型
deep = false, deep = false,
// eslint-disable-next-line prefer-const
len = arguments.length; len = arguments.length;
if (typeof target === "boolean") { if (typeof target === "boolean") {
deep = target; deep = target;
// eslint-disable-next-line prefer-rest-params
target = arguments[1] || {}; target = arguments[1] || {};
i++; i++;
} }
@@ -82,7 +79,6 @@ export function copyObj() {
for (; i < len; i++) { for (; i < len; i++) {
//所以如果源对象中数据类型为Undefined或Null那么就会跳过本次循环接着循环下一个源对象 //所以如果源对象中数据类型为Undefined或Null那么就会跳过本次循环接着循环下一个源对象
// eslint-disable-next-line prefer-rest-params
if ((options = arguments[i]) != null) { if ((options = arguments[i]) != null) {
// 如果遇到源对象的数据类型为Boolean, Number for in循环会被跳过不执行for in循环// src用于判断target对象是否存在name属性 // 如果遇到源对象的数据类型为Boolean, Number for in循环会被跳过不执行for in循环// src用于判断target对象是否存在name属性
for (name in options) { for (name in options) {

View File

@@ -98,6 +98,7 @@
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

@@ -33,10 +33,10 @@
} }
l(); l();
} }
((t = function () { (t = function () {
var e, var e,
t = document.createElement("div"); t = document.createElement("div");
((t.innerHTML = i), (t.innerHTML = i),
(i = null), (i = null),
(t = t.getElementsByTagName("svg")[0]) && (t = t.getElementsByTagName("svg")[0]) &&
((t.style.position = "absolute"), ((t.style.position = "absolute"),
@@ -44,15 +44,13 @@
(t.style.height = 0), (t.style.height = 0),
(t.style.overflow = "hidden"), (t.style.overflow = "hidden"),
(t = t), (t = t),
(e = document.body).firstChild (e = document.body).firstChild ? m(t, e.firstChild) : e.appendChild(t));
? m(t, e.firstChild)
: e.appendChild(t)));
}), }),
document.addEventListener document.addEventListener
? ~["complete", "loaded", "interactive"].indexOf(document.readyState) ? ~["complete", "loaded", "interactive"].indexOf(document.readyState)
? setTimeout(t, 0) ? setTimeout(t, 0)
: ((n = function () { : ((n = function () {
(document.removeEventListener("DOMContentLoaded", n, !1), t()); document.removeEventListener("DOMContentLoaded", n, !1), t();
}), }),
document.addEventListener("DOMContentLoaded", n, !1)) document.addEventListener("DOMContentLoaded", n, !1))
: document.attachEvent && : document.attachEvent &&
@@ -62,5 +60,5 @@
a(), a(),
(o.onreadystatechange = function () { (o.onreadystatechange = function () {
"complete" == o.readyState && ((o.onreadystatechange = null), l()); "complete" == o.readyState && ((o.onreadystatechange = null), l());
}))); }));
})(window); })(window);

View File

@@ -80,9 +80,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-hidden" "outline-none"
]; ];
}); });
@@ -93,7 +93,7 @@ export default defineComponent({
"pt-[3px]", "pt-[3px]",
"px-[11px]", "px-[11px]",
"border-b-[1px]", "border-b-[1px]",
"border-b-solid", "border-solid",
"border-[#dcdfe6]", "border-[#dcdfe6]",
"dark:border-[#303030]" "dark:border-[#303030]"
]; ];
@@ -248,12 +248,12 @@ export default defineComponent({
<div <div
{...attrs} {...attrs}
class={[ class={[
"w-full", "w-[99/100]",
"px-2", "px-2",
"pb-2", "pb-2",
"bg-bg_color", "bg-bg_color",
isFullscreen.value isFullscreen.value
? ["h-full!", "z-2002", "fixed", "inset-0"] ? ["!w-full", "!h-full", "z-[2002]", "fixed", "inset-0"]
: "mt-2" : "mt-2"
]} ]}
> >
@@ -310,7 +310,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}
@@ -340,8 +340,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;

View File

@@ -13,10 +13,10 @@ $ripple-animation-visible-opacity: 0.25 !default;
z-index: 0; z-index: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
contain: strict;
overflow: hidden; overflow: hidden;
pointer-events: none; pointer-events: none;
border-radius: inherit; border-radius: inherit;
contain: strict;
} }
&__animation { &__animation {

View File

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

View File

@@ -8,31 +8,20 @@ const TITLE = getConfig("Title");
<footer <footer
class="layout-footer text-[rgba(0,0,0,0.6)] dark:text-[rgba(220,220,242,0.8)]" class="layout-footer text-[rgba(0,0,0,0.6)] dark:text-[rgba(220,220,242,0.8)]"
> >
Copyright © 2020-2025 Copyright © 2020-present
<a <a
class="hover:text-primary!" class="hover:text-primary"
href="https://github.com/pure-admin" href="https://github.com/pure-admin"
target="_blank" target="_blank"
> >
&nbsp;{{ TITLE }} &nbsp;{{ TITLE }}
</a> </a>
<!-- <div class="ml-8">
<span>赞助商</span>
<a
class="hover:text-primary"
href="https://ai-tools.cn/resume/start"
target="_blank"
>
二猫 AI 简历
</a>
</div> -->
</footer> </footer>
</template> </template>
<style lang="scss" scoped> <style lang="scss" scoped>
.layout-footer { .layout-footer {
display: flex; display: flex;
flex-wrap: wrap;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;

View File

@@ -33,7 +33,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
</script> </script>
<template> <template>
<div class="navbar bg-[#fff] shadow-xs shadow-[rgba(0,21,41,0.08)]"> <div class="navbar bg-[#fff] shadow-sm shadow-[rgba(0,21,41,0.08)]">
<LaySidebarTopCollapse <LaySidebarTopCollapse
v-if="device === 'mobile'" v-if="device === 'mobile'"
class="hamburger-container" class="hamburger-container"
@@ -54,13 +54,13 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<GlobalizationIcon <GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-hidden" class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/> />
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="translation"> <el-dropdown-menu class="translation">
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'zh')" :style="getDropdownItemStyle(locale, 'zh')"
:class="['dark:text-white!', getDropdownItemClass(locale, 'zh')]" :class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh" @click="translationCh"
> >
<IconifyIconOffline <IconifyIconOffline
@@ -72,7 +72,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'en')" :style="getDropdownItemStyle(locale, 'en')"
:class="['dark:text-white!', getDropdownItemClass(locale, 'en')]" :class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn" @click="translationEn"
> >
<span v-show="locale === 'en'" class="check-en"> <span v-show="locale === 'en'" class="check-en">

View File

@@ -49,7 +49,7 @@ function hoverDescription(event, description) {
<template> <template>
<div <div
class="notice-container border-0 border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]" class="notice-container border-b-[1px] border-solid border-[#f0f0f0] dark:border-[#303030]"
> >
<el-avatar <el-avatar
v-if="noticeItem.avatar" v-if="noticeItem.avatar"

View File

@@ -16,7 +16,7 @@ const iconClass = computed(() => {
"flex", "flex",
"justify-center", "justify-center",
"items-center", "items-center",
"outline-hidden", "outline-none",
"rounded-[4px]", "rounded-[4px]",
"cursor-pointer", "cursor-pointer",
"transition-colors", "transition-colors",
@@ -51,7 +51,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-0 border-b-[1px] border-solid border-[var(--pure-border-color)]" class="project-configuration border-b-[1px] border-solid border-[var(--pure-border-color)]"
> >
<h4 class="dark:text-white"> <h4 class="dark:text-white">
{{ t("panel.pureSystemSet") }} {{ t("panel.pureSystemSet") }}
@@ -78,7 +78,7 @@ onBeforeUnmount(() => {
</el-scrollbar> </el-scrollbar>
<div <div
class="flex justify-end p-3 border-0 border-t-[1px] border-solid border-[var(--pure-border-color)]" class="flex justify-end p-3 border-t-[1px] border-solid border-[var(--pure-border-color)]"
> >
<el-button <el-button
v-tippy="{ v-tippy="{

View File

@@ -6,7 +6,7 @@ import EnterOutlined from "@/assets/svg/enter_outlined.svg?component";
import ArrowUpLine from "~icons/ri/arrow-up-line"; import ArrowUpLine from "~icons/ri/arrow-up-line";
import ArrowDownLine from "~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

@@ -6,8 +6,8 @@ import { useRouter } from "vue-router";
import SearchResult from "./SearchResult.vue"; import SearchResult from "./SearchResult.vue";
import SearchFooter from "./SearchFooter.vue"; import SearchFooter from "./SearchFooter.vue";
import { useNav } from "@/layout/hooks/useNav"; import { useNav } from "@/layout/hooks/useNav";
import { transformI18n } from "@/plugins/i18n";
import SearchHistory from "./SearchHistory.vue"; import SearchHistory from "./SearchHistory.vue";
import { transformI18n, $t } from "@/plugins/i18n";
import type { optionsItem, dragItem } from "../types"; import type { optionsItem, dragItem } from "../types";
import { ref, computed, shallowRef, watch } from "vue"; import { ref, computed, shallowRef, watch } from "vue";
import { useDebounceFn, onKeyStroke } from "@vueuse/core"; import { useDebounceFn, onKeyStroke } from "@vueuse/core";

View File

@@ -20,8 +20,8 @@ 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 "~icons/ep/check"; import Check from "~icons/ep/check";
import LeftArrow from "~icons/ri/arrow-left-s-line?width=20&height=20"; import LeftArrow from "~icons/ri/arrow-left-s-line";
import RightArrow from "~icons/ri/arrow-right-s-line?width=20&height=20"; import RightArrow from "~icons/ri/arrow-right-s-line";
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";
@@ -189,7 +189,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>>(() => {
@@ -335,7 +335,7 @@ onUnmounted(() => removeMatchMedia);
" "
/> />
<p :class="['mt-5!', pClass]">{{ t("panel.pureThemeColor") }}</p> <p :class="['mt-5', pClass]">{{ t("panel.pureThemeColor") }}</p>
<ul class="theme-color"> <ul class="theme-color">
<li <li
v-for="(item, index) in themeColors" v-for="(item, index) in themeColors"
@@ -354,7 +354,7 @@ onUnmounted(() => removeMatchMedia);
</li> </li>
</ul> </ul>
<p :class="['mt-5!', pClass]">{{ t("panel.pureLayoutModel") }}</p> <p :class="['mt-5', pClass]">{{ t("panel.pureLayoutModel") }}</p>
<ul class="pure-theme"> <ul class="pure-theme">
<li <li
ref="verticalRef" ref="verticalRef"
@@ -397,7 +397,7 @@ onUnmounted(() => removeMatchMedia);
</ul> </ul>
<span v-if="useAppStoreHook().getViewportWidth > 1280"> <span v-if="useAppStoreHook().getViewportWidth > 1280">
<p :class="['mt-5!', pClass]">{{ t("panel.pureStretch") }}</p> <p :class="['mt-5', pClass]">{{ t("panel.pureStretch") }}</p>
<Segmented <Segmented
resize resize
class="mb-2 select-none" class="mb-2 select-none"
@@ -426,19 +426,21 @@ onUnmounted(() => removeMatchMedia);
> >
<IconifyIconOffline <IconifyIconOffline
:icon="settings.stretch ? RightArrow : LeftArrow" :icon="settings.stretch ? RightArrow : LeftArrow"
height="20"
/> />
<div <div
class="grow border-0 border-b border-dashed" class="flex-grow 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]">{{ t("panel.pureTagsStyle") }}</p> <p :class="['mt-4', pClass]">{{ t("panel.pureTagsStyle") }}</p>
<Segmented <Segmented
resize resize
class="select-none" class="select-none"
@@ -447,7 +449,7 @@ onUnmounted(() => removeMatchMedia);
@change="onChange" @change="onChange"
/> />
<p class="mt-5! font-medium text-sm dark:text-white"> <p class="mt-5 font-medium text-sm dark:text-white">
{{ t("panel.pureInterfaceDisplay") }} {{ t("panel.pureInterfaceDisplay") }}
</p> </p>
<ul class="setting"> <ul class="setting">

View File

@@ -84,13 +84,13 @@ onMounted(() => {
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<GlobalizationIcon <GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-hidden" class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/> />
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="translation"> <el-dropdown-menu class="translation">
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'zh')" :style="getDropdownItemStyle(locale, 'zh')"
:class="['dark:text-white!', getDropdownItemClass(locale, 'zh')]" :class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh" @click="translationCh"
> >
<span v-show="locale === 'zh'" class="check-zh"> <span v-show="locale === 'zh'" class="check-zh">
@@ -100,7 +100,7 @@ onMounted(() => {
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'en')" :style="getDropdownItemStyle(locale, 'en')"
:class="['dark:text-white!', getDropdownItemClass(locale, 'en')]" :class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn" @click="translationEn"
> >
<span v-show="locale === 'en'" class="check-en"> <span v-show="locale === 'en'" class="check-en">

View File

@@ -105,13 +105,13 @@ watch(
<!-- 国际化 --> <!-- 国际化 -->
<el-dropdown id="header-translation" trigger="click"> <el-dropdown id="header-translation" trigger="click">
<GlobalizationIcon <GlobalizationIcon
class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-hidden" class="navbar-bg-hover w-[40px] h-[48px] p-[11px] cursor-pointer outline-none"
/> />
<template #dropdown> <template #dropdown>
<el-dropdown-menu class="translation"> <el-dropdown-menu class="translation">
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'zh')" :style="getDropdownItemStyle(locale, 'zh')"
:class="['dark:text-white!', getDropdownItemClass(locale, 'zh')]" :class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh" @click="translationCh"
> >
<span v-show="locale === 'zh'" class="check-zh"> <span v-show="locale === 'zh'" class="check-zh">
@@ -121,7 +121,7 @@ watch(
</el-dropdown-item> </el-dropdown-item>
<el-dropdown-item <el-dropdown-item
:style="getDropdownItemStyle(locale, 'en')" :style="getDropdownItemStyle(locale, 'en')"
:class="['dark:text-white!', getDropdownItemClass(locale, 'en')]" :class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn" @click="translationEn"
> >
<span v-show="locale === 'en'" class="check-en"> <span v-show="locale === 'en'" class="check-en">

View File

@@ -105,12 +105,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)">
{{ transformI18n(item.meta.title) }} {{ transformI18n(item.meta.title) }}

View File

@@ -7,7 +7,7 @@ import { useNav } from "@/layout/hooks/useNav";
import ArrowLeft from "~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

@@ -61,21 +61,6 @@ 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 {
@@ -159,7 +144,7 @@ function resolvePath(routePath) {
item?.pathList?.length === 2) item?.pathList?.length === 2)
" "
truncated truncated
class="w-full! px-3! min-w-[54px]! text-center! text-inherit!" class="!w-full !pl-4 !text-inherit"
> >
{{ transformI18n(onlyOneChild.meta.title) }} {{ transformI18n(onlyOneChild.meta.title) }}
</el-text> </el-text>
@@ -171,7 +156,7 @@ function resolvePath(routePath) {
offset: [0, -10], offset: [0, -10],
theme: tooltipEffect theme: tooltipEffect
}" }"
class="w-full! text-inherit!" class="!w-full !text-inherit"
> >
{{ transformI18n(onlyOneChild.meta.title) }} {{ transformI18n(onlyOneChild.meta.title) }}
</ReText> </ReText>
@@ -210,7 +195,15 @@ function resolvePath(routePath) {
offset: [0, -10], offset: [0, -10],
theme: tooltipEffect theme: tooltipEffect
}" }"
:class="textClass" :class="{
'!w-full': true,
'!text-inherit': true,
'!pl-4':
layout !== 'horizontal' &&
isCollapse &&
!toRaw(item.meta.icon) &&
item.parentId === null
}"
> >
{{ transformI18n(item.meta.title) }} {{ transformI18n(item.meta.title) }}
</ReText> </ReText>

View File

@@ -7,7 +7,7 @@ import { useNav } from "@/layout/hooks/useNav";
import MenuFold from "~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>(), {
@@ -23,7 +23,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

@@ -4,7 +4,7 @@ import MenuFold from "~icons/ri/menu-fold-fill";
import MenuUnfold from "~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>(), {
@@ -32,7 +32,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

@@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { $t } from "@/plugins/i18n"; import { $t } from "@/plugins/i18n";
import { emitter } from "@/utils/mitt"; import { emitter } from "@/utils/mitt";
import NProgress from "@/utils/progress";
import { RouteConfigs } from "../../types"; import { RouteConfigs } from "../../types";
import { useTags } from "../../hooks/useTag"; import { useTags } from "../../hooks/useTag";
import { routerArrays } from "@/layout/types"; import { routerArrays } from "@/layout/types";
@@ -207,14 +206,12 @@ function dynamicRouteTag(value: string): void {
/** 刷新路由 */ /** 刷新路由 */
function onFresh() { function onFresh() {
NProgress.start();
const { fullPath, query } = unref(route); const { fullPath, query } = unref(route);
router.replace({ router.replace({
path: "/redirect" + fullPath, path: "/redirect" + fullPath,
query query
}); });
handleAliveRoute(route as ToRouteType, "refresh"); handleAliveRoute(route as ToRouteType, "refresh");
NProgress.done();
} }
function deleteDynamicTag(obj: any, current: any, tag?: string) { function deleteDynamicTag(obj: any, current: any, tag?: string) {
@@ -257,7 +254,7 @@ function deleteDynamicTag(obj: any, current: any, tag?: string) {
if (tag === "other") { if (tag === "other") {
spliceRoute(1, 1, true); spliceRoute(1, 1, true);
} else if (tag === "left") { } else if (tag === "left") {
spliceRoute(fixedTags.length, valueIndex - fixedTags.length); spliceRoute(fixedTags.length, valueIndex - 1, true);
} else if (tag === "right") { } else if (tag === "right") {
spliceRoute(valueIndex + 1, multiTags.value.length); spliceRoute(valueIndex + 1, multiTags.value.length);
} else { } else {
@@ -356,7 +353,7 @@ function onClickDrop(key, item, selectRoute?: RouteConfigs) {
break; break;
} }
setTimeout(() => { setTimeout(() => {
showMenuModel(route.fullPath, route.query, route.params); showMenuModel(route.fullPath, route.query);
}); });
} }
@@ -391,18 +388,15 @@ function disabledMenus(value: boolean, fixedTag = false) {
function showMenuModel( function showMenuModel(
currentPath: string, currentPath: string,
query: object = {}, query: object = {},
params: object = {},
refresh = false refresh = false
) { ) {
const allRoute = multiTags.value; const allRoute = multiTags.value;
const routeLength = multiTags.value.length; const routeLength = multiTags.value.length;
let currentIndex = -1; let currentIndex = -1;
if (!isAllEmpty(params)) { if (isAllEmpty(query)) {
currentIndex = allRoute.findIndex(v => isEqual(v.params, params));
} else if (!isAllEmpty(query)) {
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
} else {
currentIndex = allRoute.findIndex(v => v.path === currentPath); currentIndex = allRoute.findIndex(v => v.path === currentPath);
} else {
currentIndex = allRoute.findIndex(v => isEqual(v.query, query));
} }
function fixedTagDisabled() { function fixedTagDisabled() {
if (allRoute[currentIndex]?.meta?.fixedTag) { if (allRoute[currentIndex]?.meta?.fixedTag) {
@@ -468,14 +462,14 @@ function openMenu(tag, e) {
} else if (route.path !== tag.path && route.name !== tag.name) { } else if (route.path !== tag.path && route.name !== tag.name) {
// 右键菜单不匹配当前路由,隐藏刷新 // 右键菜单不匹配当前路由,隐藏刷新
tagsViews[0].show = false; tagsViews[0].show = false;
showMenuModel(tag.path, tag.query, tag.params); showMenuModel(tag.path, tag.query);
} else if (multiTags.value.length === 2 && route.path !== tag.path) { } else if (multiTags.value.length === 2 && route.path !== tag.path) {
showMenus(true); showMenus(true);
// 只有两个标签时不显示关闭其他标签页 // 只有两个标签时不显示关闭其他标签页
tagsViews[4].show = false; tagsViews[4].show = false;
showMenuModel(tag.path, tag.query, tag.params); } else if (route.path === tag.path) {
} else { // 右键当前激活的菜单
showMenuModel(tag.path, tag.query, tag.params, true); showMenuModel(tag.path, tag.query, true);
} }
currentSelect.value = tag; currentSelect.value = tag;
@@ -595,7 +589,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"
> >
{{ transformI18n(item.meta.title) }} {{ transformI18n(item.meta.title) }}
</span> </span>

View File

@@ -19,22 +19,13 @@ const loading = ref(true);
const currentRoute = useRoute(); const currentRoute = useRoute();
const frameSrc = ref<string>(""); const frameSrc = ref<string>("");
const frameRef = ref<HTMLElement | null>(null); const frameRef = ref<HTMLElement | null>(null);
const fallbackTimer = ref<number | null>(null);
if (unref(currentRoute.meta)?.frameSrc) { if (unref(currentRoute.meta)?.frameSrc) {
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string; frameSrc.value = unref(currentRoute.meta)?.frameSrc as string;
} }
unref(currentRoute.meta)?.frameLoading === false && hideLoading();
function clearFallbackTimer() {
if (fallbackTimer.value !== null) {
clearTimeout(fallbackTimer.value);
fallbackTimer.value = null;
}
}
function hideLoading() { function hideLoading() {
loading.value = false; loading.value = false;
clearFallbackTimer();
} }
function init() { function init() {
@@ -43,42 +34,32 @@ function init() {
if (!iframe) return; if (!iframe) return;
const _frame = iframe as any; const _frame = iframe as any;
if (_frame.attachEvent) { if (_frame.attachEvent) {
_frame.attachEvent("onload", hideLoading); _frame.attachEvent("onload", () => {
hideLoading();
});
} else { } else {
iframe.onload = hideLoading; iframe.onload = () => {
hideLoading();
};
} }
}); });
} }
let isRedirect = false;
watch( watch(
() => currentRoute.fullPath, () => currentRoute.fullPath,
path => { path => {
if ( if (
currentRoute.name === "Redirect" && currentRoute.name === "Redirect" &&
props.frameInfo?.fullPath && path.includes(props.frameInfo?.fullPath)
path.includes(props.frameInfo.fullPath)
) { ) {
isRedirect = true; frameSrc.value = path; // redirect时置换成任意值待重定向后 重新赋值
loading.value = true; loading.value = true;
return;
} }
if (props.frameInfo?.fullPath === path && isRedirect) { // 重新赋值
loading.value = true; if (props.frameInfo?.fullPath === path) {
clearFallbackTimer(); frameSrc.value = props.frameInfo?.frameSrc;
const url = new URL(props.frameInfo.frameSrc, window.location.origin);
const joinChar = url.search ? "&" : "?";
frameSrc.value = `${props.frameInfo.frameSrc}${joinChar}t=${Date.now()}`;
fallbackTimer.value = window.setTimeout(() => {
if (loading.value) {
hideLoading();
} }
}, 1500);
isRedirect = false;
} }
},
{ immediate: true }
); );
onMounted(() => { onMounted(() => {

View File

@@ -64,7 +64,7 @@ export function useNav() {
const getDropdownItemClass = computed(() => { const getDropdownItemClass = computed(() => {
return (locale, t) => { return (locale, t) => {
return locale === t ? "" : "dark:hover:text-primary!"; return locale === t ? "" : "dark:hover:!text-primary";
}; };
}); });

View File

@@ -114,21 +114,14 @@ export function useTags() {
]); ]);
function conditionHandle(item, previous, next) { function conditionHandle(item, previous, next) {
const currentName = route.name || "";
const itemName = item.name || "";
if (isBoolean(route?.meta?.showLink) && route?.meta?.showLink === false) { if (isBoolean(route?.meta?.showLink) && route?.meta?.showLink === false) {
if (Object.keys(route.query).length > 0) { if (Object.keys(route.query).length > 0) {
return currentName === itemName && isEqual(route.query, item.query) return isEqual(route.query, item.query) ? previous : next;
? previous
: next;
} else { } else {
return currentName === itemName && isEqual(route.params, item.params) return isEqual(route.params, item.params) ? previous : next;
? previous
: next;
} }
} else { } else {
return currentName === itemName ? previous : next; return route.path === item.path ? previous : next;
} }
} }

View File

@@ -6,7 +6,6 @@ export const routerArrays: Array<RouteConfigs> =
? [ ? [
{ {
path: "/welcome", path: "/welcome",
name: "Welcome",
meta: { meta: {
title: "menus.pureHome", title: "menus.pureHome",
icon: "ep/home-filled" icon: "ep/home-filled"

View File

@@ -10,7 +10,7 @@ import zhLocale from "element-plus/es/locale/lang/zh-cn";
const siphonI18n = (function () { const siphonI18n = (function () {
// 仅初始化一次国际化配置 // 仅初始化一次国际化配置
const cache = Object.fromEntries( let cache = Object.fromEntries(
Object.entries( Object.entries(
import.meta.glob("../../locales/*.y(a)?ml", { eager: true }) import.meta.glob("../../locales/*.y(a)?ml", { eager: true })
).map(([key, value]: any) => { ).map(([key, value]: any) => {

View File

@@ -7,13 +7,7 @@ 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 { import { isUrl, openLink, storageLocal, isAllEmpty } from "@pureadmin/utils";
isUrl,
openLink,
cloneDeep,
isAllEmpty,
storageLocal
} from "@pureadmin/utils";
import { import {
ascending, ascending,
getTopMenu, getTopMenu,
@@ -27,9 +21,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,
@@ -61,9 +55,6 @@ 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)
@@ -94,25 +85,20 @@ export const router: Router = createRouter({
} }
}); });
/** 记录已经加载的页面路径 */
const loadedPaths = new Set<string>();
/** 重置已加载页面记录 */
export function resetLoadedPaths() {
loadedPaths.clear();
}
/** 重置路由 */ /** 重置路由 */
export function resetRouter() { export function resetRouter() {
router.clearRoutes(); router.getRoutes().forEach(route => {
for (const route of initConstantRoutes.concat(...(remainingRouter as any))) { const { name, meta } = route;
router.addRoute(route); if (name && router.hasRoute(name) && meta?.backstage) {
} router.removeRoute(name);
router.options.routes = formatTwoStageRoutes( router.options.routes = formatTwoStageRoutes(
formatFlatteningRoutes(buildHierarchyTree(ascending(routes.flat(Infinity)))) formatFlatteningRoutes(
buildHierarchyTree(ascending(routes.flat(Infinity)))
)
); );
}
});
usePermissionStoreHook().clearAllCachePage(); usePermissionStoreHook().clearAllCachePage();
resetLoadedPaths();
} }
/** 路由白名单 */ /** 路由白名单 */
@@ -121,12 +107,6 @@ const whiteList = ["/login"];
const { VITE_HIDE_HOME } = import.meta.env; const { VITE_HIDE_HOME } = import.meta.env;
router.beforeEach((to: ToRouteType, _from, next) => { router.beforeEach((to: ToRouteType, _from, next) => {
to.meta.loaded = loadedPaths.has(to.path);
if (!to.meta.loaded) {
NProgress.start();
}
if (to.meta?.keepAlive) { if (to.meta?.keepAlive) {
handleAliveRoute(to, "add"); handleAliveRoute(to, "add");
// 页面整体刷新和点击标签页刷新 // 页面整体刷新和点击标签页刷新
@@ -135,6 +115,7 @@ router.beforeEach((to: ToRouteType, _from, next) => {
} }
} }
const userInfo = storageLocal().getItem<DataInfo<number>>(userKey); const userInfo = storageLocal().getItem<DataInfo<number>>(userKey);
NProgress.start();
const externalLink = isUrl(to?.name as string); const externalLink = isUrl(to?.name as string);
if (!externalLink) { if (!externalLink) {
to.matched.some(item => { to.matched.some(item => {
@@ -220,8 +201,7 @@ router.beforeEach((to: ToRouteType, _from, next) => {
} }
}); });
router.afterEach(to => { router.afterEach(() => {
loadedPaths.add(to.path);
NProgress.done(); NProgress.done();
}); });

View File

@@ -16,7 +16,7 @@ export default {
name: "403", name: "403",
component: () => import("@/views/error/403.vue"), component: () => import("@/views/error/403.vue"),
meta: { meta: {
title: $t("menus.pureAccessDenied") title: $t("menus.pureFourZeroOne")
} }
}, },
{ {
@@ -24,7 +24,7 @@ export default {
name: "404", name: "404",
component: () => import("@/views/error/404.vue"), component: () => import("@/views/error/404.vue"),
meta: { meta: {
title: $t("menus.purePageNotFound") title: $t("menus.pureFourZeroFour")
} }
}, },
{ {
@@ -32,7 +32,7 @@ export default {
name: "500", name: "500",
component: () => import("@/views/error/500.vue"), component: () => import("@/views/error/500.vue"),
meta: { meta: {
title: $t("menus.pureServerError") title: $t("menus.pureFive")
} }
} }
] ]

View File

@@ -8,27 +8,8 @@ export default [
component: () => import("@/views/login/index.vue"), component: () => import("@/views/login/index.vue"),
meta: { meta: {
title: $t("menus.pureLogin"), title: $t("menus.pureLogin"),
showLink: false showLink: false,
} rank: 101
},
// 全屏403无权访问页面
{
path: "/access-denied",
name: "AccessDenied",
component: () => import("@/views/error/403.vue"),
meta: {
title: $t("menus.pureAccessDenied"),
showLink: false
}
},
// 全屏500服务器出错页面
{
path: "/server-error",
name: "ServerError",
component: () => import("@/views/error/500.vue"),
meta: {
title: $t("menus.pureServerError"),
showLink: false
} }
}, },
{ {
@@ -36,7 +17,8 @@ export default [
component: Layout, component: Layout,
meta: { meta: {
title: $t("status.pureLoad"), title: $t("status.pureLoad"),
showLink: false showLink: false,
rank: 102
}, },
children: [ children: [
{ {
@@ -46,15 +28,6 @@ export default [
} }
] ]
}, },
{
path: "/account-settings",
name: "AccountSettings",
component: () => import("@/views/account-settings/index.vue"),
meta: {
title: $t("buttons.pureAccountSettings"),
showLink: false
}
},
// 下面是一个无layout菜单的例子一个全屏空白页面因为这种情况极少发生所以只需要在前端配置即可配置路径src/router/modules/remaining.ts // 下面是一个无layout菜单的例子一个全屏空白页面因为这种情况极少发生所以只需要在前端配置即可配置路径src/router/modules/remaining.ts
{ {
path: "/empty", path: "/empty",
@@ -62,7 +35,18 @@ export default [
component: () => import("@/views/empty/index.vue"), component: () => import("@/views/empty/index.vue"),
meta: { meta: {
title: $t("menus.pureEmpty"), title: $t("menus.pureEmpty"),
showLink: false showLink: false,
rank: 103
}
},
{
path: "/account-settings",
name: "AccountSettings",
component: () => import("@/views/account-settings/index.vue"),
meta: {
title: $t("buttons.pureAccountSettings"),
showLink: false,
rank: 104
} }
} }
] satisfies Array<RouteConfigsTable>; ] satisfies Array<RouteConfigsTable>;

View File

@@ -139,17 +139,12 @@ function findRouteByPath(path: string, routes: RouteRecordRaw[]) {
} }
} }
/** 动态路由注册完成后再添加全屏404页面不存在页面避免刷新动态路由页面时误跳转到404页面 */
function addPathMatch() { function addPathMatch() {
if (!router.hasRoute("pathMatch")) { if (!router.hasRoute("pathMatch")) {
router.addRoute({ router.addRoute({
path: "/:pathMatch(.*)*", path: "/:pathMatch(.*)",
name: "PageNotFound", name: "pathMatch",
component: () => import("@/views/error/404.vue"), redirect: "/error/404"
meta: {
title: "menus.purePageNotFound",
showLink: false
}
}); });
} }
} }
@@ -177,8 +172,6 @@ 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

@@ -80,15 +80,22 @@ export const useMultiTagsStore = defineStore("pure-multiTags", {
if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink) if (isBoolean(tagVal?.meta?.showLink) && !tagVal?.meta?.showLink)
return; return;
const tagPath = tagVal.path; const tagPath = tagVal.path;
// 判断tag是否已存在
const tagHasExits = this.multiTags.some(tag => { const tagHasExits = this.multiTags.some(tag => {
return ( return tag.path === tagPath;
tag.path === tagPath &&
isEqual(tag?.query, tagVal?.query) &&
isEqual(tag?.params, tagVal?.params)
);
}); });
if (tagHasExits) return; // 判断tag中的query键值是否相等
const tagQueryHasExits = this.multiTags.some(tag => {
return isEqual(tag?.query, tagVal?.query);
});
// 判断tag中的params键值是否相等
const tagParamsHasExits = this.multiTags.some(tag => {
return isEqual(tag?.params, tagVal?.params);
});
if (tagHasExits && tagQueryHasExits && tagParamsHasExits) return;
// 动态路由可打开的最大数量 // 动态路由可打开的最大数量
const dynamicLevel = tagVal?.meta?.dynamicLevel ?? -1; const dynamicLevel = tagVal?.meta?.dynamicLevel ?? -1;

View File

@@ -2,6 +2,7 @@ import { defineStore } from "pinia";
import { import {
type cacheType, type cacheType,
store, store,
debounce,
ascending, ascending,
getKeyList, getKeyList,
filterTree, filterTree,
@@ -32,8 +33,21 @@ export const usePermissionStore = defineStore("pure-permission", {
this.constantMenus.concat(routes) as any this.constantMenus.concat(routes) as any
); );
}, },
cacheOperate({ mode, name }: cacheType) {
const delIndex = this.cachePageList.findIndex(v => v === name);
switch (mode) {
case "refresh":
this.cachePageList = this.cachePageList.filter(v => v !== name);
break;
case "add":
this.cachePageList.push(name);
break;
case "delete":
delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
break;
}
/** 监听缓存页面是否存在于标签页,不存在则删除 */ /** 监听缓存页面是否存在于标签页,不存在则删除 */
clearCache() { debounce(() => {
let cacheLength = this.cachePageList.length; let cacheLength = this.cachePageList.length;
const nameList = getKeyList(useMultiTagsStoreHook().multiTags, "name"); const nameList = getKeyList(useMultiTagsStoreHook().multiTags, "name");
while (cacheLength > 0) { while (cacheLength > 0) {
@@ -45,22 +59,7 @@ export const usePermissionStore = defineStore("pure-permission", {
); );
cacheLength--; cacheLength--;
} }
}, })();
cacheOperate({ mode, name }: cacheType) {
const delIndex = this.cachePageList.findIndex(v => v === name);
switch (mode) {
case "refresh":
this.cachePageList = this.cachePageList.filter(v => v !== name);
this.clearCache();
break;
case "add":
this.cachePageList.push(name);
break;
case "delete":
delIndex !== -1 && this.cachePageList.splice(delIndex, 1);
this.clearCache();
break;
}
}, },
/** 清空缓存页面 */ /** 清空缓存页面 */
clearAllCachePage() { clearAllCachePage() {

View File

@@ -1,3 +1,12 @@
*,
::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%;

View File

@@ -172,6 +172,10 @@
.is-active > .el-sub-menu__title, .is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown { .is-active.submenu-title-noDropdown {
color: var(--pure-theme-sub-menu-active-text) !important; color: var(--pure-theme-sub-menu-active-text) !important;
i {
color: var(--pure-theme-sub-menu-active-text) !important;
}
} }
.is-active { .is-active {
@@ -231,7 +235,6 @@
.is-active.submenu-title-noDropdown.outer-most > * { .is-active.submenu-title-noDropdown.outer-most > * {
z-index: 1; z-index: 1;
color: #fff; color: #fff;
background: transparent !important;
} }
.is-active.submenu-title-noDropdown.outer-most::before { .is-active.submenu-title-noDropdown.outer-most::before {
@@ -267,6 +270,10 @@
.is-active > .el-sub-menu__title, .is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown { .is-active.submenu-title-noDropdown {
color: var(--pure-theme-sub-menu-active-text) !important; color: var(--pure-theme-sub-menu-active-text) !important;
i {
color: var(--pure-theme-sub-menu-active-text) !important;
}
} }
/* 子菜单中还有子菜单 */ /* 子菜单中还有子菜单 */
@@ -367,6 +374,10 @@
.is-active > .el-sub-menu__title, .is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown { .is-active.submenu-title-noDropdown {
color: var(--pure-theme-sub-menu-active-text) !important; color: var(--pure-theme-sub-menu-active-text) !important;
i {
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,
@@ -530,6 +541,10 @@
.is-active > .el-sub-menu__title, .is-active > .el-sub-menu__title,
.is-active.submenu-title-noDropdown { .is-active.submenu-title-noDropdown {
color: var(--pure-theme-sub-menu-active-text) !important; color: var(--pure-theme-sub-menu-active-text) !important;
i {
color: var(--pure-theme-sub-menu-active-text) !important;
}
} }
.is-active { .is-active {
@@ -651,10 +666,6 @@ 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,7 +687,6 @@ 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 {

View File

@@ -1,46 +1,21 @@
@layer theme, base, components, utilities; @tailwind base;
@import "tailwindcss/theme.css" layer(theme); @tailwind components;
@import "tailwindcss/utilities.css" layer(utilities); @tailwind utilities;
@custom-variant dark (&:is(.dark *)); @layer components {
.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;
} }
@utility flex-ac { .flex-ac {
@apply flex justify-around items-center; @apply flex justify-around items-center;
} }
@utility flex-bc { .flex-bc {
@apply flex justify-between items-center; @apply flex justify-between items-center;
} }
@utility navbar-bg-hover { .navbar-bg-hover {
@apply dark:text-white dark:hover:bg-[#242424]!; @apply dark:text-white dark:hover:!bg-[#242424];
}
} }

View File

@@ -10,6 +10,7 @@ import type {
PureHttpRequestConfig PureHttpRequestConfig
} from "./types.d"; } from "./types.d";
import { stringify } from "qs"; import { stringify } from "qs";
import NProgress from "../progress";
import { getToken, formatToken } from "@/utils/auth"; import { getToken, formatToken } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
@@ -60,6 +61,8 @@ class PureHttp {
private httpInterceptorsRequest(): void { private httpInterceptorsRequest(): void {
PureHttp.axiosInstance.interceptors.request.use( PureHttp.axiosInstance.interceptors.request.use(
async (config: PureHttpRequestConfig): Promise<any> => { async (config: PureHttpRequestConfig): Promise<any> => {
// 开启进度条动画
NProgress.start();
// 优先判断post/get等方法是否传入回调否则执行初始化设置等回调 // 优先判断post/get等方法是否传入回调否则执行初始化设置等回调
if (typeof config.beforeRequestCallback === "function") { if (typeof config.beforeRequestCallback === "function") {
config.beforeRequestCallback(config); config.beforeRequestCallback(config);
@@ -118,6 +121,8 @@ class PureHttp {
instance.interceptors.response.use( instance.interceptors.response.use(
(response: PureHttpResponse) => { (response: PureHttpResponse) => {
const $config = response.config; const $config = response.config;
// 关闭进度条动画
NProgress.done();
// 优先判断post/get等方法是否传入回调否则执行初始化设置等回调 // 优先判断post/get等方法是否传入回调否则执行初始化设置等回调
if (typeof $config.beforeResponseCallback === "function") { if (typeof $config.beforeResponseCallback === "function") {
$config.beforeResponseCallback(response); $config.beforeResponseCallback(response);
@@ -132,6 +137,8 @@ class PureHttp {
(error: PureHttpError) => { (error: PureHttpError) => {
const $error = error; const $error = error;
$error.isCancelRequest = Axios.isCancel($error); $error.isCancelRequest = Axios.isCancel($error);
// 关闭进度条动画
NProgress.done();
// 所有的响应异常 区分来源为取消请求/非取消请求 // 所有的响应异常 区分来源为取消请求/非取消请求
return Promise.reject($error); return Promise.reject($error);
} }

View File

@@ -8,8 +8,6 @@ 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` */
@@ -20,14 +18,14 @@ interface MessageParams {
duration?: number; duration?: number;
/** 是否显示关闭按钮,默认值 `false` */ /** 是否显示关闭按钮,默认值 `false` */
showClose?: boolean; showClose?: boolean;
/** `Message` 距离窗口顶部的偏移量,默认 `16` */ /** 文字是否居中,默认 `false` */
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;
} }
@@ -50,30 +48,28 @@ 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,
offset = 16, center = false,
offset = 20,
appendTo = document.body, appendTo = document.body,
grouping = false, grouping = false,
repeatNum = 1,
onClose onClose
} = params; } = params;
return ElMessage({ return ElMessage({
message, message,
icon,
type, type,
plain, icon,
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

@@ -140,10 +140,8 @@ 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);

View File

@@ -76,7 +76,7 @@ function onReset() {
immediate: true, immediate: true,
timeout: 1000 timeout: 1000
}" }"
class="w-[200px]!" class="!w-[200px]"
clearable clearable
@clear="onInput" @clear="onInput"
/> />
@@ -86,7 +86,7 @@ function onReset() {
<el-input <el-input
v-model="searchTwo" v-model="searchTwo"
v-optimize="{ event: 'input', fn: onInputTwo, timeout: 400 }" v-optimize="{ event: 'input', fn: onInputTwo, timeout: 400 }"
class="w-[200px]!" class="!w-[200px]"
clearable clearable
/> />
</div> </div>
@@ -100,7 +100,7 @@ function onReset() {
timeout: 400, timeout: 400,
params: { name: '小明', sex: '男' } params: { name: '小明', sex: '男' }
}" }"
class="w-[200px]!" class="!w-[200px]"
clearable clearable
/> />
</div> </div>
@@ -112,7 +112,7 @@ function onReset() {
<el-input <el-input
v-model="searchFour" v-model="searchFour"
v-optimize:throttle="{ event: 'input', fn: onInputFour, timeout: 1000 }" v-optimize:throttle="{ event: 'input', fn: onInputFour, timeout: 1000 }"
class="w-[200px]!" class="!w-[200px]"
clearable clearable
/> />
</div> </div>
@@ -125,7 +125,7 @@ function onReset() {
fn: onInputFive, fn: onInputFive,
params: { name: '小明', sex: '男' } params: { name: '小明', sex: '男' }
}" }"
class="w-[200px]!" class="!w-[200px]"
clearable clearable
/> />
</div> </div>
@@ -134,7 +134,7 @@ function onReset() {
<div class="mb-2"> <div class="mb-2">
文本复制指令(双击输入框内容即可复制) 文本复制指令(双击输入框内容即可复制)
<el-input v-model="searchSix" v-copy="searchSix" class="w-[200px]!" /> <el-input v-model="searchSix" v-copy="searchSix" class="!w-[200px]" />
</div> </div>
<div> <div>
文本复制指令(自定义触发事件,单击复制) 文本复制指令(自定义触发事件,单击复制)

View File

@@ -51,9 +51,7 @@ const load = () => {
代码位置 src/views/able/infinite-scroll.vue 代码位置 src/views/able/infinite-scroll.vue
</el-link> </el-link>
</template> </template>
<div class="mb-2"> <p class="mb-2">{{ isBottom ? "已加载全部页" : `加载到第 ${page}` }}</p>
{{ isBottom ? "已加载全部页" : `加载到第 ${page}` }}
</div>
<el-table <el-table
v-el-table-infinite-scroll="load" v-el-table-infinite-scroll="load"
border border

View File

@@ -194,7 +194,7 @@ onUnmounted(() => {
基于 基于
<el-link <el-link
type="primary" type="primary"
underline="never" :underline="false"
href="https://github.com/mqttjs/MQTT.js" href="https://github.com/mqttjs/MQTT.js"
target="_blank" target="_blank"
> >
@@ -203,7 +203,7 @@ onUnmounted(() => {
免费的公共MQTT代理 免费的公共MQTT代理
<el-link <el-link
type="primary" type="primary"
underline="never" :underline="false"
href="broker.emqx.io" href="broker.emqx.io"
target="_blank" target="_blank"
> >

View File

@@ -21,8 +21,8 @@ defineOptions({
</div> </div>
</template> </template>
<p v-html="html('带 音 调')" /> <p v-html="html('带 音 调')" />
<p class="mt-2!" v-html="html('不 带 音 调', { toneType: 'none' })" /> <p class="mt-2" v-html="html('不 带 音 调', { toneType: 'none' })" />
<p class="mt-2! custom" v-html="html('自 定 义 样 式')" /> <p class="mt-2 custom" v-html="html('自 定 义 样 式')" />
</el-card> </el-card>
</template> </template>

View File

@@ -83,7 +83,7 @@ const tableData: User[] = [
<span class="font-medium">打印功能报表图表图片</span> <span class="font-medium">打印功能报表图表图片</span>
<el-select <el-select
v-model="value" v-model="value"
class="w-[100px]! mr-2" class="!w-[100px] mr-2"
placeholder="Select" placeholder="Select"
size="small" size="small"
> >

View File

@@ -45,6 +45,6 @@ function onInput() {
</el-tag> </el-tag>
</div> </div>
<el-input v-model="modelValue" @input="onInput" /> <el-input v-model="modelValue" @input="onInput" />
<p class="mt-2!">{{ modelValue }}</p> <p class="mt-2">{{ modelValue }}</p>
</el-card> </el-card>
</template> </template>

View File

@@ -53,7 +53,7 @@ onBeforeUnmount(() => {
代码位置 src/views/able/watermark.vue 代码位置 src/views/able/watermark.vue
</el-link> </el-link>
</template> </template>
<el-space wrap class="mb-2!"> <el-space wrap class="!mb-2">
<span> 请输入要创建水印的值</span> <span> 请输入要创建水印的值</span>
<el-input v-model="value" class="mr-4" style="width: 200px" clearable /> <el-input v-model="value" class="mr-4" style="width: 200px" clearable />
<span>请选择要创建水印的颜色</span> <span>请选择要创建水印的颜色</span>

View File

@@ -103,13 +103,13 @@ onBeforeUnmount(() => {
</template> </template>
<div <div
v-loading="loading" v-loading="loading"
class="w-8/12 m-auto! mt-[20px]!" class="w-8/12 !m-auto !mt-[20px]"
element-loading-background="transparent" element-loading-background="transparent"
> >
<div ref="wavesurferRef" /> <div ref="wavesurferRef" />
<div v-show="totalTime" class="flex justify-between"> <div v-show="totalTime" class="flex justify-between">
<span class="text-[#81888f]">00:00</span> <span class="text-[#81888f]">00:00</span>
<h1 class="text-4xl mt-2!">{{ curTime }}</h1> <h1 class="text-4xl mt-2">{{ curTime }}</h1>
<span class="text-[#81888f]">{{ totalTime }}</span> <span class="text-[#81888f]">{{ totalTime }}</span>
</div> </div>
<div v-show="totalTime" class="flex mt-2 w-[180px] justify-around m-auto"> <div v-show="totalTime" class="flex mt-2 w-[180px] justify-around m-auto">

View File

@@ -7,7 +7,7 @@ export function useColumns() {
minWidth: 100, minWidth: 100,
cellRenderer: () => { cellRenderer: () => {
return ( return (
<el-tag size="large" class="text-base!"> <el-tag size="large" class="!text-base">
{version} {version}
</el-tag> </el-tag>
); );
@@ -18,7 +18,7 @@ export function useColumns() {
minWidth: 120, minWidth: 120,
cellRenderer: () => { cellRenderer: () => {
return ( return (
<el-tag size="large" class="text-base!"> <el-tag size="large" class="!text-base">
{lastBuildTime} {lastBuildTime}
</el-tag> </el-tag>
); );
@@ -29,7 +29,7 @@ export function useColumns() {
minWidth: 140, minWidth: 140,
cellRenderer: () => { cellRenderer: () => {
return ( return (
<el-tag size="large" class="text-base!"> <el-tag size="large" class="!text-base">
{engines.node} {engines.node}
</el-tag> </el-tag>
); );
@@ -40,7 +40,7 @@ export function useColumns() {
minWidth: 140, minWidth: 140,
cellRenderer: () => { cellRenderer: () => {
return ( return (
<el-tag size="large" class="text-base!"> <el-tag size="large" class="!text-base">
{engines.pnpm} {engines.pnpm}
</el-tag> </el-tag>
); );

View File

@@ -43,7 +43,7 @@ function onClick(item) {
deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]' deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
]" ]"
> >
<h3 class="my-8!">账户管理</h3> <h3 class="my-8">账户管理</h3>
<div v-for="(item, index) in list" :key="index"> <div v-for="(item, index) in list" :key="index">
<div class="flex items-center"> <div class="flex items-center">
<div class="flex-1"> <div class="flex-1">

View File

@@ -38,7 +38,7 @@ function onChange(val, item) {
deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]' deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
]" ]"
> >
<h3 class="my-8!">偏好设置</h3> <h3 class="my-8">偏好设置</h3>
<div v-for="(item, index) in list" :key="index"> <div v-for="(item, index) in list" :key="index">
<div class="flex items-center"> <div class="flex items-center">
<div class="flex-1"> <div class="flex-1">

View File

@@ -110,7 +110,7 @@ getMine().then(res => {
deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]' deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
]" ]"
> >
<h3 class="my-8!">个人信息</h3> <h3 class="my-8">个人信息</h3>
<el-form <el-form
ref="userInfoFormRef" ref="userInfoFormRef"
label-position="top" label-position="top"
@@ -128,7 +128,7 @@ getMine().then(res => {
:show-file-list="false" :show-file-list="false"
:on-change="onChange" :on-change="onChange"
> >
<el-button plain class="ml-4!"> <el-button plain class="ml-4">
<IconifyIconOffline :icon="uploadLine" /> <IconifyIconOffline :icon="uploadLine" />
<span class="ml-2">更新头像</span> <span class="ml-2">更新头像</span>
</el-button> </el-button>

View File

@@ -78,7 +78,7 @@ onMounted(() => {
deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]' deviceDetection() ? 'max-w-[100%]' : 'max-w-[70%]'
]" ]"
> >
<h3 class="my-8!">安全日志</h3> <h3 class="my-8">安全日志</h3>
<pure-table <pure-table
row-key="id" row-key="id"
table-layout="auto" table-layout="auto"

View File

@@ -70,28 +70,26 @@ getMine().then(res => {
<el-container class="h-full"> <el-container class="h-full">
<el-aside <el-aside
v-if="isOpen" v-if="isOpen"
class="pure-account-settings overflow-hidden px-2 dark:bg-(--el-bg-color)! border-r-[1px] border-[var(--pure-border-color)]" class="pure-account-settings overflow-hidden px-2 dark:!bg-[var(--el-bg-color)] border-r-[1px] border-[var(--pure-border-color)]"
:width="deviceDetection() ? '180px' : '240px'" :width="deviceDetection() ? '180px' : '240px'"
> >
<el-menu :default-active="witchPane" class="pure-account-settings-menu"> <el-menu :default-active="witchPane" class="pure-account-settings-menu">
<div <el-menu-item
class="h-[50px]! text-[var(--pure-theme-menu-text)] cursor-pointer text-sm transition-all duration-300 ease-in-out hover:scale-105 will-change-transform transform-gpu origin-center hover:text-base! hover:text-[var(--pure-theme-menu-title-hover)]!" class="hover:!transition-all hover:!duration-200 hover:!text-base !h-[50px]"
@click="router.go(-1)" @click="router.go(-1)"
> >
<div <div class="flex items-center">
class="h-full flex items-center px-[var(--el-menu-base-level-padding)]"
>
<IconifyIconOffline :icon="leftLine" /> <IconifyIconOffline :icon="leftLine" />
<span class="ml-2">返回</span> <span class="ml-2">返回</span>
</div> </div>
</div> </el-menu-item>
<div class="flex items-center ml-8 mt-4 mb-4"> <div class="flex items-center ml-8 mt-4 mb-4">
<el-avatar :size="48" :src="userInfo.avatar" /> <el-avatar :size="48" :src="userInfo.avatar" />
<div class="ml-4 flex flex-col max-w-[130px]"> <div class="ml-4 flex flex-col max-w-[130px]">
<ReText class="font-bold self-baseline!"> <ReText class="font-bold !self-baseline">
{{ userInfo.nickname }} {{ userInfo.nickname }}
</ReText> </ReText>
<ReText class="self-baseline!" type="info"> <ReText class="!self-baseline" type="info">
{{ userInfo.username }} {{ userInfo.username }}
</ReText> </ReText>
</div> </div>

View File

@@ -36,6 +36,6 @@ watch(animate, () => {
代码位置 src/views/components/animatecss.vue 代码位置 src/views/components/animatecss.vue
</el-link> </el-link>
</template> </template>
<ReAnimateSelector v-model="animate" class="w-[200px]!" /> <ReAnimateSelector v-model="animate" class="!w-[200px]" />
</el-card> </el-card>
</template> </template>

View File

@@ -109,7 +109,7 @@ watch(size, val =>
代码位置 src/views/components/check-button.vue 代码位置 src/views/components/check-button.vue
</el-link> </el-link>
</template> </template>
<div class="mb-2">单选紧凑风格的按钮样式</div> <p class="mb-2">单选紧凑风格的按钮样式</p>
<el-radio-group <el-radio-group
v-model="radio" v-model="radio"
:size="dynamicSize" :size="dynamicSize"
@@ -121,7 +121,7 @@ watch(size, val =>
</el-radio-group> </el-radio-group>
<el-divider /> <el-divider />
<div class="mb-2">单选带有边框</div> <p class="mb-2">单选带有边框</p>
<el-radio-group <el-radio-group
v-model="radioBox" v-model="radioBox"
:size="dynamicSize" :size="dynamicSize"
@@ -133,7 +133,7 @@ watch(size, val =>
</el-radio-group> </el-radio-group>
<el-divider /> <el-divider />
<div class="mb-2">单选自定义内容</div> <p class="mb-2">单选自定义内容</p>
<el-radio-group <el-radio-group
v-model="radioCustom" v-model="radioCustom"
:size="dynamicSize" :size="dynamicSize"
@@ -160,7 +160,7 @@ watch(size, val =>
</el-radio-group> </el-radio-group>
<el-divider /> <el-divider />
<div class="mb-2">多选紧凑风格的按钮样式</div> <p class="mb-2">多选紧凑风格的按钮样式</p>
<el-checkbox-group <el-checkbox-group
v-model="checkboxGroup" v-model="checkboxGroup"
:size="dynamicSize" :size="dynamicSize"
@@ -172,7 +172,7 @@ watch(size, val =>
</el-checkbox-group> </el-checkbox-group>
<el-divider /> <el-divider />
<div class="mb-2">多选带有边框</div> <p class="mb-2">多选带有边框</p>
<el-checkbox-group <el-checkbox-group
v-model="checkboxGroupBox" v-model="checkboxGroupBox"
:size="dynamicSize" :size="dynamicSize"
@@ -184,7 +184,7 @@ watch(size, val =>
</el-checkbox-group> </el-checkbox-group>
<el-divider /> <el-divider />
<div class="mb-2">多选来点不一样的体验</div> <p class="mb-2">多选来点不一样的体验</p>
<el-checkbox-group <el-checkbox-group
v-model="checkboxGroupCustom" v-model="checkboxGroupCustom"
class="pure-checkbox" class="pure-checkbox"
@@ -218,14 +218,14 @@ watch(size, val =>
</el-checkbox-group> </el-checkbox-group>
<el-divider /> <el-divider />
<div>可控制间距的按钮样式</div> <p>可控制间距的按钮样式</p>
<el-slider <el-slider
v-model="spaceSize" v-model="spaceSize"
class="mb-2 w-[300px]!" class="mb-2 !w-[300px]"
:show-tooltip="false" :show-tooltip="false"
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
<div class="mb-2">单选</div> <p class="mb-2">单选</p>
<el-space wrap :size="spaceSize"> <el-space wrap :size="spaceSize">
<el-check-tag <el-check-tag
v-for="(tag, index) in checkTag" v-for="(tag, index) in checkTag"
@@ -241,7 +241,7 @@ watch(size, val =>
{{ tag.title }} {{ tag.title }}
</el-check-tag> </el-check-tag>
</el-space> </el-space>
<div class="mb-2 mt-4"> <p class="mb-2 mt-4">
多选 多选
{{ {{
getKeyList( getKeyList(
@@ -249,7 +249,7 @@ watch(size, val =>
"title" "title"
) )
}} }}
</div> </p>
<el-space wrap :size="spaceSize"> <el-space wrap :size="spaceSize">
<el-check-tag <el-check-tag
v-for="(tag, index) in checkGroupTag" v-for="(tag, index) in checkGroupTag"
@@ -267,7 +267,7 @@ watch(size, val =>
</el-space> </el-space>
<el-divider /> <el-divider />
<div class="mb-2">单个可选按钮</div> <p class="mb-2">单个可选按钮</p>
<el-check-tag <el-check-tag
:class="[ :class="[
'select-none', 'select-none',

View File

@@ -69,7 +69,7 @@ watch(size, val =>
</el-link> </el-link>
</template> </template>
<div class="mb-2 mt-4">单选</div> <p class="mb-2 mt-4">单选</p>
<PlusCheckCardGroup <PlusCheckCardGroup
v-model="list" v-model="list"
:options="options" :options="options"
@@ -77,7 +77,7 @@ watch(size, val =>
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
<div class="mb-2 mt-4">多选</div> <p class="mb-2 mt-4">多选</p>
<PlusCheckCardGroup <PlusCheckCardGroup
v-model="list1" v-model="list1"
:options="options" :options="options"

View File

@@ -50,12 +50,12 @@ const handleChange = (val: string[]) => {
</el-link> </el-link>
</template> </template>
<div class="mb-2">基础用法</div> <p class="mb-2">基础用法</p>
<el-radio-group v-model="radio" class="mb-3"> <el-radio-group v-model="radio" class="mb-3">
<el-radio value="">可同时展开多个面板</el-radio> <el-radio value="">可同时展开多个面板</el-radio>
<el-radio value="accordion">每次只能展开一个面板</el-radio> <el-radio value="accordion">每次只能展开一个面板</el-radio>
</el-radio-group> </el-radio-group>
<el-button size="small" text bg class="ml-8! mb-1!" @click="onClick"> <el-button size="small" text bg class="ml-8 mb-1" @click="onClick">
外部触发打开、关闭 外部触发打开、关闭
</el-button> </el-button>
<el-collapse <el-collapse

View File

@@ -73,7 +73,7 @@ function onClick() {
</el-link> </el-link>
</template> </template>
<div class="mb-2">不同尺寸、选择透明度、预定义颜色</div> <p class="mb-2">不同尺寸、选择透明度、预定义颜色</p>
<el-color-picker <el-color-picker
v-model="color" v-model="color"
show-alpha show-alpha
@@ -83,7 +83,7 @@ function onClick() {
/> />
<el-divider /> <el-divider />
<div class="mb-2">外部触发器</div> <p class="mb-2">外部触发器</p>
<el-space wrap> <el-space wrap>
<el-color-picker <el-color-picker
ref="colorPickerRef" ref="colorPickerRef"

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<div class="mb-2">基础用法</div> <p class="mb-2">基础用法</p>
<div v-contextmenu:contextmenu class="wrapper"> <div v-contextmenu:contextmenu class="wrapper">
<code>右键点击此区域</code> <code>右键点击此区域</code>
</div> </div>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<div class="mb-2">动态菜单</div> <p class="mb-2">动态菜单</p>
<div v-contextmenu:contextmenu class="wrapper"> <div v-contextmenu:contextmenu class="wrapper">
<code>右键点击此区域</code> <code>右键点击此区域</code>
</div> </div>

View File

@@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<div class="mb-2">按钮组</div> <p class="mb-2">按钮组</p>
<div v-contextmenu:contextmenu class="wrapper"> <div v-contextmenu:contextmenu class="wrapper">
<code>右键点击此区域</code> <code>右键点击此区域</code>
</div> </div>

View File

@@ -160,11 +160,11 @@ watch(size, val =>
</el-link> </el-link>
</template> </template>
<div class="mb-2">选择某一天</div> <p class="mb-2">选择某一天</p>
<el-date-picker <el-date-picker
v-model="value" v-model="value"
type="date" type="date"
class="w-[160px]!" class="!w-[160px]"
placeholder="请选择" placeholder="请选择"
:disabled-date="disabledDate" :disabled-date="disabledDate"
:shortcuts="shortcuts" :shortcuts="shortcuts"
@@ -175,12 +175,12 @@ watch(size, val =>
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
<div class="mb-2 mt-4">选择周、月、年或多个日期</div> <p class="mb-2 mt-4">选择周、月、年或多个日期</p>
<el-space wrap> <el-space wrap>
<el-date-picker <el-date-picker
v-model="value1" v-model="value1"
type="week" type="week"
class="w-[160px]!" class="!w-[160px]"
format="YYYY年第ww周" format="YYYY年第ww周"
placeholder="选择某年中的某周" placeholder="选择某年中的某周"
:size="dynamicSize" :size="dynamicSize"
@@ -189,7 +189,7 @@ watch(size, val =>
<el-date-picker <el-date-picker
v-model="value2" v-model="value2"
type="month" type="month"
class="w-[160px]!" class="!w-[160px]"
placeholder="选择某月" placeholder="选择某月"
:size="dynamicSize" :size="dynamicSize"
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
@@ -197,7 +197,7 @@ watch(size, val =>
<el-date-picker <el-date-picker
v-model="value3" v-model="value3"
type="year" type="year"
class="w-[160px]!" class="!w-[160px]"
placeholder="选择某年" placeholder="选择某年"
:size="dynamicSize" :size="dynamicSize"
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
@@ -205,18 +205,18 @@ watch(size, val =>
<el-date-picker <el-date-picker
v-model="value4" v-model="value4"
type="dates" type="dates"
class="w-[160px]!" class="!w-[160px]"
placeholder="选择多个日期" placeholder="选择多个日期"
:size="dynamicSize" :size="dynamicSize"
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
</el-space> </el-space>
<div class="mb-2 mt-4">选择一段时间</div> <p class="mb-2 mt-4">选择一段时间</p>
<el-date-picker <el-date-picker
v-model="value5" v-model="value5"
type="daterange" type="daterange"
class="w-[240px]!" class="!w-[240px]"
unlink-panels unlink-panels
range-separator="至" range-separator="至"
start-placeholder="开始时间" start-placeholder="开始时间"
@@ -229,7 +229,7 @@ watch(size, val =>
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
<div class="mb-2 mt-4">选择月份范围</div> <p class="mb-2 mt-4">选择月份范围</p>
<el-date-picker <el-date-picker
v-model="value6" v-model="value6"
type="monthrange" type="monthrange"
@@ -245,7 +245,7 @@ watch(size, val =>
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
<div class="mb-2 mt-4">日期格式</div> <p class="mb-2 mt-4">日期格式</p>
<el-radio-group <el-radio-group
v-model="dateFormat" v-model="dateFormat"
class="mb-2" class="mb-2"
@@ -261,7 +261,7 @@ watch(size, val =>
<el-date-picker <el-date-picker
v-model="value7" v-model="value7"
type="date" type="date"
class="w-[160px]!" class="!w-[160px]"
placeholder="请选择日期" placeholder="请选择日期"
format="YYYY/MM/DD" format="YYYY/MM/DD"
:value-format="dateFormat" :value-format="dateFormat"
@@ -271,18 +271,18 @@ watch(size, val =>
<span class="ml-2">{{ value7 }}</span> <span class="ml-2">{{ value7 }}</span>
</el-space> </el-space>
<div class="mb-2 mt-4">自定义前缀</div> <p class="mb-2 mt-4">自定义前缀</p>
<el-date-picker <el-date-picker
v-model="value8" v-model="value8"
type="date" type="date"
class="w-[160px]!" class="!w-[160px]"
placeholder="请选择日期" placeholder="请选择日期"
:prefix-icon="useRenderIcon('twemoji:spiral-calendar')" :prefix-icon="useRenderIcon('twemoji:spiral-calendar')"
:size="dynamicSize" :size="dynamicSize"
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
<div class="mb-2 mt-4">自定义内容</div> <p class="mb-2 mt-4">自定义内容</p>
<el-date-picker <el-date-picker
v-model="value9" v-model="value9"
type="date" type="date"

View File

@@ -186,18 +186,18 @@ watch(size, val =>
</el-link> </el-link>
</template> </template>
<div class="mb-2">日期和时间点</div> <p class="mb-2">日期和时间点</p>
<el-date-picker <el-date-picker
v-model="value" v-model="value"
type="datetime" type="datetime"
class="w-[200px]!" class="!w-[200px]"
placeholder="请选择日期时间" placeholder="请选择日期时间"
:shortcuts="shortcuts" :shortcuts="shortcuts"
:size="dynamicSize" :size="dynamicSize"
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
<div class="mb-2 mt-4">日期时间格式</div> <p class="mb-2 mt-4">日期时间格式</p>
<el-radio-group <el-radio-group
v-model="datetimeFormat" v-model="datetimeFormat"
class="mb-2" class="mb-2"
@@ -213,7 +213,7 @@ watch(size, val =>
<el-date-picker <el-date-picker
v-model="value1" v-model="value1"
type="datetime" type="datetime"
class="w-[200px]!" class="!w-[200px]"
placeholder="请选择日期时间" placeholder="请选择日期时间"
format="YYYY/MM/DD hh:mm:ss" format="YYYY/MM/DD hh:mm:ss"
:value-format="datetimeFormat" :value-format="datetimeFormat"
@@ -223,7 +223,7 @@ watch(size, val =>
<span class="ml-2">{{ value1 }}</span> <span class="ml-2">{{ value1 }}</span>
</el-space> </el-space>
<div class="mb-2 mt-4">日期和时间范围</div> <p class="mb-2 mt-4">日期和时间范围</p>
<el-date-picker <el-date-picker
v-model="value2" v-model="value2"
type="datetimerange" type="datetimerange"
@@ -238,9 +238,9 @@ watch(size, val =>
:disabled="size === 'disabled'" :disabled="size === 'disabled'"
/> />
<div class="mb-2 mt-4"> <p class="mb-2 mt-4">
弹出面板位置可控(如果弹出位置不足以完整展示面板会自动调整位置) 弹出面板位置可控(如果弹出位置不足以完整展示面板会自动调整位置)
</div> </p>
<el-space wrap class="w-[400px]"> <el-space wrap class="w-[400px]">
<el-check-tag <el-check-tag
v-for="(tag, index) in checkTag" v-for="(tag, index) in checkTag"

View File

@@ -3,7 +3,7 @@ import { ref } from "vue";
// 声明 props 类型 // 声明 props 类型
export interface FormProps { export interface FormProps {
formInline?: { formInline: {
user: string; user: string;
region: string; region: string;
}; };
@@ -28,14 +28,14 @@ const newFormInline = ref(props.formInline);
<el-form-item label="姓名"> <el-form-item label="姓名">
<el-input <el-input
v-model="newFormInline.user" v-model="newFormInline.user"
class="w-[220px]!" class="!w-[220px]"
placeholder="请输入姓名" placeholder="请输入姓名"
/> />
</el-form-item> </el-form-item>
<el-form-item label="城市"> <el-form-item label="城市">
<el-select <el-select
v-model="newFormInline.region" v-model="newFormInline.region"
class="w-[220px]!" class="!w-[220px]"
placeholder="请选择城市" placeholder="请选择城市"
> >
<el-option label="上海" value="上海" /> <el-option label="上海" value="上海" />

View File

@@ -3,7 +3,7 @@ import { useVModel } from "@vueuse/core";
// 声明 props 类型 // 声明 props 类型
export interface FormProps { export interface FormProps {
data?: string; data: string;
} }
// 声明 props 默认值 // 声明 props 默认值
@@ -18,5 +18,5 @@ const data = useVModel(props, "data", emit);
</script> </script>
<template> <template>
<el-input v-model="data" class="w-[220px]!" placeholder="请输入内容" /> <el-input v-model="data" class="!w-[220px]" placeholder="请输入内容" />
</template> </template>

View File

@@ -463,16 +463,6 @@ function onSureBtnLoading() {
} }
}); });
} }
// 自定义动画
function onTransitionClick(title, transition) {
addDialog({
width: "30%",
title,
transition,
contentRenderer: () => <p>{JSON.stringify(transition)}</p>
});
}
</script> </script>
<template> <template>
@@ -561,112 +551,5 @@ function onTransitionClick(title, transition) {
点击底部确定按钮可开启按钮动画 点击底部确定按钮可开启按钮动画
</el-button> </el-button>
</el-space> </el-space>
<el-divider />
<el-space wrap>
<el-button
@click="onTransitionClick('淡入淡出动画(默认)', 'dialog-fade')"
>
淡入淡出动画默认
</el-button>
<el-button @click="onTransitionClick('缩放动画', 'dialog-scale')">
缩放动画
</el-button>
<el-button @click="onTransitionClick('滑动动画', 'dialog-slide')">
滑动动画
</el-button>
<el-button @click="onTransitionClick('弹跳动画', 'dialog-bounce')">
弹跳动画
</el-button>
<el-button
@click="
onTransitionClick('自定义动画事件处理器(可配置对象)', {
name: 'dialog-custom-object',
appear: true,
mode: 'out-in',
duration: 500
})
"
>
自定义动画事件处理器可配置对象
</el-button>
</el-space>
</el-card> </el-card>
</template> </template>
<style>
/* Scale Animation */
.dialog-scale-enter-active,
.dialog-scale-leave-active,
.dialog-scale-enter-active .el-dialog,
.dialog-scale-leave-active .el-dialog {
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.dialog-scale-enter-from,
.dialog-scale-leave-to {
opacity: 0;
}
.dialog-scale-enter-from .el-dialog,
.dialog-scale-leave-to .el-dialog {
opacity: 0;
transform: scale(0.5);
}
/* Slide Animation */
.dialog-slide-enter-active,
.dialog-slide-leave-active,
.dialog-slide-enter-active .el-dialog,
.dialog-slide-leave-active .el-dialog {
transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
.dialog-slide-enter-from,
.dialog-slide-leave-to {
opacity: 0;
}
.dialog-slide-enter-from .el-dialog,
.dialog-slide-leave-to .el-dialog {
opacity: 0;
transform: translateY(-100px);
}
/* Bounce Animation */
.dialog-bounce-enter-active,
.dialog-bounce-leave-active,
.dialog-bounce-enter-active .el-dialog,
.dialog-bounce-leave-active .el-dialog {
transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}
.dialog-bounce-enter-from,
.dialog-bounce-leave-to {
opacity: 0;
}
.dialog-bounce-enter-from .el-dialog,
.dialog-bounce-leave-to .el-dialog {
opacity: 0;
transform: scale(0.3) translateY(-50px);
}
/* Object Configuration Animation */
.dialog-custom-object-enter-active,
.dialog-custom-object-leave-active,
.dialog-custom-object-enter-active .el-dialog,
.dialog-custom-object-leave-active .el-dialog {
transition: all 0.5s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.dialog-custom-object-enter-from,
.dialog-custom-object-leave-to {
opacity: 0;
}
.dialog-custom-object-enter-from .el-dialog,
.dialog-custom-object-leave-to .el-dialog {
opacity: 0;
transform: rotate(180deg) scale(0.5);
}
</style>

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