diff --git a/adminSystem/.env.development b/adminSystem/.env.development
new file mode 100644
index 0000000..10cc209
--- /dev/null
+++ b/adminSystem/.env.development
@@ -0,0 +1,13 @@
+# 【开发】环境变量
+
+# 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
+VITE_BASE_URL = /
+
+# API 请求基础路径(开发环境设置为 / 使用代理,生产环境设置为完整后端地址)
+VITE_API_URL = /
+
+# 代理目标地址(开发环境通过 Vite 代理转发请求到此地址,解决跨域问题)
+VITE_API_PROXY_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
+
+# Delete console
+VITE_DROP_CONSOLE = false
\ No newline at end of file
diff --git a/adminSystem/.env.production b/adminSystem/.env.production
new file mode 100644
index 0000000..89e0aaa
--- /dev/null
+++ b/adminSystem/.env.production
@@ -0,0 +1,10 @@
+# 【生产】环境变量
+
+# 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
+VITE_BASE_URL = /
+
+# API 地址前缀
+VITE_API_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
+
+# Delete console
+VITE_DROP_CONSOLE = true
\ No newline at end of file
diff --git a/adminSystem/.gitattributes b/adminSystem/.gitattributes
new file mode 100644
index 0000000..866e8ee
--- /dev/null
+++ b/adminSystem/.gitattributes
@@ -0,0 +1,2 @@
+*.html linguist-detectable=false
+*.vue linguist-detectable=true
diff --git a/adminSystem/.gitignore b/adminSystem/.gitignore
new file mode 100644
index 0000000..e48d2e9
--- /dev/null
+++ b/adminSystem/.gitignore
@@ -0,0 +1,11 @@
+node_modules
+.DS_Store
+dist
+dist-ssr
+*.local
+.cursorrules
+
+# Auto-generated files
+src/types/import/auto-imports.d.ts
+src/types/import/components.d.ts
+.auto-import.json
diff --git a/adminSystem/.husky/commit-msg b/adminSystem/.husky/commit-msg
new file mode 100644
index 0000000..09d2b14
--- /dev/null
+++ b/adminSystem/.husky/commit-msg
@@ -0,0 +1 @@
+pnpm dlx commitlint --edit $1
\ No newline at end of file
diff --git a/adminSystem/.husky/pre-commit b/adminSystem/.husky/pre-commit
new file mode 100644
index 0000000..22c0347
--- /dev/null
+++ b/adminSystem/.husky/pre-commit
@@ -0,0 +1 @@
+pnpm run lint:lint-staged
\ No newline at end of file
diff --git a/adminSystem/.prettierignore b/adminSystem/.prettierignore
new file mode 100644
index 0000000..9e96efc
--- /dev/null
+++ b/adminSystem/.prettierignore
@@ -0,0 +1,3 @@
+/node_modules/*
+/dist/*
+/src/main.ts
\ No newline at end of file
diff --git a/adminSystem/.prettierrc b/adminSystem/.prettierrc
new file mode 100644
index 0000000..f3d6ad5
--- /dev/null
+++ b/adminSystem/.prettierrc
@@ -0,0 +1,20 @@
+{
+ "printWidth": 100,
+ "tabWidth": 2,
+ "useTabs": false,
+ "semi": false,
+ "vueIndentScriptAndStyle": true,
+ "singleQuote": true,
+ "quoteProps": "as-needed",
+ "bracketSpacing": true,
+ "trailingComma": "none",
+ "bracketSameLine": false,
+ "jsxSingleQuote": false,
+ "arrowParens": "always",
+ "insertPragma": false,
+ "requirePragma": false,
+ "proseWrap": "never",
+ "htmlWhitespaceSensitivity": "strict",
+ "endOfLine": "auto",
+ "rangeStart": 0
+}
diff --git a/adminSystem/.stylelintignore b/adminSystem/.stylelintignore
new file mode 100644
index 0000000..476ea45
--- /dev/null
+++ b/adminSystem/.stylelintignore
@@ -0,0 +1,9 @@
+dist
+node_modules
+public
+.husky
+.vscode
+
+src/components/Layout/MenuLeft/index.vue
+src/assets
+stats.html
\ No newline at end of file
diff --git a/adminSystem/.stylelintrc.cjs b/adminSystem/.stylelintrc.cjs
new file mode 100644
index 0000000..9dbea0b
--- /dev/null
+++ b/adminSystem/.stylelintrc.cjs
@@ -0,0 +1,82 @@
+module.exports = {
+ // 继承推荐规范配置
+ extends: [
+ 'stylelint-config-standard',
+ 'stylelint-config-recommended-scss',
+ 'stylelint-config-recommended-vue/scss',
+ 'stylelint-config-html/vue',
+ 'stylelint-config-recess-order'
+ ],
+ // 指定不同文件对应的解析器
+ overrides: [
+ {
+ files: ['**/*.{vue,html}'],
+ customSyntax: 'postcss-html'
+ },
+ {
+ files: ['**/*.{css,scss}'],
+ customSyntax: 'postcss-scss'
+ }
+ ],
+ // 自定义规则
+ rules: {
+ 'import-notation': 'string', // 指定导入CSS文件的方式("string"|"url")
+ 'selector-class-pattern': null, // 选择器类名命名规则
+ 'custom-property-pattern': null, // 自定义属性命名规则
+ 'keyframes-name-pattern': null, // 动画帧节点样式命名规则
+ 'no-descending-specificity': null, // 允许无降序特异性
+ 'no-empty-source': null, // 允许空样式
+ 'property-no-vendor-prefix': null, // 允许属性前缀
+ // 允许 global 、export 、deep伪类
+ 'selector-pseudo-class-no-unknown': [
+ true,
+ {
+ ignorePseudoClasses: ['global', 'export', 'deep']
+ }
+ ],
+ // 允许未知属性
+ 'property-no-unknown': [
+ true,
+ {
+ ignoreProperties: []
+ }
+ ],
+ // 允许未知规则
+ 'at-rule-no-unknown': [
+ true,
+ {
+ ignoreAtRules: [
+ 'apply',
+ 'use',
+ 'mixin',
+ 'include',
+ 'extend',
+ 'each',
+ 'if',
+ 'else',
+ 'for',
+ 'while',
+ 'reference'
+ ]
+ }
+ ],
+ 'scss/at-rule-no-unknown': [
+ true,
+ {
+ ignoreAtRules: [
+ 'apply',
+ 'use',
+ 'mixin',
+ 'include',
+ 'extend',
+ 'each',
+ 'if',
+ 'else',
+ 'for',
+ 'while',
+ 'reference'
+ ]
+ }
+ ]
+ }
+}
diff --git a/adminSystem/LICENSE b/adminSystem/LICENSE
new file mode 100644
index 0000000..68322de
--- /dev/null
+++ b/adminSystem/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 SuperManTT
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/adminSystem/commitlint.config.cjs b/adminSystem/commitlint.config.cjs
new file mode 100644
index 0000000..d2ef1bd
--- /dev/null
+++ b/adminSystem/commitlint.config.cjs
@@ -0,0 +1,97 @@
+/**
+ * commitlint 配置文件
+ * 文档
+ * https://commitlint.js.org/#/reference-rules
+ * https://cz-git.qbb.sh/zh/guide/
+ */
+
+module.exports = {
+ // 继承的规则
+ extends: ['@commitlint/config-conventional'],
+ // 自定义规则
+ rules: {
+ // 提交类型枚举,git提交type必须是以下类型
+ 'type-enum': [
+ 2,
+ 'always',
+ [
+ 'feat', // 新增功能
+ 'fix', // 修复缺陷
+ 'docs', // 文档变更
+ 'style', // 代码格式(不影响功能,例如空格、分号等格式修正)
+ 'refactor', // 代码重构(不包括 bug 修复、功能新增)
+ 'perf', // 性能优化
+ 'test', // 添加疏漏测试或已有测试改动
+ 'build', // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
+ 'ci', // 修改 CI 配置、脚本
+ 'revert', // 回滚 commit
+ 'chore', // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
+ 'wip' // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
+ ]
+ ],
+ 'subject-case': [0] // subject大小写不做校验
+ },
+
+ prompt: {
+ messages: {
+ type: '选择你要提交的类型 :',
+ scope: '选择一个提交范围(可选):',
+ customScope: '请输入自定义的提交范围 :',
+ subject: '填写简短精炼的变更描述 :\n',
+ body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
+ breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
+ footerPrefixesSelect: '选择关联issue前缀(可选):',
+ customFooterPrefix: '输入自定义issue前缀 :',
+ footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
+ generatingByAI: '正在通过 AI 生成你的提交简短描述...',
+ generatedSelectByAI: '选择一个 AI 生成的简短描述:',
+ confirmCommit: '是否提交或修改commit ?'
+ },
+ // prettier-ignore
+ types: [
+ { value: "feat", name: "feat: 新增功能" },
+ { value: "fix", name: "fix: 修复缺陷" },
+ { value: "docs", name: "docs: 文档变更" },
+ { value: "style", name: "style: 代码格式(不影响功能,例如空格、分号等格式修正)" },
+ { value: "refactor", name: "refactor: 代码重构(不包括 bug 修复、功能新增)" },
+ { value: "perf", name: "perf: 性能优化" },
+ { value: "test", name: "test: 添加疏漏测试或已有测试改动" },
+ { value: "build", name: "build: 构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)" },
+ { value: "ci", name: "ci: 修改 CI 配置、脚本" },
+ { value: "revert", name: "revert: 回滚 commit" },
+ { value: "chore", name: "chore: 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)" },
+ ],
+ useEmoji: true,
+ emojiAlign: 'center',
+ useAI: false,
+ aiNumber: 1,
+ themeColorCode: '',
+ scopes: [],
+ allowCustomScopes: true,
+ allowEmptyScopes: true,
+ customScopesAlign: 'bottom',
+ customScopesAlias: 'custom',
+ emptyScopesAlias: 'empty',
+ upperCaseSubject: false,
+ markBreakingChangeMode: false,
+ allowBreakingChanges: ['feat', 'fix'],
+ breaklineNumber: 100,
+ breaklineChar: '|',
+ skipQuestions: ['breaking', 'footerPrefix', 'footer'], // 跳过的步骤
+ issuePrefixes: [{ value: 'closed', name: 'closed: ISSUES has been processed' }],
+ customIssuePrefixAlign: 'top',
+ emptyIssuePrefixAlias: 'skip',
+ customIssuePrefixAlias: 'custom',
+ allowCustomIssuePrefix: true,
+ allowEmptyIssuePrefix: true,
+ confirmColorize: true,
+ maxHeaderLength: Infinity,
+ maxSubjectLength: Infinity,
+ minSubjectLength: 0,
+ scopeOverrides: undefined,
+ defaultBody: '',
+ defaultIssues: '',
+ defaultScope: '',
+ defaultSubject: ''
+ }
+}
diff --git a/adminSystem/eslint.config.mjs b/adminSystem/eslint.config.mjs
new file mode 100644
index 0000000..bb317f6
--- /dev/null
+++ b/adminSystem/eslint.config.mjs
@@ -0,0 +1,83 @@
+// 从 URL 和路径模块中导入必要的功能
+import fs from 'fs'
+import path, { dirname } from 'path'
+import { fileURLToPath } from 'url'
+
+// 从 ESLint 插件中导入推荐配置
+import pluginJs from '@eslint/js'
+import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended'
+import pluginVue from 'eslint-plugin-vue'
+import globals from 'globals'
+import tseslint from 'typescript-eslint'
+
+// 使用 import.meta.url 获取当前模块的路径
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = dirname(__filename)
+
+// 读取 .auto-import.json 文件的内容,并将其解析为 JSON 对象
+const autoImportConfig = JSON.parse(
+ fs.readFileSync(path.resolve(__dirname, '.auto-import.json'), 'utf-8')
+)
+
+export default [
+ // 指定文件匹配规则
+ {
+ files: ['**/*.{js,mjs,cjs,ts,vue}']
+ },
+ // 指定全局变量和环境
+ {
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ ...globals.node
+ }
+ }
+ },
+ // 扩展配置
+ pluginJs.configs.recommended,
+ ...tseslint.configs.recommended,
+ ...pluginVue.configs['flat/essential'],
+ // 自定义规则
+ {
+ // 针对所有 JavaScript、TypeScript 和 Vue 文件应用以下配置
+ files: ['**/*.{js,mjs,cjs,ts,vue}'],
+
+ languageOptions: {
+ globals: {
+ // 合并从 autoImportConfig 中读取的全局变量配置
+ ...autoImportConfig.globals,
+ // TypeScript 全局命名空间
+ Api: 'readonly'
+ }
+ },
+ rules: {
+ quotes: ['error', 'single'], // 使用单引号
+ semi: ['error', 'never'], // 语句末尾不加分号
+ 'no-var': 'error', // 要求使用 let 或 const 而不是 var
+ '@typescript-eslint/no-explicit-any': 'off', // 禁用 any 检查
+ 'vue/multi-word-component-names': 'off', // 禁用对 Vue 组件名称的多词要求检查
+ 'no-multiple-empty-lines': ['warn', { max: 1 }], // 不允许多个空行
+ 'no-unexpected-multiline': 'error' // 禁止空余的多行
+ }
+ },
+ // vue 规则
+ {
+ files: ['**/*.vue'],
+ languageOptions: {
+ parserOptions: { parser: tseslint.parser }
+ }
+ },
+ // 忽略文件
+ {
+ ignores: [
+ 'node_modules',
+ 'dist',
+ 'public',
+ '.vscode/**',
+ 'src/assets/**',
+ 'src/utils/console.ts'
+ ]
+ },
+ // prettier 配置
+ eslintPluginPrettierRecommended
+]
diff --git a/adminSystem/index.html b/adminSystem/index.html
new file mode 100644
index 0000000..ba51ce5
--- /dev/null
+++ b/adminSystem/index.html
@@ -0,0 +1,47 @@
+
+
+
+ Art Design Pro
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/package.json b/adminSystem/package.json
new file mode 100644
index 0000000..7b66362
--- /dev/null
+++ b/adminSystem/package.json
@@ -0,0 +1,123 @@
+{
+ "name": "art-design-pro",
+ "version": "0.0.0",
+ "type": "module",
+ "engines": {
+ "node": ">=20.19.0",
+ "pnpm": ">=8.8.0"
+ },
+ "scripts": {
+ "dev": "vite --open",
+ "build": "vue-tsc --noEmit && vite build",
+ "serve": "vite preview",
+ "lint": "eslint",
+ "fix": "eslint --fix",
+ "lint:prettier": "prettier --write \"**/*.{js,cjs,ts,json,tsx,css,less,scss,vue,html,md}\"",
+ "lint:stylelint": "stylelint \"**/*.{css,scss,vue}\" --fix",
+ "lint:lint-staged": "lint-staged",
+ "prepare": "husky",
+ "commit": "git-cz",
+ "clean:dev": "tsx scripts/clean-dev.ts"
+ },
+ "config": {
+ "commitizen": {
+ "path": "node_modules/cz-git"
+ }
+ },
+ "lint-staged": {
+ "*.{js,ts,mjs,mts,tsx}": [
+ "eslint --fix",
+ "prettier --write"
+ ],
+ "*.{cjs,json,jsonc}": [
+ "prettier --write"
+ ],
+ "*.vue": [
+ "eslint --fix",
+ "stylelint --fix --allow-empty-input",
+ "prettier --write"
+ ],
+ "*.{html,htm}": [
+ "prettier --write"
+ ],
+ "*.{scss,css,less}": [
+ "stylelint --fix --allow-empty-input",
+ "prettier --write"
+ ],
+ "*.{md,mdx}": [
+ "prettier --write"
+ ],
+ "*.{yaml,yml}": [
+ "prettier --write"
+ ]
+ },
+ "dependencies": {
+ "@element-plus/icons-vue": "^2.3.2",
+ "@iconify/vue": "^5.0.0",
+ "@tailwindcss/vite": "^4.1.14",
+ "@vue/reactivity": "^3.5.21",
+ "@vueuse/core": "^13.9.0",
+ "@wangeditor/editor": "^5.1.23",
+ "@wangeditor/editor-for-vue": "next",
+ "axios": "^1.12.2",
+ "crypto-js": "^4.2.0",
+ "echarts": "^6.0.0",
+ "element-plus": "^2.11.2",
+ "file-saver": "^2.0.5",
+ "highlight.js": "^11.10.0",
+ "mitt": "^3.0.1",
+ "nprogress": "^0.2.0",
+ "ohash": "^2.0.11",
+ "pinia": "^3.0.3",
+ "pinia-plugin-persistedstate": "^4.3.0",
+ "qrcode.vue": "^3.6.0",
+ "tailwindcss": "^4.1.14",
+ "vue": "^3.5.21",
+ "vue-draggable-plus": "^0.6.0",
+ "vue-i18n": "^9.14.0",
+ "vue-router": "^4.5.1",
+ "xgplayer": "^3.0.20",
+ "xlsx": "^0.18.5"
+ },
+ "devDependencies": {
+ "@commitlint/cli": "^19.4.1",
+ "@commitlint/config-conventional": "^19.4.1",
+ "@eslint/js": "^9.9.1",
+ "@types/node": "^24.0.5",
+ "@typescript-eslint/eslint-plugin": "^8.3.0",
+ "@typescript-eslint/parser": "^8.3.0",
+ "@vitejs/plugin-vue": "^6.0.1",
+ "@vue/compiler-sfc": "^3.0.5",
+ "commitizen": "^4.3.0",
+ "cz-git": "^1.11.1",
+ "eslint": "^9.9.1",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-prettier": "^5.2.1",
+ "eslint-plugin-vue": "^9.27.0",
+ "globals": "^15.9.0",
+ "husky": "^9.1.5",
+ "lint-staged": "^15.5.2",
+ "prettier": "^3.5.3",
+ "rollup-plugin-visualizer": "^5.12.0",
+ "sass": "^1.81.0",
+ "stylelint": "^16.20.0",
+ "stylelint-config-html": "^1.1.0",
+ "stylelint-config-recess-order": "^4.6.0",
+ "stylelint-config-recommended-scss": "^14.1.0",
+ "stylelint-config-recommended-vue": "^1.5.0",
+ "stylelint-config-standard": "^36.0.1",
+ "terser": "^5.36.0",
+ "tsx": "^4.20.3",
+ "typescript": "~5.6.3",
+ "typescript-eslint": "^8.9.0",
+ "unplugin-auto-import": "^20.2.0",
+ "unplugin-element-plus": "^0.10.0",
+ "unplugin-vue-components": "^29.1.0",
+ "vite": "^7.1.5",
+ "vite-plugin-compression": "^0.5.1",
+ "vite-plugin-vue-devtools": "^7.7.6",
+ "vue-demi": "^0.14.9",
+ "vue-img-cutter": "^3.0.5",
+ "vue-tsc": "~2.1.6"
+ }
+}
diff --git a/adminSystem/pnpm-lock.yaml b/adminSystem/pnpm-lock.yaml
new file mode 100644
index 0000000..401c518
--- /dev/null
+++ b/adminSystem/pnpm-lock.yaml
@@ -0,0 +1,10109 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+ .:
+ dependencies:
+ '@element-plus/icons-vue':
+ specifier: ^2.3.2
+ version: 2.3.2(vue@3.5.22(typescript@5.6.3))
+ '@iconify/vue':
+ specifier: ^5.0.0
+ version: 5.0.0(vue@3.5.22(typescript@5.6.3))
+ '@tailwindcss/vite':
+ specifier: ^4.1.14
+ version: 4.1.14(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ '@vue/reactivity':
+ specifier: ^3.5.21
+ version: 3.5.22
+ '@vueuse/core':
+ specifier: ^13.9.0
+ version: 13.9.0(vue@3.5.22(typescript@5.6.3))
+ '@wangeditor/editor':
+ specifier: ^5.1.23
+ version: 5.1.23
+ '@wangeditor/editor-for-vue':
+ specifier: next
+ version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.22(typescript@5.6.3))
+ axios:
+ specifier: ^1.12.2
+ version: 1.12.2
+ crypto-js:
+ specifier: ^4.2.0
+ version: 4.2.0
+ echarts:
+ specifier: ^6.0.0
+ version: 6.0.0
+ element-plus:
+ specifier: ^2.11.2
+ version: 2.11.4(vue@3.5.22(typescript@5.6.3))
+ file-saver:
+ specifier: ^2.0.5
+ version: 2.0.5
+ highlight.js:
+ specifier: ^11.10.0
+ version: 11.11.1
+ mitt:
+ specifier: ^3.0.1
+ version: 3.0.1
+ nprogress:
+ specifier: ^0.2.0
+ version: 0.2.0
+ ohash:
+ specifier: ^2.0.11
+ version: 2.0.11
+ pinia:
+ specifier: ^3.0.3
+ version: 3.0.3(typescript@5.6.3)(vue@3.5.22(typescript@5.6.3))
+ pinia-plugin-persistedstate:
+ specifier: ^4.3.0
+ version: 4.5.0(pinia@3.0.3(typescript@5.6.3)(vue@3.5.22(typescript@5.6.3)))
+ qrcode.vue:
+ specifier: ^3.6.0
+ version: 3.6.0(vue@3.5.22(typescript@5.6.3))
+ tailwindcss:
+ specifier: ^4.1.14
+ version: 4.1.14
+ vue:
+ specifier: ^3.5.21
+ version: 3.5.22(typescript@5.6.3)
+ vue-draggable-plus:
+ specifier: ^0.6.0
+ version: 0.6.0(@types/sortablejs@1.15.8)
+ vue-i18n:
+ specifier: ^9.14.0
+ version: 9.14.5(vue@3.5.22(typescript@5.6.3))
+ vue-router:
+ specifier: ^4.5.1
+ version: 4.5.1(vue@3.5.22(typescript@5.6.3))
+ xgplayer:
+ specifier: ^3.0.20
+ version: 3.0.23(core-js@3.45.1)
+ xlsx:
+ specifier: ^0.18.5
+ version: 0.18.5
+ devDependencies:
+ '@commitlint/cli':
+ specifier: ^19.4.1
+ version: 19.8.1(@types/node@24.8.1)(typescript@5.6.3)
+ '@commitlint/config-conventional':
+ specifier: ^19.4.1
+ version: 19.8.1
+ '@eslint/js':
+ specifier: ^9.9.1
+ version: 9.36.0
+ '@types/node':
+ specifier: ^24.0.5
+ version: 24.8.1
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^8.3.0
+ version: 8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3))(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ '@typescript-eslint/parser':
+ specifier: ^8.3.0
+ version: 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ '@vitejs/plugin-vue':
+ specifier: ^6.0.1
+ version: 6.0.1(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.6.3))
+ '@vue/compiler-sfc':
+ specifier: ^3.0.5
+ version: 3.5.22
+ commitizen:
+ specifier: ^4.3.0
+ version: 4.3.1(@types/node@24.8.1)(typescript@5.6.3)
+ cz-git:
+ specifier: ^1.11.1
+ version: 1.12.0
+ eslint:
+ specifier: ^9.9.1
+ version: 9.36.0(jiti@2.6.0)
+ eslint-config-prettier:
+ specifier: ^9.1.0
+ version: 9.1.2(eslint@9.36.0(jiti@2.6.0))
+ eslint-plugin-prettier:
+ specifier: ^5.2.1
+ version: 5.5.4(eslint-config-prettier@9.1.2(eslint@9.36.0(jiti@2.6.0)))(eslint@9.36.0(jiti@2.6.0))(prettier@3.6.2)
+ eslint-plugin-vue:
+ specifier: ^9.27.0
+ version: 9.33.0(eslint@9.36.0(jiti@2.6.0))
+ globals:
+ specifier: ^15.9.0
+ version: 15.15.0
+ husky:
+ specifier: ^9.1.5
+ version: 9.1.7
+ lint-staged:
+ specifier: ^15.5.2
+ version: 15.5.2
+ prettier:
+ specifier: ^3.5.3
+ version: 3.6.2
+ rollup-plugin-visualizer:
+ specifier: ^5.12.0
+ version: 5.14.0(rollup@4.52.3)
+ sass:
+ specifier: ^1.81.0
+ version: 1.93.2
+ stylelint:
+ specifier: ^16.20.0
+ version: 16.24.0(typescript@5.6.3)
+ stylelint-config-html:
+ specifier: ^1.1.0
+ version: 1.1.0(postcss-html@1.8.0)(stylelint@16.24.0(typescript@5.6.3))
+ stylelint-config-recess-order:
+ specifier: ^4.6.0
+ version: 4.6.0(stylelint@16.24.0(typescript@5.6.3))
+ stylelint-config-recommended-scss:
+ specifier: ^14.1.0
+ version: 14.1.0(postcss@8.5.6)(stylelint@16.24.0(typescript@5.6.3))
+ stylelint-config-recommended-vue:
+ specifier: ^1.5.0
+ version: 1.6.1(postcss-html@1.8.0)(stylelint@16.24.0(typescript@5.6.3))
+ stylelint-config-standard:
+ specifier: ^36.0.1
+ version: 36.0.1(stylelint@16.24.0(typescript@5.6.3))
+ terser:
+ specifier: ^5.36.0
+ version: 5.44.0
+ tsx:
+ specifier: ^4.20.3
+ version: 4.20.6
+ typescript:
+ specifier: ~5.6.3
+ version: 5.6.3
+ typescript-eslint:
+ specifier: ^8.9.0
+ version: 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ unplugin-auto-import:
+ specifier: ^20.2.0
+ version: 20.2.0(@vueuse/core@13.9.0(vue@3.5.22(typescript@5.6.3)))
+ unplugin-element-plus:
+ specifier: ^0.10.0
+ version: 0.10.0
+ unplugin-vue-components:
+ specifier: ^29.1.0
+ version: 29.1.0(@babel/parser@7.28.4)(vue@3.5.22(typescript@5.6.3))
+ vite:
+ specifier: ^7.1.5
+ version: 7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ vite-plugin-compression:
+ specifier: ^0.5.1
+ version: 0.5.1(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ vite-plugin-vue-devtools:
+ specifier: ^7.7.6
+ version: 7.7.7(rollup@4.52.3)(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.6.3))
+ vue-demi:
+ specifier: ^0.14.9
+ version: 0.14.10(vue@3.5.22(typescript@5.6.3))
+ vue-img-cutter:
+ specifier: ^3.0.5
+ version: 3.0.7(typescript@5.6.3)
+ vue-tsc:
+ specifier: ~2.1.6
+ version: 2.1.10(typescript@5.6.3)
+
+packages:
+ '@antfu/utils@0.7.10':
+ resolution:
+ {
+ integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==
+ }
+
+ '@babel/code-frame@7.27.1':
+ resolution:
+ {
+ integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/compat-data@7.28.4':
+ resolution:
+ {
+ integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/core@7.28.4':
+ resolution:
+ {
+ integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/generator@7.28.3':
+ resolution:
+ {
+ integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ resolution:
+ {
+ integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-compilation-targets@7.27.2':
+ resolution:
+ {
+ integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-create-class-features-plugin@7.28.3':
+ resolution:
+ {
+ integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-globals@7.28.0':
+ resolution:
+ {
+ integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-member-expression-to-functions@7.27.1':
+ resolution:
+ {
+ integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-module-imports@7.27.1':
+ resolution:
+ {
+ integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-module-transforms@7.28.3':
+ resolution:
+ {
+ integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ resolution:
+ {
+ integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-plugin-utils@7.27.1':
+ resolution:
+ {
+ integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-replace-supers@7.27.1':
+ resolution:
+ {
+ integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ resolution:
+ {
+ integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution:
+ {
+ integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution:
+ {
+ integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution:
+ {
+ integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/helpers@7.28.4':
+ resolution:
+ {
+ integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/parser@7.28.4':
+ resolution:
+ {
+ integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==
+ }
+ engines: { node: '>=6.0.0' }
+ hasBin: true
+
+ '@babel/plugin-proposal-decorators@7.28.0':
+ resolution:
+ {
+ integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-decorators@7.27.1':
+ resolution:
+ {
+ integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-attributes@7.27.1':
+ resolution:
+ {
+ integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-meta@7.10.4':
+ resolution:
+ {
+ integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==
+ }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-jsx@7.27.1':
+ resolution:
+ {
+ integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.27.1':
+ resolution:
+ {
+ integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-typescript@7.28.0':
+ resolution:
+ {
+ integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==
+ }
+ engines: { node: '>=6.9.0' }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/runtime@7.28.4':
+ resolution:
+ {
+ integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/template@7.27.2':
+ resolution:
+ {
+ integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/traverse@7.28.4':
+ resolution:
+ {
+ integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@babel/types@7.28.4':
+ resolution:
+ {
+ integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==
+ }
+ engines: { node: '>=6.9.0' }
+
+ '@cacheable/memoize@2.0.2':
+ resolution:
+ {
+ integrity: sha512-wPrr7FUiq3Qt4yQyda2/NcOLTJCFcQSU3Am2adP+WLy+sz93/fKTokVTHmtz+rjp4PD7ee0AEOeRVNN6IvIfsg==
+ }
+
+ '@cacheable/memory@2.0.2':
+ resolution:
+ {
+ integrity: sha512-sJTITLfeCI1rg7P3ssaGmQryq235EGT8dXGcx6oZwX5NRnKq9IE6lddlllcOl+oXW+yaeTRddCjo0xrfU6ZySA==
+ }
+
+ '@cacheable/utils@2.0.2':
+ resolution:
+ {
+ integrity: sha512-JTFM3raFhVv8LH95T7YnZbf2YoE9wEtkPPStuRF9a6ExZ103hFvs+QyCuYJ6r0hA9wRtbzgZtwUCoDWxssZd4Q==
+ }
+
+ '@commitlint/cli@19.8.1':
+ resolution:
+ {
+ integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==
+ }
+ engines: { node: '>=v18' }
+ hasBin: true
+
+ '@commitlint/config-conventional@19.8.1':
+ resolution:
+ {
+ integrity: sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/config-validator@19.8.1':
+ resolution:
+ {
+ integrity: sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/config-validator@20.0.0':
+ resolution:
+ {
+ integrity: sha512-BeyLMaRIJDdroJuYM2EGhDMGwVBMZna9UiIqV9hxj+J551Ctc6yoGuGSmghOy/qPhBSuhA6oMtbEiTmxECafsg==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/ensure@19.8.1':
+ resolution:
+ {
+ integrity: sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/execute-rule@19.8.1':
+ resolution:
+ {
+ integrity: sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/execute-rule@20.0.0':
+ resolution:
+ {
+ integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/format@19.8.1':
+ resolution:
+ {
+ integrity: sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/is-ignored@19.8.1':
+ resolution:
+ {
+ integrity: sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/lint@19.8.1':
+ resolution:
+ {
+ integrity: sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/load@19.8.1':
+ resolution:
+ {
+ integrity: sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/load@20.0.0':
+ resolution:
+ {
+ integrity: sha512-WiNKO9fDPlLY90Rruw2HqHKcghrmj5+kMDJ4GcTlX1weL8K07Q6b27C179DxnsrjGCRAKVwFKyzxV4x+xDY28Q==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/message@19.8.1':
+ resolution:
+ {
+ integrity: sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/parse@19.8.1':
+ resolution:
+ {
+ integrity: sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/read@19.8.1':
+ resolution:
+ {
+ integrity: sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/resolve-extends@19.8.1':
+ resolution:
+ {
+ integrity: sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/resolve-extends@20.0.0':
+ resolution:
+ {
+ integrity: sha512-BA4vva1hY8y0/Hl80YDhe9TJZpRFMsUYzVxvwTLPTEBotbGx/gS49JlVvtF1tOCKODQp7pS7CbxCpiceBgp3Dg==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/rules@19.8.1':
+ resolution:
+ {
+ integrity: sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/to-lines@19.8.1':
+ resolution:
+ {
+ integrity: sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/top-level@19.8.1':
+ resolution:
+ {
+ integrity: sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/types@19.8.1':
+ resolution:
+ {
+ integrity: sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==
+ }
+ engines: { node: '>=v18' }
+
+ '@commitlint/types@20.0.0':
+ resolution:
+ {
+ integrity: sha512-bVUNBqG6aznYcYjTjnc3+Cat/iBgbgpflxbIBTnsHTX0YVpnmINPEkSRWymT2Q8aSH3Y7aKnEbunilkYe8TybA==
+ }
+ engines: { node: '>=v18' }
+
+ '@csstools/css-parser-algorithms@3.0.5':
+ resolution:
+ {
+ integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==
+ }
+ engines: { node: '>=18' }
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-tokenizer@3.0.4':
+ resolution:
+ {
+ integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==
+ }
+ engines: { node: '>=18' }
+
+ '@csstools/media-query-list-parser@4.0.3':
+ resolution:
+ {
+ integrity: sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==
+ }
+ engines: { node: '>=18' }
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/selector-specificity@5.0.0':
+ resolution:
+ {
+ integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==
+ }
+ engines: { node: '>=18' }
+ peerDependencies:
+ postcss-selector-parser: ^7.0.0
+
+ '@ctrl/tinycolor@3.6.1':
+ resolution:
+ {
+ integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==
+ }
+ engines: { node: '>=10' }
+
+ '@dual-bundle/import-meta-resolve@4.2.1':
+ resolution:
+ {
+ integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==
+ }
+
+ '@element-plus/icons-vue@2.3.2':
+ resolution:
+ {
+ integrity: sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==
+ }
+ peerDependencies:
+ vue: ^3.2.0
+
+ '@esbuild/aix-ppc64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==
+ }
+ engines: { node: '>=18' }
+ cpu: [ppc64]
+ os: [aix]
+
+ '@esbuild/android-arm64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm64]
+ os: [android]
+
+ '@esbuild/android-arm@0.25.10':
+ resolution:
+ {
+ integrity: sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm]
+ os: [android]
+
+ '@esbuild/android-x64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==
+ }
+ engines: { node: '>=18' }
+ cpu: [x64]
+ os: [android]
+
+ '@esbuild/darwin-arm64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm64]
+ os: [darwin]
+
+ '@esbuild/darwin-x64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==
+ }
+ engines: { node: '>=18' }
+ cpu: [x64]
+ os: [darwin]
+
+ '@esbuild/freebsd-arm64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@esbuild/freebsd-x64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==
+ }
+ engines: { node: '>=18' }
+ cpu: [x64]
+ os: [freebsd]
+
+ '@esbuild/linux-arm64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm64]
+ os: [linux]
+
+ '@esbuild/linux-arm@0.25.10':
+ resolution:
+ {
+ integrity: sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm]
+ os: [linux]
+
+ '@esbuild/linux-ia32@0.25.10':
+ resolution:
+ {
+ integrity: sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==
+ }
+ engines: { node: '>=18' }
+ cpu: [ia32]
+ os: [linux]
+
+ '@esbuild/linux-loong64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==
+ }
+ engines: { node: '>=18' }
+ cpu: [loong64]
+ os: [linux]
+
+ '@esbuild/linux-mips64el@0.25.10':
+ resolution:
+ {
+ integrity: sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==
+ }
+ engines: { node: '>=18' }
+ cpu: [mips64el]
+ os: [linux]
+
+ '@esbuild/linux-ppc64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==
+ }
+ engines: { node: '>=18' }
+ cpu: [ppc64]
+ os: [linux]
+
+ '@esbuild/linux-riscv64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==
+ }
+ engines: { node: '>=18' }
+ cpu: [riscv64]
+ os: [linux]
+
+ '@esbuild/linux-s390x@0.25.10':
+ resolution:
+ {
+ integrity: sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==
+ }
+ engines: { node: '>=18' }
+ cpu: [s390x]
+ os: [linux]
+
+ '@esbuild/linux-x64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==
+ }
+ engines: { node: '>=18' }
+ cpu: [x64]
+ os: [linux]
+
+ '@esbuild/netbsd-arm64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm64]
+ os: [netbsd]
+
+ '@esbuild/netbsd-x64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==
+ }
+ engines: { node: '>=18' }
+ cpu: [x64]
+ os: [netbsd]
+
+ '@esbuild/openbsd-arm64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm64]
+ os: [openbsd]
+
+ '@esbuild/openbsd-x64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==
+ }
+ engines: { node: '>=18' }
+ cpu: [x64]
+ os: [openbsd]
+
+ '@esbuild/openharmony-arm64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@esbuild/sunos-x64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==
+ }
+ engines: { node: '>=18' }
+ cpu: [x64]
+ os: [sunos]
+
+ '@esbuild/win32-arm64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==
+ }
+ engines: { node: '>=18' }
+ cpu: [arm64]
+ os: [win32]
+
+ '@esbuild/win32-ia32@0.25.10':
+ resolution:
+ {
+ integrity: sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==
+ }
+ engines: { node: '>=18' }
+ cpu: [ia32]
+ os: [win32]
+
+ '@esbuild/win32-x64@0.25.10':
+ resolution:
+ {
+ integrity: sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==
+ }
+ engines: { node: '>=18' }
+ cpu: [x64]
+ os: [win32]
+
+ '@eslint-community/eslint-utils@4.9.0':
+ resolution:
+ {
+ integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==
+ }
+ engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.1':
+ resolution:
+ {
+ integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
+ }
+ engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 }
+
+ '@eslint/config-array@0.21.0':
+ resolution:
+ {
+ integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@eslint/config-helpers@0.3.1':
+ resolution:
+ {
+ integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@eslint/core@0.15.2':
+ resolution:
+ {
+ integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@eslint/eslintrc@3.3.1':
+ resolution:
+ {
+ integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@eslint/js@9.36.0':
+ resolution:
+ {
+ integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@eslint/object-schema@2.1.6':
+ resolution:
+ {
+ integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@eslint/plugin-kit@0.3.5':
+ resolution:
+ {
+ integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@floating-ui/core@1.7.3':
+ resolution:
+ {
+ integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==
+ }
+
+ '@floating-ui/dom@1.7.4':
+ resolution:
+ {
+ integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==
+ }
+
+ '@floating-ui/utils@0.2.10':
+ resolution:
+ {
+ integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==
+ }
+
+ '@humanfs/core@0.19.1':
+ resolution:
+ {
+ integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==
+ }
+ engines: { node: '>=18.18.0' }
+
+ '@humanfs/node@0.16.7':
+ resolution:
+ {
+ integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==
+ }
+ engines: { node: '>=18.18.0' }
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution:
+ {
+ integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+ }
+ engines: { node: '>=12.22' }
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution:
+ {
+ integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==
+ }
+ engines: { node: '>=18.18' }
+
+ '@iconify/types@2.0.0':
+ resolution:
+ {
+ integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==
+ }
+
+ '@iconify/vue@5.0.0':
+ resolution:
+ {
+ integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==
+ }
+ peerDependencies:
+ vue: '>=3'
+
+ '@intlify/core-base@9.14.5':
+ resolution:
+ {
+ integrity: sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==
+ }
+ engines: { node: '>= 16' }
+
+ '@intlify/message-compiler@9.14.5':
+ resolution:
+ {
+ integrity: sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==
+ }
+ engines: { node: '>= 16' }
+
+ '@intlify/shared@9.14.5':
+ resolution:
+ {
+ integrity: sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==
+ }
+ engines: { node: '>= 16' }
+
+ '@isaacs/fs-minipass@4.0.1':
+ resolution:
+ {
+ integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==
+ }
+ engines: { node: '>=18.0.0' }
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution:
+ {
+ integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
+ }
+
+ '@jridgewell/remapping@2.3.5':
+ resolution:
+ {
+ integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
+ }
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution:
+ {
+ integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
+ }
+ engines: { node: '>=6.0.0' }
+
+ '@jridgewell/source-map@0.3.11':
+ resolution:
+ {
+ integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==
+ }
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution:
+ {
+ integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
+ }
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution:
+ {
+ integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
+ }
+
+ '@keyv/bigmap@1.0.2':
+ resolution:
+ {
+ integrity: sha512-KR03xkEZlAZNF4IxXgVXb+uNIVNvwdh8UwI0cnc7WI6a+aQcDp8GL80qVfeB4E5NpsKJzou5jU0r6yLSSbMOtA==
+ }
+ engines: { node: '>= 18' }
+
+ '@keyv/serialize@1.1.1':
+ resolution:
+ {
+ integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==
+ }
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution:
+ {
+ integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+ }
+ engines: { node: '>= 8' }
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution:
+ {
+ integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+ }
+ engines: { node: '>= 8' }
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution:
+ {
+ integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+ }
+ engines: { node: '>= 8' }
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ resolution:
+ {
+ integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ resolution:
+ {
+ integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ resolution:
+ {
+ integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ resolution:
+ {
+ integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ resolution:
+ {
+ integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ resolution:
+ {
+ integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ resolution:
+ {
+ integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ resolution:
+ {
+ integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ resolution:
+ {
+ integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ resolution:
+ {
+ integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ resolution:
+ {
+ integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ resolution:
+ {
+ integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ resolution:
+ {
+ integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==
+ }
+ engines: { node: '>= 10.0.0' }
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.1':
+ resolution:
+ {
+ integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==
+ }
+ engines: { node: '>= 10.0.0' }
+
+ '@pkgr/core@0.2.9':
+ resolution:
+ {
+ integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==
+ }
+ engines: { node: ^12.20.0 || ^14.18.0 || >=16.0.0 }
+
+ '@polka/url@1.0.0-next.29':
+ resolution:
+ {
+ integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==
+ }
+
+ '@rolldown/pluginutils@1.0.0-beta.29':
+ resolution:
+ {
+ integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==
+ }
+
+ '@rollup/pluginutils@5.3.0':
+ resolution:
+ {
+ integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==
+ }
+ engines: { node: '>=14.0.0' }
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
+ '@rollup/rollup-android-arm-eabi@4.52.3':
+ resolution:
+ {
+ integrity: sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==
+ }
+ cpu: [arm]
+ os: [android]
+
+ '@rollup/rollup-android-arm64@4.52.3':
+ resolution:
+ {
+ integrity: sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==
+ }
+ cpu: [arm64]
+ os: [android]
+
+ '@rollup/rollup-darwin-arm64@4.52.3':
+ resolution:
+ {
+ integrity: sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==
+ }
+ cpu: [arm64]
+ os: [darwin]
+
+ '@rollup/rollup-darwin-x64@4.52.3':
+ resolution:
+ {
+ integrity: sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==
+ }
+ cpu: [x64]
+ os: [darwin]
+
+ '@rollup/rollup-freebsd-arm64@4.52.3':
+ resolution:
+ {
+ integrity: sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==
+ }
+ cpu: [arm64]
+ os: [freebsd]
+
+ '@rollup/rollup-freebsd-x64@4.52.3':
+ resolution:
+ {
+ integrity: sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==
+ }
+ cpu: [x64]
+ os: [freebsd]
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.52.3':
+ resolution:
+ {
+ integrity: sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==
+ }
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm-musleabihf@4.52.3':
+ resolution:
+ {
+ integrity: sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==
+ }
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-arm64-gnu@4.52.3':
+ resolution:
+ {
+ integrity: sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==
+ }
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-arm64-musl@4.52.3':
+ resolution:
+ {
+ integrity: sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==
+ }
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-loong64-gnu@4.52.3':
+ resolution:
+ {
+ integrity: sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==
+ }
+ cpu: [loong64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-ppc64-gnu@4.52.3':
+ resolution:
+ {
+ integrity: sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==
+ }
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-gnu@4.52.3':
+ resolution:
+ {
+ integrity: sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==
+ }
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-riscv64-musl@4.52.3':
+ resolution:
+ {
+ integrity: sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==
+ }
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-linux-s390x-gnu@4.52.3':
+ resolution:
+ {
+ integrity: sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==
+ }
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-gnu@4.52.3':
+ resolution:
+ {
+ integrity: sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==
+ }
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@rollup/rollup-linux-x64-musl@4.52.3':
+ resolution:
+ {
+ integrity: sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==
+ }
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@rollup/rollup-openharmony-arm64@4.52.3':
+ resolution:
+ {
+ integrity: sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==
+ }
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@rollup/rollup-win32-arm64-msvc@4.52.3':
+ resolution:
+ {
+ integrity: sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==
+ }
+ cpu: [arm64]
+ os: [win32]
+
+ '@rollup/rollup-win32-ia32-msvc@4.52.3':
+ resolution:
+ {
+ integrity: sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==
+ }
+ cpu: [ia32]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-gnu@4.52.3':
+ resolution:
+ {
+ integrity: sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==
+ }
+ cpu: [x64]
+ os: [win32]
+
+ '@rollup/rollup-win32-x64-msvc@4.52.3':
+ resolution:
+ {
+ integrity: sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==
+ }
+ cpu: [x64]
+ os: [win32]
+
+ '@sec-ant/readable-stream@0.4.1':
+ resolution:
+ {
+ integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==
+ }
+
+ '@sindresorhus/merge-streams@4.0.0':
+ resolution:
+ {
+ integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==
+ }
+ engines: { node: '>=18' }
+
+ '@sxzz/popperjs-es@2.11.7':
+ resolution:
+ {
+ integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==
+ }
+
+ '@tailwindcss/node@4.1.14':
+ resolution:
+ {
+ integrity: sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==
+ }
+
+ '@tailwindcss/oxide-android-arm64@4.1.14':
+ resolution:
+ {
+ integrity: sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==
+ }
+ engines: { node: '>= 10' }
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.14':
+ resolution:
+ {
+ integrity: sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==
+ }
+ engines: { node: '>= 10' }
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.14':
+ resolution:
+ {
+ integrity: sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==
+ }
+ engines: { node: '>= 10' }
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.14':
+ resolution:
+ {
+ integrity: sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==
+ }
+ engines: { node: '>= 10' }
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14':
+ resolution:
+ {
+ integrity: sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==
+ }
+ engines: { node: '>= 10' }
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
+ resolution:
+ {
+ integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==
+ }
+ engines: { node: '>= 10' }
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.14':
+ resolution:
+ {
+ integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==
+ }
+ engines: { node: '>= 10' }
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.14':
+ resolution:
+ {
+ integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==
+ }
+ engines: { node: '>= 10' }
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.14':
+ resolution:
+ {
+ integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==
+ }
+ engines: { node: '>= 10' }
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.14':
+ resolution:
+ {
+ integrity: sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==
+ }
+ engines: { node: '>=14.0.0' }
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.14':
+ resolution:
+ {
+ integrity: sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==
+ }
+ engines: { node: '>= 10' }
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.14':
+ resolution:
+ {
+ integrity: sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==
+ }
+ engines: { node: '>= 10' }
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.14':
+ resolution:
+ {
+ integrity: sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==
+ }
+ engines: { node: '>= 10' }
+
+ '@tailwindcss/vite@4.1.14':
+ resolution:
+ {
+ integrity: sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==
+ }
+ peerDependencies:
+ vite: ^5.2.0 || ^6 || ^7
+
+ '@transloadit/prettier-bytes@0.0.7':
+ resolution:
+ {
+ integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==
+ }
+
+ '@types/conventional-commits-parser@5.0.1':
+ resolution:
+ {
+ integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==
+ }
+
+ '@types/estree@1.0.8':
+ resolution:
+ {
+ integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
+ }
+
+ '@types/event-emitter@0.3.5':
+ resolution:
+ {
+ integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==
+ }
+
+ '@types/json-schema@7.0.15':
+ resolution:
+ {
+ integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+ }
+
+ '@types/lodash-es@4.17.12':
+ resolution:
+ {
+ integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==
+ }
+
+ '@types/lodash@4.17.20':
+ resolution:
+ {
+ integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==
+ }
+
+ '@types/node@24.8.1':
+ resolution:
+ {
+ integrity: sha512-alv65KGRadQVfVcG69MuB4IzdYVpRwMG/mq8KWOaoOdyY617P5ivaDiMCGOFDWD2sAn5Q0mR3mRtUOgm99hL9Q==
+ }
+
+ '@types/sortablejs@1.15.8':
+ resolution:
+ {
+ integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==
+ }
+
+ '@types/web-bluetooth@0.0.16':
+ resolution:
+ {
+ integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==
+ }
+
+ '@types/web-bluetooth@0.0.21':
+ resolution:
+ {
+ integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==
+ }
+
+ '@typescript-eslint/eslint-plugin@8.44.1':
+ resolution:
+ {
+ integrity: sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.44.1
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/parser@8.44.1':
+ resolution:
+ {
+ integrity: sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/project-service@8.44.1':
+ resolution:
+ {
+ integrity: sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/scope-manager@8.44.1':
+ resolution:
+ {
+ integrity: sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@typescript-eslint/tsconfig-utils@8.44.1':
+ resolution:
+ {
+ integrity: sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/type-utils@8.44.1':
+ resolution:
+ {
+ integrity: sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/types@8.44.1':
+ resolution:
+ {
+ integrity: sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@typescript-eslint/typescript-estree@8.44.1':
+ resolution:
+ {
+ integrity: sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/utils@8.44.1':
+ resolution:
+ {
+ integrity: sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/visitor-keys@8.44.1':
+ resolution:
+ {
+ integrity: sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ '@uppy/companion-client@2.2.2':
+ resolution:
+ {
+ integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==
+ }
+
+ '@uppy/core@2.3.4':
+ resolution:
+ {
+ integrity: sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==
+ }
+
+ '@uppy/store-default@2.1.1':
+ resolution:
+ {
+ integrity: sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==
+ }
+
+ '@uppy/utils@4.1.3':
+ resolution:
+ {
+ integrity: sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==
+ }
+
+ '@uppy/xhr-upload@2.1.3':
+ resolution:
+ {
+ integrity: sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==
+ }
+ peerDependencies:
+ '@uppy/core': ^2.3.3
+
+ '@vitejs/plugin-vue@6.0.1':
+ resolution:
+ {
+ integrity: sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==
+ }
+ engines: { node: ^20.19.0 || >=22.12.0 }
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+ vue: ^3.2.25
+
+ '@volar/language-core@2.4.23':
+ resolution:
+ {
+ integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==
+ }
+
+ '@volar/source-map@2.4.23':
+ resolution:
+ {
+ integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==
+ }
+
+ '@volar/typescript@2.4.23':
+ resolution:
+ {
+ integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==
+ }
+
+ '@vue/babel-helper-vue-transform-on@1.5.0':
+ resolution:
+ {
+ integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==
+ }
+
+ '@vue/babel-plugin-jsx@1.5.0':
+ resolution:
+ {
+ integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==
+ }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+
+ '@vue/babel-plugin-resolve-type@1.5.0':
+ resolution:
+ {
+ integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==
+ }
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@vue/compiler-core@3.5.22':
+ resolution:
+ {
+ integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==
+ }
+
+ '@vue/compiler-dom@3.5.22':
+ resolution:
+ {
+ integrity: sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==
+ }
+
+ '@vue/compiler-sfc@3.5.22':
+ resolution:
+ {
+ integrity: sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==
+ }
+
+ '@vue/compiler-ssr@3.5.22':
+ resolution:
+ {
+ integrity: sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==
+ }
+
+ '@vue/compiler-vue2@2.7.16':
+ resolution:
+ {
+ integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==
+ }
+
+ '@vue/devtools-api@6.6.4':
+ resolution:
+ {
+ integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==
+ }
+
+ '@vue/devtools-api@7.7.7':
+ resolution:
+ {
+ integrity: sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==
+ }
+
+ '@vue/devtools-core@7.7.7':
+ resolution:
+ {
+ integrity: sha512-9z9TLbfC+AjAi1PQyWX+OErjIaJmdFlbDHcD+cAMYKY6Bh5VlsAtCeGyRMrXwIlMEQPukvnWt3gZBLwTAIMKzQ==
+ }
+ peerDependencies:
+ vue: ^3.0.0
+
+ '@vue/devtools-kit@7.7.7':
+ resolution:
+ {
+ integrity: sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==
+ }
+
+ '@vue/devtools-shared@7.7.7':
+ resolution:
+ {
+ integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==
+ }
+
+ '@vue/language-core@2.1.10':
+ resolution:
+ {
+ integrity: sha512-DAI289d0K3AB5TUG3xDp9OuQ71CnrujQwJrQnfuZDwo6eGNf0UoRlPuaVNO+Zrn65PC3j0oB2i7mNmVPggeGeQ==
+ }
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ '@vue/reactivity@3.5.22':
+ resolution:
+ {
+ integrity: sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==
+ }
+
+ '@vue/runtime-core@3.5.22':
+ resolution:
+ {
+ integrity: sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==
+ }
+
+ '@vue/runtime-dom@3.5.22':
+ resolution:
+ {
+ integrity: sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==
+ }
+
+ '@vue/server-renderer@3.5.22':
+ resolution:
+ {
+ integrity: sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==
+ }
+ peerDependencies:
+ vue: 3.5.22
+
+ '@vue/shared@3.5.22':
+ resolution:
+ {
+ integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==
+ }
+
+ '@vueuse/core@13.9.0':
+ resolution:
+ {
+ integrity: sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==
+ }
+ peerDependencies:
+ vue: ^3.5.0
+
+ '@vueuse/core@9.13.0':
+ resolution:
+ {
+ integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==
+ }
+
+ '@vueuse/metadata@13.9.0':
+ resolution:
+ {
+ integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==
+ }
+
+ '@vueuse/metadata@9.13.0':
+ resolution:
+ {
+ integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==
+ }
+
+ '@vueuse/shared@13.9.0':
+ resolution:
+ {
+ integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==
+ }
+ peerDependencies:
+ vue: ^3.5.0
+
+ '@vueuse/shared@9.13.0':
+ resolution:
+ {
+ integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==
+ }
+
+ '@wangeditor/basic-modules@1.1.7':
+ resolution:
+ {
+ integrity: sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==
+ }
+ peerDependencies:
+ '@wangeditor/core': 1.x
+ dom7: ^3.0.0
+ lodash.throttle: ^4.1.1
+ nanoid: ^3.2.0
+ slate: ^0.72.0
+ snabbdom: ^3.1.0
+
+ '@wangeditor/code-highlight@1.0.3':
+ resolution:
+ {
+ integrity: sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==
+ }
+ peerDependencies:
+ '@wangeditor/core': 1.x
+ dom7: ^3.0.0
+ slate: ^0.72.0
+ snabbdom: ^3.1.0
+
+ '@wangeditor/core@1.1.19':
+ resolution:
+ {
+ integrity: sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==
+ }
+ peerDependencies:
+ '@uppy/core': ^2.1.1
+ '@uppy/xhr-upload': ^2.0.3
+ dom7: ^3.0.0
+ is-hotkey: ^0.2.0
+ lodash.camelcase: ^4.3.0
+ lodash.clonedeep: ^4.5.0
+ lodash.debounce: ^4.0.8
+ lodash.foreach: ^4.5.0
+ lodash.isequal: ^4.5.0
+ lodash.throttle: ^4.1.1
+ lodash.toarray: ^4.4.0
+ nanoid: ^3.2.0
+ slate: ^0.72.0
+ snabbdom: ^3.1.0
+
+ '@wangeditor/editor-for-vue@5.1.12':
+ resolution:
+ {
+ integrity: sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==
+ }
+ peerDependencies:
+ '@wangeditor/editor': '>=5.1.0'
+ vue: ^3.0.5
+
+ '@wangeditor/editor@5.1.23':
+ resolution:
+ {
+ integrity: sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==
+ }
+
+ '@wangeditor/list-module@1.0.5':
+ resolution:
+ {
+ integrity: sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==
+ }
+ peerDependencies:
+ '@wangeditor/core': 1.x
+ dom7: ^3.0.0
+ slate: ^0.72.0
+ snabbdom: ^3.1.0
+
+ '@wangeditor/table-module@1.1.4':
+ resolution:
+ {
+ integrity: sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==
+ }
+ peerDependencies:
+ '@wangeditor/core': 1.x
+ dom7: ^3.0.0
+ lodash.isequal: ^4.5.0
+ lodash.throttle: ^4.1.1
+ nanoid: ^3.2.0
+ slate: ^0.72.0
+ snabbdom: ^3.1.0
+
+ '@wangeditor/upload-image-module@1.0.2':
+ resolution:
+ {
+ integrity: sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==
+ }
+ peerDependencies:
+ '@uppy/core': ^2.0.3
+ '@uppy/xhr-upload': ^2.0.3
+ '@wangeditor/basic-modules': 1.x
+ '@wangeditor/core': 1.x
+ dom7: ^3.0.0
+ lodash.foreach: ^4.5.0
+ slate: ^0.72.0
+ snabbdom: ^3.1.0
+
+ '@wangeditor/video-module@1.1.4':
+ resolution:
+ {
+ integrity: sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==
+ }
+ peerDependencies:
+ '@uppy/core': ^2.1.4
+ '@uppy/xhr-upload': ^2.0.7
+ '@wangeditor/core': 1.x
+ dom7: ^3.0.0
+ nanoid: ^3.2.0
+ slate: ^0.72.0
+ snabbdom: ^3.1.0
+
+ JSONStream@1.3.5:
+ resolution:
+ {
+ integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==
+ }
+ hasBin: true
+
+ acorn-jsx@5.3.2:
+ resolution:
+ {
+ integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+ }
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.15.0:
+ resolution:
+ {
+ integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==
+ }
+ engines: { node: '>=0.4.0' }
+ hasBin: true
+
+ adler-32@1.3.1:
+ resolution:
+ {
+ integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==
+ }
+ engines: { node: '>=0.8' }
+
+ ajv@6.12.6:
+ resolution:
+ {
+ integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ }
+
+ ajv@8.17.1:
+ resolution:
+ {
+ integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==
+ }
+
+ alien-signals@0.2.2:
+ resolution:
+ {
+ integrity: sha512-cZIRkbERILsBOXTQmMrxc9hgpxglstn69zm+F1ARf4aPAzdAFYd6sBq87ErO0Fj3DV94tglcyHG5kQz9nDC/8A==
+ }
+
+ ansi-escapes@4.3.2:
+ resolution:
+ {
+ integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+ }
+ engines: { node: '>=8' }
+
+ ansi-escapes@7.1.1:
+ resolution:
+ {
+ integrity: sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q==
+ }
+ engines: { node: '>=18' }
+
+ ansi-regex@5.0.1:
+ resolution:
+ {
+ integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+ }
+ engines: { node: '>=8' }
+
+ ansi-regex@6.2.2:
+ resolution:
+ {
+ integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==
+ }
+ engines: { node: '>=12' }
+
+ ansi-styles@3.2.1:
+ resolution:
+ {
+ integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ }
+ engines: { node: '>=4' }
+
+ ansi-styles@4.3.0:
+ resolution:
+ {
+ integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ }
+ engines: { node: '>=8' }
+
+ ansi-styles@6.2.3:
+ resolution:
+ {
+ integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==
+ }
+ engines: { node: '>=12' }
+
+ anymatch@3.1.3:
+ resolution:
+ {
+ integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+ }
+ engines: { node: '>= 8' }
+
+ argparse@2.0.1:
+ resolution:
+ {
+ integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+ }
+
+ array-ify@1.0.0:
+ resolution:
+ {
+ integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==
+ }
+
+ array-union@2.1.0:
+ resolution:
+ {
+ integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+ }
+ engines: { node: '>=8' }
+
+ astral-regex@2.0.0:
+ resolution:
+ {
+ integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+ }
+ engines: { node: '>=8' }
+
+ async-validator@4.2.5:
+ resolution:
+ {
+ integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
+ }
+
+ asynckit@0.4.0:
+ resolution:
+ {
+ integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+ }
+
+ at-least-node@1.0.0:
+ resolution:
+ {
+ integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
+ }
+ engines: { node: '>= 4.0.0' }
+
+ axios@1.12.2:
+ resolution:
+ {
+ integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==
+ }
+
+ balanced-match@1.0.2:
+ resolution:
+ {
+ integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+ }
+
+ balanced-match@2.0.0:
+ resolution:
+ {
+ integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
+ }
+
+ base64-js@1.5.1:
+ resolution:
+ {
+ integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+ }
+
+ baseline-browser-mapping@2.8.8:
+ resolution:
+ {
+ integrity: sha512-be0PUaPsQX/gPWWgFsdD+GFzaoig5PXaUC1xLkQiYdDnANU8sMnHoQd8JhbJQuvTWrWLyeFN9Imb5Qtfvr4RrQ==
+ }
+ hasBin: true
+
+ binary-extensions@2.3.0:
+ resolution:
+ {
+ integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
+ }
+ engines: { node: '>=8' }
+
+ birpc@2.6.1:
+ resolution:
+ {
+ integrity: sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==
+ }
+
+ bl@4.1.0:
+ resolution:
+ {
+ integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+ }
+
+ boolbase@1.0.0:
+ resolution:
+ {
+ integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+ }
+
+ brace-expansion@1.1.12:
+ resolution:
+ {
+ integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==
+ }
+
+ brace-expansion@2.0.2:
+ resolution:
+ {
+ integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
+ }
+
+ braces@3.0.3:
+ resolution:
+ {
+ integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+ }
+ engines: { node: '>=8' }
+
+ browserslist@4.26.2:
+ resolution:
+ {
+ integrity: sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==
+ }
+ engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 }
+ hasBin: true
+
+ buffer-from@1.1.2:
+ resolution:
+ {
+ integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+ }
+
+ buffer@5.7.1:
+ resolution:
+ {
+ integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+ }
+
+ bundle-name@4.1.0:
+ resolution:
+ {
+ integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==
+ }
+ engines: { node: '>=18' }
+
+ cacheable@2.0.2:
+ resolution:
+ {
+ integrity: sha512-dWjhLx8RWnPsAWVKwW/wI6OJpQ/hSVb1qS0NUif8TR9vRiSwci7Gey8x04kRU9iAF+Rnbtex5Kjjfg/aB5w8Pg==
+ }
+
+ cachedir@2.3.0:
+ resolution:
+ {
+ integrity: sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==
+ }
+ engines: { node: '>=6' }
+
+ call-bind-apply-helpers@1.0.2:
+ resolution:
+ {
+ integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
+ }
+ engines: { node: '>= 0.4' }
+
+ callsites@3.1.0:
+ resolution:
+ {
+ integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+ }
+ engines: { node: '>=6' }
+
+ caniuse-lite@1.0.30001745:
+ resolution:
+ {
+ integrity: sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==
+ }
+
+ cfb@1.2.2:
+ resolution:
+ {
+ integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==
+ }
+ engines: { node: '>=0.8' }
+
+ chalk@2.4.2:
+ resolution:
+ {
+ integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ }
+ engines: { node: '>=4' }
+
+ chalk@4.1.2:
+ resolution:
+ {
+ integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ }
+ engines: { node: '>=10' }
+
+ chalk@5.6.2:
+ resolution:
+ {
+ integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==
+ }
+ engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 }
+
+ chardet@0.7.0:
+ resolution:
+ {
+ integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+ }
+
+ chokidar@3.6.0:
+ resolution:
+ {
+ integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
+ }
+ engines: { node: '>= 8.10.0' }
+
+ chokidar@4.0.3:
+ resolution:
+ {
+ integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==
+ }
+ engines: { node: '>= 14.16.0' }
+
+ chownr@3.0.0:
+ resolution:
+ {
+ integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==
+ }
+ engines: { node: '>=18' }
+
+ cli-cursor@3.1.0:
+ resolution:
+ {
+ integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+ }
+ engines: { node: '>=8' }
+
+ cli-cursor@5.0.0:
+ resolution:
+ {
+ integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==
+ }
+ engines: { node: '>=18' }
+
+ cli-spinners@2.9.2:
+ resolution:
+ {
+ integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==
+ }
+ engines: { node: '>=6' }
+
+ cli-truncate@4.0.0:
+ resolution:
+ {
+ integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==
+ }
+ engines: { node: '>=18' }
+
+ cli-width@3.0.0:
+ resolution:
+ {
+ integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
+ }
+ engines: { node: '>= 10' }
+
+ cliui@8.0.1:
+ resolution:
+ {
+ integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
+ }
+ engines: { node: '>=12' }
+
+ clone@1.0.4:
+ resolution:
+ {
+ integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==
+ }
+ engines: { node: '>=0.8' }
+
+ codepage@1.15.0:
+ resolution:
+ {
+ integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==
+ }
+ engines: { node: '>=0.8' }
+
+ color-convert@1.9.3:
+ resolution:
+ {
+ integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ }
+
+ color-convert@2.0.1:
+ resolution:
+ {
+ integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ }
+ engines: { node: '>=7.0.0' }
+
+ color-name@1.1.3:
+ resolution:
+ {
+ integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+ }
+
+ color-name@1.1.4:
+ resolution:
+ {
+ integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+ }
+
+ colord@2.9.3:
+ resolution:
+ {
+ integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
+ }
+
+ colorette@2.0.20:
+ resolution:
+ {
+ integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
+ }
+
+ combined-stream@1.0.8:
+ resolution:
+ {
+ integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+ }
+ engines: { node: '>= 0.8' }
+
+ commander@13.1.0:
+ resolution:
+ {
+ integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==
+ }
+ engines: { node: '>=18' }
+
+ commander@2.20.3:
+ resolution:
+ {
+ integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+ }
+
+ commitizen@4.3.1:
+ resolution:
+ {
+ integrity: sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw==
+ }
+ engines: { node: '>= 12' }
+ hasBin: true
+
+ compare-func@2.0.0:
+ resolution:
+ {
+ integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==
+ }
+
+ compute-scroll-into-view@1.0.20:
+ resolution:
+ {
+ integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==
+ }
+
+ concat-map@0.0.1:
+ resolution:
+ {
+ integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+ }
+
+ confbox@0.1.8:
+ resolution:
+ {
+ integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==
+ }
+
+ confbox@0.2.2:
+ resolution:
+ {
+ integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==
+ }
+
+ conventional-changelog-angular@7.0.0:
+ resolution:
+ {
+ integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==
+ }
+ engines: { node: '>=16' }
+
+ conventional-changelog-conventionalcommits@7.0.2:
+ resolution:
+ {
+ integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==
+ }
+ engines: { node: '>=16' }
+
+ conventional-commit-types@3.0.0:
+ resolution:
+ {
+ integrity: sha512-SmmCYnOniSsAa9GqWOeLqc179lfr5TRu5b4QFDkbsrJ5TZjPJx85wtOr3zn+1dbeNiXDKGPbZ72IKbPhLXh/Lg==
+ }
+
+ conventional-commits-parser@5.0.0:
+ resolution:
+ {
+ integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==
+ }
+ engines: { node: '>=16' }
+ hasBin: true
+
+ convert-source-map@2.0.0:
+ resolution:
+ {
+ integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+ }
+
+ copy-anything@3.0.5:
+ resolution:
+ {
+ integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==
+ }
+ engines: { node: '>=12.13' }
+
+ core-js@3.45.1:
+ resolution:
+ {
+ integrity: sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==
+ }
+
+ cosmiconfig-typescript-loader@6.1.0:
+ resolution:
+ {
+ integrity: sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==
+ }
+ engines: { node: '>=v18' }
+ peerDependencies:
+ '@types/node': '*'
+ cosmiconfig: '>=9'
+ typescript: '>=5'
+
+ cosmiconfig@9.0.0:
+ resolution:
+ {
+ integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==
+ }
+ engines: { node: '>=14' }
+ peerDependencies:
+ typescript: '>=4.9.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ crc-32@1.2.2:
+ resolution:
+ {
+ integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
+ }
+ engines: { node: '>=0.8' }
+ hasBin: true
+
+ cross-spawn@7.0.6:
+ resolution:
+ {
+ integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+ }
+ engines: { node: '>= 8' }
+
+ crypto-js@4.2.0:
+ resolution:
+ {
+ integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==
+ }
+
+ css-functions-list@3.2.3:
+ resolution:
+ {
+ integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==
+ }
+ engines: { node: '>=12 || >=16' }
+
+ css-tree@3.1.0:
+ resolution:
+ {
+ integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==
+ }
+ engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 }
+
+ cssesc@3.0.0:
+ resolution:
+ {
+ integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
+ }
+ engines: { node: '>=4' }
+ hasBin: true
+
+ csstype@3.1.3:
+ resolution:
+ {
+ integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+ }
+
+ cz-conventional-changelog@3.3.0:
+ resolution:
+ {
+ integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==
+ }
+ engines: { node: '>= 10' }
+
+ cz-git@1.12.0:
+ resolution:
+ {
+ integrity: sha512-LaZ+8whPPUOo6Y0Zy4nIbf6JOleV3ejp41sT6N4RPKiKKA+ICWf4ueeIlxIO8b6JtdlDxRzHH/EcRji07nDxcg==
+ }
+ engines: { node: '>=v12.20.0' }
+
+ d@1.0.2:
+ resolution:
+ {
+ integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==
+ }
+ engines: { node: '>=0.12' }
+
+ danmu.js@1.1.13:
+ resolution:
+ {
+ integrity: sha512-knFd0/cB2HA4FFWiA7eB2suc5vCvoHdqio33FyyCSfP7C+1A+zQcTvnvwfxaZhrxsGj4qaQI2I8XiTqedRaVmg==
+ }
+
+ dargs@8.1.0:
+ resolution:
+ {
+ integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==
+ }
+ engines: { node: '>=12' }
+
+ dayjs@1.11.18:
+ resolution:
+ {
+ integrity: sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==
+ }
+
+ de-indent@1.0.2:
+ resolution:
+ {
+ integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
+ }
+
+ debug@4.4.3:
+ resolution:
+ {
+ integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
+ }
+ engines: { node: '>=6.0' }
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ dedent@0.7.0:
+ resolution:
+ {
+ integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==
+ }
+
+ deep-is@0.1.4:
+ resolution:
+ {
+ integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+ }
+
+ deep-pick-omit@1.2.1:
+ resolution:
+ {
+ integrity: sha512-2J6Kc/m3irCeqVG42T+SaUMesaK7oGWaedGnQQK/+O0gYc+2SP5bKh/KKTE7d7SJ+GCA9UUE1GRzh6oDe0EnGw==
+ }
+
+ default-browser-id@5.0.0:
+ resolution:
+ {
+ integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==
+ }
+ engines: { node: '>=18' }
+
+ default-browser@5.2.1:
+ resolution:
+ {
+ integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==
+ }
+ engines: { node: '>=18' }
+
+ defaults@1.0.4:
+ resolution:
+ {
+ integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==
+ }
+
+ define-lazy-prop@2.0.0:
+ resolution:
+ {
+ integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
+ }
+ engines: { node: '>=8' }
+
+ define-lazy-prop@3.0.0:
+ resolution:
+ {
+ integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==
+ }
+ engines: { node: '>=12' }
+
+ defu@6.1.4:
+ resolution:
+ {
+ integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==
+ }
+
+ delayed-stream@1.0.0:
+ resolution:
+ {
+ integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+ }
+ engines: { node: '>=0.4.0' }
+
+ delegate@3.2.0:
+ resolution:
+ {
+ integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
+ }
+
+ destr@2.0.5:
+ resolution:
+ {
+ integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==
+ }
+
+ detect-file@1.0.0:
+ resolution:
+ {
+ integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==
+ }
+ engines: { node: '>=0.10.0' }
+
+ detect-indent@6.1.0:
+ resolution:
+ {
+ integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==
+ }
+ engines: { node: '>=8' }
+
+ detect-libc@1.0.3:
+ resolution:
+ {
+ integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
+ }
+ engines: { node: '>=0.10' }
+ hasBin: true
+
+ detect-libc@2.1.2:
+ resolution:
+ {
+ integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
+ }
+ engines: { node: '>=8' }
+
+ dir-glob@3.0.1:
+ resolution:
+ {
+ integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+ }
+ engines: { node: '>=8' }
+
+ dom-serializer@2.0.0:
+ resolution:
+ {
+ integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
+ }
+
+ dom7@3.0.0:
+ resolution:
+ {
+ integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==
+ }
+
+ domelementtype@2.3.0:
+ resolution:
+ {
+ integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+ }
+
+ domhandler@5.0.3:
+ resolution:
+ {
+ integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
+ }
+ engines: { node: '>= 4' }
+
+ domutils@3.2.2:
+ resolution:
+ {
+ integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==
+ }
+
+ dot-prop@5.3.0:
+ resolution:
+ {
+ integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==
+ }
+ engines: { node: '>=8' }
+
+ downloadjs@1.4.7:
+ resolution:
+ {
+ integrity: sha512-LN1gO7+u9xjU5oEScGFKvXhYf7Y/empUIIEAGBs1LzUq/rg5duiDrkuH5A2lQGd5jfMOb9X9usDa2oVXwJ0U/Q==
+ }
+
+ dunder-proto@1.0.1:
+ resolution:
+ {
+ integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
+ }
+ engines: { node: '>= 0.4' }
+
+ echarts@6.0.0:
+ resolution:
+ {
+ integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==
+ }
+
+ electron-to-chromium@1.5.227:
+ resolution:
+ {
+ integrity: sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==
+ }
+
+ element-plus@2.11.4:
+ resolution:
+ {
+ integrity: sha512-sLq+Ypd0cIVilv8wGGMEGvzRVBBsRpJjnAS5PsI/1JU1COZXqzH3N1UYMUc/HCdvdjf6dfrBy80Sj7KcACsT7w==
+ }
+ peerDependencies:
+ vue: ^3.2.0
+
+ emoji-regex@10.5.0:
+ resolution:
+ {
+ integrity: sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==
+ }
+
+ emoji-regex@8.0.0:
+ resolution:
+ {
+ integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+ }
+
+ enhanced-resolve@5.18.3:
+ resolution:
+ {
+ integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==
+ }
+ engines: { node: '>=10.13.0' }
+
+ entities@4.5.0:
+ resolution:
+ {
+ integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
+ }
+ engines: { node: '>=0.12' }
+
+ env-paths@2.2.1:
+ resolution:
+ {
+ integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
+ }
+ engines: { node: '>=6' }
+
+ environment@1.1.0:
+ resolution:
+ {
+ integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==
+ }
+ engines: { node: '>=18' }
+
+ error-ex@1.3.4:
+ resolution:
+ {
+ integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==
+ }
+
+ error-stack-parser-es@0.1.5:
+ resolution:
+ {
+ integrity: sha512-xHku1X40RO+fO8yJ8Wh2f2rZWVjqyhb1zgq1yZ8aZRQkv6OOKhKWRUaht3eSCUbAOBaKIgM+ykwFLE+QUxgGeg==
+ }
+
+ es-define-property@1.0.1:
+ resolution:
+ {
+ integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
+ }
+ engines: { node: '>= 0.4' }
+
+ es-errors@1.3.0:
+ resolution:
+ {
+ integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+ }
+ engines: { node: '>= 0.4' }
+
+ es-module-lexer@1.7.0:
+ resolution:
+ {
+ integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==
+ }
+
+ es-object-atoms@1.1.1:
+ resolution:
+ {
+ integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
+ }
+ engines: { node: '>= 0.4' }
+
+ es-set-tostringtag@2.1.0:
+ resolution:
+ {
+ integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==
+ }
+ engines: { node: '>= 0.4' }
+
+ es5-ext@0.10.64:
+ resolution:
+ {
+ integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==
+ }
+ engines: { node: '>=0.10' }
+
+ es6-iterator@2.0.3:
+ resolution:
+ {
+ integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
+ }
+
+ es6-symbol@3.1.4:
+ resolution:
+ {
+ integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==
+ }
+ engines: { node: '>=0.12' }
+
+ esbuild@0.25.10:
+ resolution:
+ {
+ integrity: sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==
+ }
+ engines: { node: '>=18' }
+ hasBin: true
+
+ escalade@3.2.0:
+ resolution:
+ {
+ integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+ }
+ engines: { node: '>=6' }
+
+ escape-html@1.0.3:
+ resolution:
+ {
+ integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+ }
+
+ escape-string-regexp@1.0.5:
+ resolution:
+ {
+ integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+ }
+ engines: { node: '>=0.8.0' }
+
+ escape-string-regexp@4.0.0:
+ resolution:
+ {
+ integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+ }
+ engines: { node: '>=10' }
+
+ escape-string-regexp@5.0.0:
+ resolution:
+ {
+ integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
+ }
+ engines: { node: '>=12' }
+
+ eslint-config-prettier@9.1.2:
+ resolution:
+ {
+ integrity: sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==
+ }
+ hasBin: true
+ peerDependencies:
+ eslint: '>=7.0.0'
+
+ eslint-plugin-prettier@5.5.4:
+ resolution:
+ {
+ integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==
+ }
+ engines: { node: ^14.18.0 || >=16.0.0 }
+ peerDependencies:
+ '@types/eslint': '>=8.0.0'
+ eslint: '>=8.0.0'
+ eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0'
+ prettier: '>=3.0.0'
+ peerDependenciesMeta:
+ '@types/eslint':
+ optional: true
+ eslint-config-prettier:
+ optional: true
+
+ eslint-plugin-vue@9.33.0:
+ resolution:
+ {
+ integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==
+ }
+ engines: { node: ^14.17.0 || >=16.0.0 }
+ peerDependencies:
+ eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0
+
+ eslint-scope@7.2.2:
+ resolution:
+ {
+ integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==
+ }
+ engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
+
+ eslint-scope@8.4.0:
+ resolution:
+ {
+ integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ eslint-visitor-keys@3.4.3:
+ resolution:
+ {
+ integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+ }
+ engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
+
+ eslint-visitor-keys@4.2.1:
+ resolution:
+ {
+ integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ eslint@9.36.0:
+ resolution:
+ {
+ integrity: sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ esniff@2.0.1:
+ resolution:
+ {
+ integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
+ }
+ engines: { node: '>=0.10' }
+
+ espree@10.4.0:
+ resolution:
+ {
+ integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+
+ espree@9.6.1:
+ resolution:
+ {
+ integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
+ }
+ engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 }
+
+ esquery@1.6.0:
+ resolution:
+ {
+ integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
+ }
+ engines: { node: '>=0.10' }
+
+ esrecurse@4.3.0:
+ resolution:
+ {
+ integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ }
+ engines: { node: '>=4.0' }
+
+ estraverse@5.3.0:
+ resolution:
+ {
+ integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+ }
+ engines: { node: '>=4.0' }
+
+ estree-walker@2.0.2:
+ resolution:
+ {
+ integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+ }
+
+ estree-walker@3.0.3:
+ resolution:
+ {
+ integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==
+ }
+
+ esutils@2.0.3:
+ resolution:
+ {
+ integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+ }
+ engines: { node: '>=0.10.0' }
+
+ event-emitter@0.3.5:
+ resolution:
+ {
+ integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
+ }
+
+ eventemitter3@4.0.7:
+ resolution:
+ {
+ integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+ }
+
+ eventemitter3@5.0.1:
+ resolution:
+ {
+ integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+ }
+
+ execa@8.0.1:
+ resolution:
+ {
+ integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==
+ }
+ engines: { node: '>=16.17' }
+
+ execa@9.6.0:
+ resolution:
+ {
+ integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==
+ }
+ engines: { node: ^18.19.0 || >=20.5.0 }
+
+ expand-tilde@2.0.2:
+ resolution:
+ {
+ integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==
+ }
+ engines: { node: '>=0.10.0' }
+
+ exsolve@1.0.7:
+ resolution:
+ {
+ integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==
+ }
+
+ ext@1.7.0:
+ resolution:
+ {
+ integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
+ }
+
+ external-editor@3.1.0:
+ resolution:
+ {
+ integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
+ }
+ engines: { node: '>=4' }
+
+ fast-deep-equal@3.1.3:
+ resolution:
+ {
+ integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+ }
+
+ fast-diff@1.3.0:
+ resolution:
+ {
+ integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
+ }
+
+ fast-glob@3.3.3:
+ resolution:
+ {
+ integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
+ }
+ engines: { node: '>=8.6.0' }
+
+ fast-json-stable-stringify@2.1.0:
+ resolution:
+ {
+ integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+ }
+
+ fast-levenshtein@2.0.6:
+ resolution:
+ {
+ integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+ }
+
+ fast-uri@3.1.0:
+ resolution:
+ {
+ integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==
+ }
+
+ fastest-levenshtein@1.0.16:
+ resolution:
+ {
+ integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
+ }
+ engines: { node: '>= 4.9.1' }
+
+ fastq@1.19.1:
+ resolution:
+ {
+ integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==
+ }
+
+ fdir@6.5.0:
+ resolution:
+ {
+ integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
+ }
+ engines: { node: '>=12.0.0' }
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ figures@3.2.0:
+ resolution:
+ {
+ integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
+ }
+ engines: { node: '>=8' }
+
+ figures@6.1.0:
+ resolution:
+ {
+ integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==
+ }
+ engines: { node: '>=18' }
+
+ file-entry-cache@10.1.4:
+ resolution:
+ {
+ integrity: sha512-5XRUFc0WTtUbjfGzEwXc42tiGxQHBmtbUG1h9L2apu4SulCGN3Hqm//9D6FAolf8MYNL7f/YlJl9vy08pj5JuA==
+ }
+
+ file-entry-cache@8.0.0:
+ resolution:
+ {
+ integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
+ }
+ engines: { node: '>=16.0.0' }
+
+ file-saver@2.0.5:
+ resolution:
+ {
+ integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
+ }
+
+ fill-range@7.1.1:
+ resolution:
+ {
+ integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+ }
+ engines: { node: '>=8' }
+
+ find-node-modules@2.1.3:
+ resolution:
+ {
+ integrity: sha512-UC2I2+nx1ZuOBclWVNdcnbDR5dlrOdVb7xNjmT/lHE+LsgztWks3dG7boJ37yTS/venXw84B/mAW9uHVoC5QRg==
+ }
+
+ find-root@1.1.0:
+ resolution:
+ {
+ integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
+ }
+
+ find-up@5.0.0:
+ resolution:
+ {
+ integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ }
+ engines: { node: '>=10' }
+
+ find-up@7.0.0:
+ resolution:
+ {
+ integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==
+ }
+ engines: { node: '>=18' }
+
+ findup-sync@4.0.0:
+ resolution:
+ {
+ integrity: sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==
+ }
+ engines: { node: '>= 8' }
+
+ flat-cache@4.0.1:
+ resolution:
+ {
+ integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
+ }
+ engines: { node: '>=16' }
+
+ flat-cache@6.1.14:
+ resolution:
+ {
+ integrity: sha512-ExZSCSV9e7v/Zt7RzCbX57lY2dnPdxzU/h3UE6WJ6NtEMfwBd8jmi1n4otDEUfz+T/R+zxrFDpICFdjhD3H/zw==
+ }
+
+ flatted@3.3.3:
+ resolution:
+ {
+ integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
+ }
+
+ follow-redirects@1.15.11:
+ resolution:
+ {
+ integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==
+ }
+ engines: { node: '>=4.0' }
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+
+ form-data@4.0.4:
+ resolution:
+ {
+ integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==
+ }
+ engines: { node: '>= 6' }
+
+ frac@1.1.2:
+ resolution:
+ {
+ integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
+ }
+ engines: { node: '>=0.8' }
+
+ fs-extra@10.1.0:
+ resolution:
+ {
+ integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
+ }
+ engines: { node: '>=12' }
+
+ fs-extra@11.3.2:
+ resolution:
+ {
+ integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==
+ }
+ engines: { node: '>=14.14' }
+
+ fs-extra@9.1.0:
+ resolution:
+ {
+ integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
+ }
+ engines: { node: '>=10' }
+
+ fs.realpath@1.0.0:
+ resolution:
+ {
+ integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+ }
+
+ fsevents@2.3.3:
+ resolution:
+ {
+ integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+ }
+ engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 }
+ os: [darwin]
+
+ function-bind@1.1.2:
+ resolution:
+ {
+ integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+ }
+
+ gensync@1.0.0-beta.2:
+ resolution:
+ {
+ integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+ }
+ engines: { node: '>=6.9.0' }
+
+ get-caller-file@2.0.5:
+ resolution:
+ {
+ integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+ }
+ engines: { node: 6.* || 8.* || >= 10.* }
+
+ get-east-asian-width@1.4.0:
+ resolution:
+ {
+ integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==
+ }
+ engines: { node: '>=18' }
+
+ get-intrinsic@1.3.0:
+ resolution:
+ {
+ integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
+ }
+ engines: { node: '>= 0.4' }
+
+ get-proto@1.0.1:
+ resolution:
+ {
+ integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
+ }
+ engines: { node: '>= 0.4' }
+
+ get-stream@8.0.1:
+ resolution:
+ {
+ integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==
+ }
+ engines: { node: '>=16' }
+
+ get-stream@9.0.1:
+ resolution:
+ {
+ integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==
+ }
+ engines: { node: '>=18' }
+
+ get-tsconfig@4.10.1:
+ resolution:
+ {
+ integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==
+ }
+
+ git-raw-commits@4.0.0:
+ resolution:
+ {
+ integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==
+ }
+ engines: { node: '>=16' }
+ hasBin: true
+
+ glob-parent@5.1.2:
+ resolution:
+ {
+ integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ }
+ engines: { node: '>= 6' }
+
+ glob-parent@6.0.2:
+ resolution:
+ {
+ integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ }
+ engines: { node: '>=10.13.0' }
+
+ glob@7.2.3:
+ resolution:
+ {
+ integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ }
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ global-directory@4.0.1:
+ resolution:
+ {
+ integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==
+ }
+ engines: { node: '>=18' }
+
+ global-modules@1.0.0:
+ resolution:
+ {
+ integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==
+ }
+ engines: { node: '>=0.10.0' }
+
+ global-modules@2.0.0:
+ resolution:
+ {
+ integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==
+ }
+ engines: { node: '>=6' }
+
+ global-prefix@1.0.2:
+ resolution:
+ {
+ integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==
+ }
+ engines: { node: '>=0.10.0' }
+
+ global-prefix@3.0.0:
+ resolution:
+ {
+ integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==
+ }
+ engines: { node: '>=6' }
+
+ globals@13.24.0:
+ resolution:
+ {
+ integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==
+ }
+ engines: { node: '>=8' }
+
+ globals@14.0.0:
+ resolution:
+ {
+ integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
+ }
+ engines: { node: '>=18' }
+
+ globals@15.15.0:
+ resolution:
+ {
+ integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==
+ }
+ engines: { node: '>=18' }
+
+ globby@11.1.0:
+ resolution:
+ {
+ integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+ }
+ engines: { node: '>=10' }
+
+ globjoin@0.1.4:
+ resolution:
+ {
+ integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==
+ }
+
+ gopd@1.2.0:
+ resolution:
+ {
+ integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
+ }
+ engines: { node: '>= 0.4' }
+
+ graceful-fs@4.2.11:
+ resolution:
+ {
+ integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+ }
+
+ graphemer@1.4.0:
+ resolution:
+ {
+ integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+ }
+
+ has-flag@3.0.0:
+ resolution:
+ {
+ integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+ }
+ engines: { node: '>=4' }
+
+ has-flag@4.0.0:
+ resolution:
+ {
+ integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+ }
+ engines: { node: '>=8' }
+
+ has-symbols@1.1.0:
+ resolution:
+ {
+ integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
+ }
+ engines: { node: '>= 0.4' }
+
+ has-tostringtag@1.0.2:
+ resolution:
+ {
+ integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==
+ }
+ engines: { node: '>= 0.4' }
+
+ hasown@2.0.2:
+ resolution:
+ {
+ integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+ }
+ engines: { node: '>= 0.4' }
+
+ he@1.2.0:
+ resolution:
+ {
+ integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+ }
+ hasBin: true
+
+ highlight.js@11.11.1:
+ resolution:
+ {
+ integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==
+ }
+ engines: { node: '>=12.0.0' }
+
+ homedir-polyfill@1.0.3:
+ resolution:
+ {
+ integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==
+ }
+ engines: { node: '>=0.10.0' }
+
+ hookable@5.5.3:
+ resolution:
+ {
+ integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==
+ }
+
+ hookified@1.12.1:
+ resolution:
+ {
+ integrity: sha512-xnKGl+iMIlhrZmGHB729MqlmPoWBznctSQTYCpFKqNsCgimJQmithcW0xSQMMFzYnV2iKUh25alswn6epgxS0Q==
+ }
+
+ html-tags@3.3.1:
+ resolution:
+ {
+ integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==
+ }
+ engines: { node: '>=8' }
+
+ html-void-elements@2.0.1:
+ resolution:
+ {
+ integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==
+ }
+
+ htmlparser2@8.0.2:
+ resolution:
+ {
+ integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==
+ }
+
+ human-signals@5.0.0:
+ resolution:
+ {
+ integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==
+ }
+ engines: { node: '>=16.17.0' }
+
+ human-signals@8.0.1:
+ resolution:
+ {
+ integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==
+ }
+ engines: { node: '>=18.18.0' }
+
+ husky@9.1.7:
+ resolution:
+ {
+ integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==
+ }
+ engines: { node: '>=18' }
+ hasBin: true
+
+ i18next@20.6.1:
+ resolution:
+ {
+ integrity: sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==
+ }
+
+ iconv-lite@0.4.24:
+ resolution:
+ {
+ integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ }
+ engines: { node: '>=0.10.0' }
+
+ ieee754@1.2.1:
+ resolution:
+ {
+ integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+ }
+
+ ignore@5.3.2:
+ resolution:
+ {
+ integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+ }
+ engines: { node: '>= 4' }
+
+ ignore@7.0.5:
+ resolution:
+ {
+ integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==
+ }
+ engines: { node: '>= 4' }
+
+ immer@9.0.21:
+ resolution:
+ {
+ integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==
+ }
+
+ immutable@5.1.3:
+ resolution:
+ {
+ integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==
+ }
+
+ import-fresh@3.3.1:
+ resolution:
+ {
+ integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==
+ }
+ engines: { node: '>=6' }
+
+ import-meta-resolve@4.2.0:
+ resolution:
+ {
+ integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==
+ }
+
+ imurmurhash@0.1.4:
+ resolution:
+ {
+ integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+ }
+ engines: { node: '>=0.8.19' }
+
+ inflight@1.0.6:
+ resolution:
+ {
+ integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ }
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution:
+ {
+ integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+ }
+
+ ini@1.3.8:
+ resolution:
+ {
+ integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
+ }
+
+ ini@4.1.1:
+ resolution:
+ {
+ integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==
+ }
+ engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 }
+
+ inquirer@8.2.5:
+ resolution:
+ {
+ integrity: sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==
+ }
+ engines: { node: '>=12.0.0' }
+
+ is-arrayish@0.2.1:
+ resolution:
+ {
+ integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+ }
+
+ is-binary-path@2.1.0:
+ resolution:
+ {
+ integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+ }
+ engines: { node: '>=8' }
+
+ is-docker@2.2.1:
+ resolution:
+ {
+ integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+ }
+ engines: { node: '>=8' }
+ hasBin: true
+
+ is-docker@3.0.0:
+ resolution:
+ {
+ integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==
+ }
+ engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
+ hasBin: true
+
+ is-extglob@2.1.1:
+ resolution:
+ {
+ integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+ }
+ engines: { node: '>=0.10.0' }
+
+ is-fullwidth-code-point@3.0.0:
+ resolution:
+ {
+ integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+ }
+ engines: { node: '>=8' }
+
+ is-fullwidth-code-point@4.0.0:
+ resolution:
+ {
+ integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==
+ }
+ engines: { node: '>=12' }
+
+ is-fullwidth-code-point@5.1.0:
+ resolution:
+ {
+ integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==
+ }
+ engines: { node: '>=18' }
+
+ is-glob@4.0.3:
+ resolution:
+ {
+ integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ }
+ engines: { node: '>=0.10.0' }
+
+ is-hotkey@0.2.0:
+ resolution:
+ {
+ integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==
+ }
+
+ is-inside-container@1.0.0:
+ resolution:
+ {
+ integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==
+ }
+ engines: { node: '>=14.16' }
+ hasBin: true
+
+ is-interactive@1.0.0:
+ resolution:
+ {
+ integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+ }
+ engines: { node: '>=8' }
+
+ is-number@7.0.0:
+ resolution:
+ {
+ integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+ }
+ engines: { node: '>=0.12.0' }
+
+ is-obj@2.0.0:
+ resolution:
+ {
+ integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+ }
+ engines: { node: '>=8' }
+
+ is-plain-obj@4.1.0:
+ resolution:
+ {
+ integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
+ }
+ engines: { node: '>=12' }
+
+ is-plain-object@5.0.0:
+ resolution:
+ {
+ integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
+ }
+ engines: { node: '>=0.10.0' }
+
+ is-stream@3.0.0:
+ resolution:
+ {
+ integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==
+ }
+ engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
+
+ is-stream@4.0.1:
+ resolution:
+ {
+ integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==
+ }
+ engines: { node: '>=18' }
+
+ is-text-path@2.0.0:
+ resolution:
+ {
+ integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==
+ }
+ engines: { node: '>=8' }
+
+ is-unicode-supported@0.1.0:
+ resolution:
+ {
+ integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+ }
+ engines: { node: '>=10' }
+
+ is-unicode-supported@2.1.0:
+ resolution:
+ {
+ integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==
+ }
+ engines: { node: '>=18' }
+
+ is-url@1.2.4:
+ resolution:
+ {
+ integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==
+ }
+
+ is-utf8@0.2.1:
+ resolution:
+ {
+ integrity: sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==
+ }
+
+ is-what@4.1.16:
+ resolution:
+ {
+ integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==
+ }
+ engines: { node: '>=12.13' }
+
+ is-windows@1.0.2:
+ resolution:
+ {
+ integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
+ }
+ engines: { node: '>=0.10.0' }
+
+ is-wsl@2.2.0:
+ resolution:
+ {
+ integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+ }
+ engines: { node: '>=8' }
+
+ is-wsl@3.1.0:
+ resolution:
+ {
+ integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==
+ }
+ engines: { node: '>=16' }
+
+ isexe@2.0.0:
+ resolution:
+ {
+ integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+ }
+
+ jiti@2.6.0:
+ resolution:
+ {
+ integrity: sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==
+ }
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution:
+ {
+ integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+ }
+
+ js-tokens@9.0.1:
+ resolution:
+ {
+ integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==
+ }
+
+ js-yaml@4.1.0:
+ resolution:
+ {
+ integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ }
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution:
+ {
+ integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==
+ }
+ engines: { node: '>=6' }
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution:
+ {
+ integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+ }
+
+ json-parse-even-better-errors@2.3.1:
+ resolution:
+ {
+ integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+ }
+
+ json-schema-traverse@0.4.1:
+ resolution:
+ {
+ integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+ }
+
+ json-schema-traverse@1.0.0:
+ resolution:
+ {
+ integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+ }
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution:
+ {
+ integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+ }
+
+ json5@2.2.3:
+ resolution:
+ {
+ integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+ }
+ engines: { node: '>=6' }
+ hasBin: true
+
+ jsonfile@6.2.0:
+ resolution:
+ {
+ integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==
+ }
+
+ jsonparse@1.3.1:
+ resolution:
+ {
+ integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==
+ }
+ engines: { '0': node >= 0.2.0 }
+
+ keyv@4.5.4:
+ resolution:
+ {
+ integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+ }
+
+ keyv@5.5.3:
+ resolution:
+ {
+ integrity: sha512-h0Un1ieD+HUrzBH6dJXhod3ifSghk5Hw/2Y4/KHBziPlZecrFyE9YOTPU6eOs0V9pYl8gOs86fkr/KN8lUX39A==
+ }
+
+ kind-of@6.0.3:
+ resolution:
+ {
+ integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+ }
+ engines: { node: '>=0.10.0' }
+
+ known-css-properties@0.36.0:
+ resolution:
+ {
+ integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==
+ }
+
+ known-css-properties@0.37.0:
+ resolution:
+ {
+ integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==
+ }
+
+ kolorist@1.8.0:
+ resolution:
+ {
+ integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==
+ }
+
+ levn@0.4.1:
+ resolution:
+ {
+ integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ }
+ engines: { node: '>= 0.8.0' }
+
+ lightningcss-darwin-arm64@1.30.1:
+ resolution:
+ {
+ integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.1:
+ resolution:
+ {
+ integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.1:
+ resolution:
+ {
+ integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ resolution:
+ {
+ integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ resolution:
+ {
+ integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ resolution:
+ {
+ integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ resolution:
+ {
+ integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.30.1:
+ resolution:
+ {
+ integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ resolution:
+ {
+ integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ resolution:
+ {
+ integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==
+ }
+ engines: { node: '>= 12.0.0' }
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.1:
+ resolution:
+ {
+ integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==
+ }
+ engines: { node: '>= 12.0.0' }
+
+ lilconfig@3.1.3:
+ resolution:
+ {
+ integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==
+ }
+ engines: { node: '>=14' }
+
+ lines-and-columns@1.2.4:
+ resolution:
+ {
+ integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+ }
+
+ lint-staged@15.5.2:
+ resolution:
+ {
+ integrity: sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w==
+ }
+ engines: { node: '>=18.12.0' }
+ hasBin: true
+
+ listr2@8.3.3:
+ resolution:
+ {
+ integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==
+ }
+ engines: { node: '>=18.0.0' }
+
+ local-pkg@1.1.2:
+ resolution:
+ {
+ integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==
+ }
+ engines: { node: '>=14' }
+
+ locate-path@6.0.0:
+ resolution:
+ {
+ integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ }
+ engines: { node: '>=10' }
+
+ locate-path@7.2.0:
+ resolution:
+ {
+ integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==
+ }
+ engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
+
+ lodash-es@4.17.21:
+ resolution:
+ {
+ integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
+ }
+
+ lodash-unified@1.0.3:
+ resolution:
+ {
+ integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==
+ }
+ peerDependencies:
+ '@types/lodash-es': '*'
+ lodash: '*'
+ lodash-es: '*'
+
+ lodash.camelcase@4.3.0:
+ resolution:
+ {
+ integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==
+ }
+
+ lodash.clonedeep@4.5.0:
+ resolution:
+ {
+ integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+ }
+
+ lodash.debounce@4.0.8:
+ resolution:
+ {
+ integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+ }
+
+ lodash.foreach@4.5.0:
+ resolution:
+ {
+ integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==
+ }
+
+ lodash.isequal@4.5.0:
+ resolution:
+ {
+ integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
+ }
+ deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
+
+ lodash.isplainobject@4.0.6:
+ resolution:
+ {
+ integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
+ }
+
+ lodash.kebabcase@4.1.1:
+ resolution:
+ {
+ integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==
+ }
+
+ lodash.map@4.6.0:
+ resolution:
+ {
+ integrity: sha512-worNHGKLDetmcEYDvh2stPCrrQRkP20E4l0iIS7F8EvzMqBBi7ltvFN5m1HvTf1P7Jk1txKhvFcmYsCr8O2F1Q==
+ }
+
+ lodash.merge@4.6.2:
+ resolution:
+ {
+ integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+ }
+
+ lodash.mergewith@4.6.2:
+ resolution:
+ {
+ integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
+ }
+
+ lodash.snakecase@4.1.1:
+ resolution:
+ {
+ integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
+ }
+
+ lodash.startcase@4.4.0:
+ resolution:
+ {
+ integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==
+ }
+
+ lodash.throttle@4.1.1:
+ resolution:
+ {
+ integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==
+ }
+
+ lodash.toarray@4.4.0:
+ resolution:
+ {
+ integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==
+ }
+
+ lodash.truncate@4.4.2:
+ resolution:
+ {
+ integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
+ }
+
+ lodash.uniq@4.5.0:
+ resolution:
+ {
+ integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
+ }
+
+ lodash.upperfirst@4.3.1:
+ resolution:
+ {
+ integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==
+ }
+
+ lodash@4.17.21:
+ resolution:
+ {
+ integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+ }
+
+ log-symbols@4.1.0:
+ resolution:
+ {
+ integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
+ }
+ engines: { node: '>=10' }
+
+ log-update@6.1.0:
+ resolution:
+ {
+ integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==
+ }
+ engines: { node: '>=18' }
+
+ longest@2.0.1:
+ resolution:
+ {
+ integrity: sha512-Ajzxb8CM6WAnFjgiloPsI3bF+WCxcvhdIG3KNA2KN962+tdBsHcuQ4k4qX/EcS/2CRkcc0iAkR956Nib6aXU/Q==
+ }
+ engines: { node: '>=0.10.0' }
+
+ lru-cache@5.1.1:
+ resolution:
+ {
+ integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+ }
+
+ magic-string@0.30.19:
+ resolution:
+ {
+ integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==
+ }
+
+ math-intrinsics@1.1.0:
+ resolution:
+ {
+ integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
+ }
+ engines: { node: '>= 0.4' }
+
+ mathml-tag-names@2.1.3:
+ resolution:
+ {
+ integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==
+ }
+
+ mdn-data@2.12.2:
+ resolution:
+ {
+ integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==
+ }
+
+ mdn-data@2.24.0:
+ resolution:
+ {
+ integrity: sha512-i97fklrJl03tL1tdRVw0ZfLLvuDsdb6wxL+TrJ+PKkCbLrp2PCu2+OYdCKychIUm19nSM/35S6qz7pJpnXttoA==
+ }
+
+ memoize-one@6.0.0:
+ resolution:
+ {
+ integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
+ }
+
+ meow@12.1.1:
+ resolution:
+ {
+ integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==
+ }
+ engines: { node: '>=16.10' }
+
+ meow@13.2.0:
+ resolution:
+ {
+ integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==
+ }
+ engines: { node: '>=18' }
+
+ merge-stream@2.0.0:
+ resolution:
+ {
+ integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+ }
+
+ merge2@1.4.1:
+ resolution:
+ {
+ integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+ }
+ engines: { node: '>= 8' }
+
+ merge@2.1.1:
+ resolution:
+ {
+ integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==
+ }
+
+ micromatch@4.0.8:
+ resolution:
+ {
+ integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
+ }
+ engines: { node: '>=8.6' }
+
+ mime-db@1.52.0:
+ resolution:
+ {
+ integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+ }
+ engines: { node: '>= 0.6' }
+
+ mime-match@1.0.2:
+ resolution:
+ {
+ integrity: sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==
+ }
+
+ mime-types@2.1.35:
+ resolution:
+ {
+ integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ }
+ engines: { node: '>= 0.6' }
+
+ mimic-fn@2.1.0:
+ resolution:
+ {
+ integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+ }
+ engines: { node: '>=6' }
+
+ mimic-fn@4.0.0:
+ resolution:
+ {
+ integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
+ }
+ engines: { node: '>=12' }
+
+ mimic-function@5.0.1:
+ resolution:
+ {
+ integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==
+ }
+ engines: { node: '>=18' }
+
+ minimatch@3.1.2:
+ resolution:
+ {
+ integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ }
+
+ minimatch@9.0.5:
+ resolution:
+ {
+ integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+ }
+ engines: { node: '>=16 || 14 >=14.17' }
+
+ minimist@1.2.7:
+ resolution:
+ {
+ integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
+ }
+
+ minimist@1.2.8:
+ resolution:
+ {
+ integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+ }
+
+ minipass@7.1.2:
+ resolution:
+ {
+ integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
+ }
+ engines: { node: '>=16 || 14 >=14.17' }
+
+ minizlib@3.1.0:
+ resolution:
+ {
+ integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==
+ }
+ engines: { node: '>= 18' }
+
+ mitt@3.0.1:
+ resolution:
+ {
+ integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==
+ }
+
+ mlly@1.8.0:
+ resolution:
+ {
+ integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==
+ }
+
+ mrmime@2.0.1:
+ resolution:
+ {
+ integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==
+ }
+ engines: { node: '>=10' }
+
+ ms@2.1.3:
+ resolution:
+ {
+ integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+ }
+
+ muggle-string@0.4.1:
+ resolution:
+ {
+ integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==
+ }
+
+ mute-stream@0.0.8:
+ resolution:
+ {
+ integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+ }
+
+ namespace-emitter@2.0.1:
+ resolution:
+ {
+ integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==
+ }
+
+ nanoid@3.3.11:
+ resolution:
+ {
+ integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
+ }
+ engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 }
+ hasBin: true
+
+ nanoid@5.1.6:
+ resolution:
+ {
+ integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==
+ }
+ engines: { node: ^18 || >=20 }
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution:
+ {
+ integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+ }
+
+ next-tick@1.1.0:
+ resolution:
+ {
+ integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
+ }
+
+ node-addon-api@7.1.1:
+ resolution:
+ {
+ integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
+ }
+
+ node-releases@2.0.21:
+ resolution:
+ {
+ integrity: sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==
+ }
+
+ normalize-path@3.0.0:
+ resolution:
+ {
+ integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+ }
+ engines: { node: '>=0.10.0' }
+
+ normalize-wheel-es@1.2.0:
+ resolution:
+ {
+ integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==
+ }
+
+ npm-run-path@5.3.0:
+ resolution:
+ {
+ integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==
+ }
+ engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
+
+ npm-run-path@6.0.0:
+ resolution:
+ {
+ integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==
+ }
+ engines: { node: '>=18' }
+
+ nprogress@0.2.0:
+ resolution:
+ {
+ integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==
+ }
+
+ nth-check@2.1.1:
+ resolution:
+ {
+ integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+ }
+
+ ohash@2.0.11:
+ resolution:
+ {
+ integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==
+ }
+
+ once@1.4.0:
+ resolution:
+ {
+ integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ }
+
+ onetime@5.1.2:
+ resolution:
+ {
+ integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ }
+ engines: { node: '>=6' }
+
+ onetime@6.0.0:
+ resolution:
+ {
+ integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==
+ }
+ engines: { node: '>=12' }
+
+ onetime@7.0.0:
+ resolution:
+ {
+ integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==
+ }
+ engines: { node: '>=18' }
+
+ open@10.2.0:
+ resolution:
+ {
+ integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==
+ }
+ engines: { node: '>=18' }
+
+ open@8.4.2:
+ resolution:
+ {
+ integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==
+ }
+ engines: { node: '>=12' }
+
+ optionator@0.9.4:
+ resolution:
+ {
+ integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==
+ }
+ engines: { node: '>= 0.8.0' }
+
+ ora@5.4.1:
+ resolution:
+ {
+ integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==
+ }
+ engines: { node: '>=10' }
+
+ os-tmpdir@1.0.2:
+ resolution:
+ {
+ integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
+ }
+ engines: { node: '>=0.10.0' }
+
+ p-limit@3.1.0:
+ resolution:
+ {
+ integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+ }
+ engines: { node: '>=10' }
+
+ p-limit@4.0.0:
+ resolution:
+ {
+ integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==
+ }
+ engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
+
+ p-locate@5.0.0:
+ resolution:
+ {
+ integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ }
+ engines: { node: '>=10' }
+
+ p-locate@6.0.0:
+ resolution:
+ {
+ integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==
+ }
+ engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
+
+ parent-module@1.0.1:
+ resolution:
+ {
+ integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ }
+ engines: { node: '>=6' }
+
+ parse-json@5.2.0:
+ resolution:
+ {
+ integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+ }
+ engines: { node: '>=8' }
+
+ parse-ms@4.0.0:
+ resolution:
+ {
+ integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==
+ }
+ engines: { node: '>=18' }
+
+ parse-passwd@1.0.0:
+ resolution:
+ {
+ integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==
+ }
+ engines: { node: '>=0.10.0' }
+
+ path-browserify@1.0.1:
+ resolution:
+ {
+ integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
+ }
+
+ path-exists@4.0.0:
+ resolution:
+ {
+ integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+ }
+ engines: { node: '>=8' }
+
+ path-exists@5.0.0:
+ resolution:
+ {
+ integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==
+ }
+ engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 }
+
+ path-is-absolute@1.0.1:
+ resolution:
+ {
+ integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+ }
+ engines: { node: '>=0.10.0' }
+
+ path-key@3.1.1:
+ resolution:
+ {
+ integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+ }
+ engines: { node: '>=8' }
+
+ path-key@4.0.0:
+ resolution:
+ {
+ integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==
+ }
+ engines: { node: '>=12' }
+
+ path-type@4.0.0:
+ resolution:
+ {
+ integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+ }
+ engines: { node: '>=8' }
+
+ pathe@2.0.3:
+ resolution:
+ {
+ integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
+ }
+
+ perfect-debounce@1.0.0:
+ resolution:
+ {
+ integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==
+ }
+
+ picocolors@1.1.1:
+ resolution:
+ {
+ integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+ }
+
+ picomatch@2.3.1:
+ resolution:
+ {
+ integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+ }
+ engines: { node: '>=8.6' }
+
+ picomatch@4.0.3:
+ resolution:
+ {
+ integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
+ }
+ engines: { node: '>=12' }
+
+ pidtree@0.6.0:
+ resolution:
+ {
+ integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==
+ }
+ engines: { node: '>=0.10' }
+ hasBin: true
+
+ pinia-plugin-persistedstate@4.5.0:
+ resolution:
+ {
+ integrity: sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==
+ }
+ peerDependencies:
+ '@nuxt/kit': '>=3.0.0'
+ '@pinia/nuxt': '>=0.10.0'
+ pinia: '>=3.0.0'
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+ '@pinia/nuxt':
+ optional: true
+ pinia:
+ optional: true
+
+ pinia@3.0.3:
+ resolution:
+ {
+ integrity: sha512-ttXO/InUULUXkMHpTdp9Fj4hLpD/2AoJdmAbAeW2yu1iy1k+pkFekQXw5VpC0/5p51IOR/jDaDRfRWRnMMsGOA==
+ }
+ peerDependencies:
+ typescript: '>=4.4.4'
+ vue: ^2.7.0 || ^3.5.11
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ pkg-types@1.3.1:
+ resolution:
+ {
+ integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==
+ }
+
+ pkg-types@2.3.0:
+ resolution:
+ {
+ integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==
+ }
+
+ postcss-html@1.8.0:
+ resolution:
+ {
+ integrity: sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==
+ }
+ engines: { node: ^12 || >=14 }
+
+ postcss-media-query-parser@0.2.3:
+ resolution:
+ {
+ integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==
+ }
+
+ postcss-resolve-nested-selector@0.1.6:
+ resolution:
+ {
+ integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==
+ }
+
+ postcss-safe-parser@6.0.0:
+ resolution:
+ {
+ integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==
+ }
+ engines: { node: '>=12.0' }
+ peerDependencies:
+ postcss: ^8.3.3
+
+ postcss-safe-parser@7.0.1:
+ resolution:
+ {
+ integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==
+ }
+ engines: { node: '>=18.0' }
+ peerDependencies:
+ postcss: ^8.4.31
+
+ postcss-scss@4.0.9:
+ resolution:
+ {
+ integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==
+ }
+ engines: { node: '>=12.0' }
+ peerDependencies:
+ postcss: ^8.4.29
+
+ postcss-selector-parser@6.1.2:
+ resolution:
+ {
+ integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
+ }
+ engines: { node: '>=4' }
+
+ postcss-selector-parser@7.1.0:
+ resolution:
+ {
+ integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==
+ }
+ engines: { node: '>=4' }
+
+ postcss-sorting@8.0.2:
+ resolution:
+ {
+ integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==
+ }
+ peerDependencies:
+ postcss: ^8.4.20
+
+ postcss-value-parser@4.2.0:
+ resolution:
+ {
+ integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+ }
+
+ postcss@8.5.6:
+ resolution:
+ {
+ integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
+ }
+ engines: { node: ^10 || ^12 || >=14 }
+
+ preact@10.27.2:
+ resolution:
+ {
+ integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==
+ }
+
+ prelude-ls@1.2.1:
+ resolution:
+ {
+ integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+ }
+ engines: { node: '>= 0.8.0' }
+
+ prettier-linter-helpers@1.0.0:
+ resolution:
+ {
+ integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+ }
+ engines: { node: '>=6.0.0' }
+
+ prettier@3.6.2:
+ resolution:
+ {
+ integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==
+ }
+ engines: { node: '>=14' }
+ hasBin: true
+
+ pretty-ms@9.3.0:
+ resolution:
+ {
+ integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==
+ }
+ engines: { node: '>=18' }
+
+ prismjs@1.30.0:
+ resolution:
+ {
+ integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==
+ }
+ engines: { node: '>=6' }
+
+ proxy-from-env@1.1.0:
+ resolution:
+ {
+ integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+ }
+
+ punycode@2.3.1:
+ resolution:
+ {
+ integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
+ }
+ engines: { node: '>=6' }
+
+ qrcode.vue@3.6.0:
+ resolution:
+ {
+ integrity: sha512-vQcl2fyHYHMjDO1GguCldJxepq2izQjBkDEEu9NENgfVKP6mv/e2SU62WbqYHGwTgWXLhxZ1NCD1dAZKHQq1fg==
+ }
+ peerDependencies:
+ vue: ^3.0.0
+
+ quansync@0.2.11:
+ resolution:
+ {
+ integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==
+ }
+
+ queue-microtask@1.2.3:
+ resolution:
+ {
+ integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+ }
+
+ readable-stream@3.6.2:
+ resolution:
+ {
+ integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
+ }
+ engines: { node: '>= 6' }
+
+ readdirp@3.6.0:
+ resolution:
+ {
+ integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+ }
+ engines: { node: '>=8.10.0' }
+
+ readdirp@4.1.2:
+ resolution:
+ {
+ integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==
+ }
+ engines: { node: '>= 14.18.0' }
+
+ require-directory@2.1.1:
+ resolution:
+ {
+ integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+ }
+ engines: { node: '>=0.10.0' }
+
+ require-from-string@2.0.2:
+ resolution:
+ {
+ integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+ }
+ engines: { node: '>=0.10.0' }
+
+ resolve-dir@1.0.1:
+ resolution:
+ {
+ integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==
+ }
+ engines: { node: '>=0.10.0' }
+
+ resolve-from@4.0.0:
+ resolution:
+ {
+ integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+ }
+ engines: { node: '>=4' }
+
+ resolve-from@5.0.0:
+ resolution:
+ {
+ integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+ }
+ engines: { node: '>=8' }
+
+ resolve-pkg-maps@1.0.0:
+ resolution:
+ {
+ integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
+ }
+
+ restore-cursor@3.1.0:
+ resolution:
+ {
+ integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+ }
+ engines: { node: '>=8' }
+
+ restore-cursor@5.1.0:
+ resolution:
+ {
+ integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==
+ }
+ engines: { node: '>=18' }
+
+ reusify@1.1.0:
+ resolution:
+ {
+ integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==
+ }
+ engines: { iojs: '>=1.0.0', node: '>=0.10.0' }
+
+ rfdc@1.4.1:
+ resolution:
+ {
+ integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==
+ }
+
+ rollup-plugin-visualizer@5.14.0:
+ resolution:
+ {
+ integrity: sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==
+ }
+ engines: { node: '>=18' }
+ hasBin: true
+ peerDependencies:
+ rolldown: 1.x
+ rollup: 2.x || 3.x || 4.x
+ peerDependenciesMeta:
+ rolldown:
+ optional: true
+ rollup:
+ optional: true
+
+ rollup@4.52.3:
+ resolution:
+ {
+ integrity: sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==
+ }
+ engines: { node: '>=18.0.0', npm: '>=8.0.0' }
+ hasBin: true
+
+ run-applescript@7.1.0:
+ resolution:
+ {
+ integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==
+ }
+ engines: { node: '>=18' }
+
+ run-async@2.4.1:
+ resolution:
+ {
+ integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
+ }
+ engines: { node: '>=0.12.0' }
+
+ run-parallel@1.2.0:
+ resolution:
+ {
+ integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ }
+
+ rxjs@7.8.2:
+ resolution:
+ {
+ integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==
+ }
+
+ safe-buffer@5.2.1:
+ resolution:
+ {
+ integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+ }
+
+ safer-buffer@2.1.2:
+ resolution:
+ {
+ integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+ }
+
+ sass@1.93.2:
+ resolution:
+ {
+ integrity: sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==
+ }
+ engines: { node: '>=14.0.0' }
+ hasBin: true
+
+ scroll-into-view-if-needed@2.2.31:
+ resolution:
+ {
+ integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==
+ }
+
+ scule@1.3.0:
+ resolution:
+ {
+ integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==
+ }
+
+ semver@6.3.1:
+ resolution:
+ {
+ integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+ }
+ hasBin: true
+
+ semver@7.7.2:
+ resolution:
+ {
+ integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
+ }
+ engines: { node: '>=10' }
+ hasBin: true
+
+ shebang-command@2.0.0:
+ resolution:
+ {
+ integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ }
+ engines: { node: '>=8' }
+
+ shebang-regex@3.0.0:
+ resolution:
+ {
+ integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+ }
+ engines: { node: '>=8' }
+
+ signal-exit@3.0.7:
+ resolution:
+ {
+ integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+ }
+
+ signal-exit@4.1.0:
+ resolution:
+ {
+ integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==
+ }
+ engines: { node: '>=14' }
+
+ sirv@3.0.2:
+ resolution:
+ {
+ integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==
+ }
+ engines: { node: '>=18' }
+
+ slash@3.0.0:
+ resolution:
+ {
+ integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+ }
+ engines: { node: '>=8' }
+
+ slate-history@0.66.0:
+ resolution:
+ {
+ integrity: sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==
+ }
+ peerDependencies:
+ slate: '>=0.65.3'
+
+ slate@0.72.8:
+ resolution:
+ {
+ integrity: sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==
+ }
+
+ slice-ansi@4.0.0:
+ resolution:
+ {
+ integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+ }
+ engines: { node: '>=10' }
+
+ slice-ansi@5.0.0:
+ resolution:
+ {
+ integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==
+ }
+ engines: { node: '>=12' }
+
+ slice-ansi@7.1.2:
+ resolution:
+ {
+ integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==
+ }
+ engines: { node: '>=18' }
+
+ snabbdom@3.6.2:
+ resolution:
+ {
+ integrity: sha512-ig5qOnCDbugFntKi6c7Xlib8bA6xiJVk8O+WdFrV3wxbMqeHO0hXFQC4nAhPVWfZfi8255lcZkNhtIBINCc4+Q==
+ }
+ engines: { node: '>=12.17.0' }
+
+ source-map-js@1.2.1:
+ resolution:
+ {
+ integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
+ }
+ engines: { node: '>=0.10.0' }
+
+ source-map-support@0.5.21:
+ resolution:
+ {
+ integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+ }
+
+ source-map@0.6.1:
+ resolution:
+ {
+ integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+ }
+ engines: { node: '>=0.10.0' }
+
+ source-map@0.7.6:
+ resolution:
+ {
+ integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==
+ }
+ engines: { node: '>= 12' }
+
+ speakingurl@14.0.1:
+ resolution:
+ {
+ integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==
+ }
+ engines: { node: '>=0.10.0' }
+
+ split2@4.2.0:
+ resolution:
+ {
+ integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
+ }
+ engines: { node: '>= 10.x' }
+
+ ssf@0.11.2:
+ resolution:
+ {
+ integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==
+ }
+ engines: { node: '>=0.8' }
+
+ ssr-window@3.0.0:
+ resolution:
+ {
+ integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==
+ }
+
+ string-argv@0.3.2:
+ resolution:
+ {
+ integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==
+ }
+ engines: { node: '>=0.6.19' }
+
+ string-width@4.2.3:
+ resolution:
+ {
+ integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ }
+ engines: { node: '>=8' }
+
+ string-width@7.2.0:
+ resolution:
+ {
+ integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==
+ }
+ engines: { node: '>=18' }
+
+ string_decoder@1.3.0:
+ resolution:
+ {
+ integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ }
+
+ strip-ansi@6.0.1:
+ resolution:
+ {
+ integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ }
+ engines: { node: '>=8' }
+
+ strip-ansi@7.1.2:
+ resolution:
+ {
+ integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==
+ }
+ engines: { node: '>=12' }
+
+ strip-bom@4.0.0:
+ resolution:
+ {
+ integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
+ }
+ engines: { node: '>=8' }
+
+ strip-final-newline@3.0.0:
+ resolution:
+ {
+ integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==
+ }
+ engines: { node: '>=12' }
+
+ strip-final-newline@4.0.0:
+ resolution:
+ {
+ integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==
+ }
+ engines: { node: '>=18' }
+
+ strip-json-comments@3.1.1:
+ resolution:
+ {
+ integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+ }
+ engines: { node: '>=8' }
+
+ strip-literal@3.1.0:
+ resolution:
+ {
+ integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==
+ }
+
+ stylelint-config-html@1.1.0:
+ resolution:
+ {
+ integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==
+ }
+ engines: { node: ^12 || >=14 }
+ peerDependencies:
+ postcss-html: ^1.0.0
+ stylelint: '>=14.0.0'
+
+ stylelint-config-recess-order@4.6.0:
+ resolution:
+ {
+ integrity: sha512-V76fhv3YtcNXh/hyAuAdSzi5FmcrG54Mp2AThJ3D/PTMTSYzUPd7GIhP6z9mTqnRhmkk6YTfcu/JWB8h+Yrcaw==
+ }
+ peerDependencies:
+ stylelint: '>=15'
+
+ stylelint-config-recommended-scss@14.1.0:
+ resolution:
+ {
+ integrity: sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==
+ }
+ engines: { node: '>=18.12.0' }
+ peerDependencies:
+ postcss: ^8.3.3
+ stylelint: ^16.6.1
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+
+ stylelint-config-recommended-vue@1.6.1:
+ resolution:
+ {
+ integrity: sha512-lLW7hTIMBiTfjenGuDq2kyHA6fBWd/+Df7MO4/AWOxiFeXP9clbpKgg27kHfwA3H7UNMGC7aeP3mNlZB5LMmEQ==
+ }
+ engines: { node: ^12 || >=14 }
+ peerDependencies:
+ postcss-html: ^1.0.0
+ stylelint: '>=14.0.0'
+
+ stylelint-config-recommended@14.0.1:
+ resolution:
+ {
+ integrity: sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==
+ }
+ engines: { node: '>=18.12.0' }
+ peerDependencies:
+ stylelint: ^16.1.0
+
+ stylelint-config-recommended@17.0.0:
+ resolution:
+ {
+ integrity: sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==
+ }
+ engines: { node: '>=18.12.0' }
+ peerDependencies:
+ stylelint: ^16.23.0
+
+ stylelint-config-standard@36.0.1:
+ resolution:
+ {
+ integrity: sha512-8aX8mTzJ6cuO8mmD5yon61CWuIM4UD8Q5aBcWKGSf6kg+EC3uhB+iOywpTK4ca6ZL7B49en8yanOFtUW0qNzyw==
+ }
+ engines: { node: '>=18.12.0' }
+ peerDependencies:
+ stylelint: ^16.1.0
+
+ stylelint-order@6.0.4:
+ resolution:
+ {
+ integrity: sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==
+ }
+ peerDependencies:
+ stylelint: ^14.0.0 || ^15.0.0 || ^16.0.1
+
+ stylelint-scss@6.12.1:
+ resolution:
+ {
+ integrity: sha512-UJUfBFIvXfly8WKIgmqfmkGKPilKB4L5j38JfsDd+OCg2GBdU0vGUV08Uw82tsRZzd4TbsUURVVNGeOhJVF7pA==
+ }
+ engines: { node: '>=18.12.0' }
+ peerDependencies:
+ stylelint: ^16.0.2
+
+ stylelint@16.24.0:
+ resolution:
+ {
+ integrity: sha512-7ksgz3zJaSbTUGr/ujMXvLVKdDhLbGl3R/3arNudH7z88+XZZGNLMTepsY28WlnvEFcuOmUe7fg40Q3lfhOfSQ==
+ }
+ engines: { node: '>=18.12.0' }
+ hasBin: true
+
+ superjson@2.2.2:
+ resolution:
+ {
+ integrity: sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==
+ }
+ engines: { node: '>=16' }
+
+ supports-color@5.5.0:
+ resolution:
+ {
+ integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ }
+ engines: { node: '>=4' }
+
+ supports-color@7.2.0:
+ resolution:
+ {
+ integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ }
+ engines: { node: '>=8' }
+
+ supports-hyperlinks@3.2.0:
+ resolution:
+ {
+ integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==
+ }
+ engines: { node: '>=14.18' }
+
+ svg-tags@1.0.0:
+ resolution:
+ {
+ integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==
+ }
+
+ synckit@0.11.11:
+ resolution:
+ {
+ integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==
+ }
+ engines: { node: ^14.18.0 || >=16.0.0 }
+
+ table@6.9.0:
+ resolution:
+ {
+ integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==
+ }
+ engines: { node: '>=10.0.0' }
+
+ tailwindcss@4.1.14:
+ resolution:
+ {
+ integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==
+ }
+
+ tapable@2.3.0:
+ resolution:
+ {
+ integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==
+ }
+ engines: { node: '>=6' }
+
+ tar@7.5.1:
+ resolution:
+ {
+ integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==
+ }
+ engines: { node: '>=18' }
+
+ terser@5.44.0:
+ resolution:
+ {
+ integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==
+ }
+ engines: { node: '>=10' }
+ hasBin: true
+
+ text-extensions@2.4.0:
+ resolution:
+ {
+ integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==
+ }
+ engines: { node: '>=8' }
+
+ through@2.3.8:
+ resolution:
+ {
+ integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+ }
+
+ tiny-warning@1.0.3:
+ resolution:
+ {
+ integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
+ }
+
+ tinyexec@1.0.1:
+ resolution:
+ {
+ integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==
+ }
+
+ tinyglobby@0.2.15:
+ resolution:
+ {
+ integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
+ }
+ engines: { node: '>=12.0.0' }
+
+ tmp@0.0.33:
+ resolution:
+ {
+ integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+ }
+ engines: { node: '>=0.6.0' }
+
+ to-regex-range@5.0.1:
+ resolution:
+ {
+ integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ }
+ engines: { node: '>=8.0' }
+
+ totalist@3.0.1:
+ resolution:
+ {
+ integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==
+ }
+ engines: { node: '>=6' }
+
+ ts-api-utils@2.1.0:
+ resolution:
+ {
+ integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==
+ }
+ engines: { node: '>=18.12' }
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tslib@2.3.0:
+ resolution:
+ {
+ integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
+ }
+
+ tslib@2.8.1:
+ resolution:
+ {
+ integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+ }
+
+ tsx@4.20.6:
+ resolution:
+ {
+ integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==
+ }
+ engines: { node: '>=18.0.0' }
+ hasBin: true
+
+ type-check@0.4.0:
+ resolution:
+ {
+ integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ }
+ engines: { node: '>= 0.8.0' }
+
+ type-fest@0.20.2:
+ resolution:
+ {
+ integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+ }
+ engines: { node: '>=10' }
+
+ type-fest@0.21.3:
+ resolution:
+ {
+ integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+ }
+ engines: { node: '>=10' }
+
+ type@2.7.3:
+ resolution:
+ {
+ integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==
+ }
+
+ typescript-eslint@8.44.1:
+ resolution:
+ {
+ integrity: sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==
+ }
+ engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 }
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ typescript@5.6.3:
+ resolution:
+ {
+ integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==
+ }
+ engines: { node: '>=14.17' }
+ hasBin: true
+
+ ufo@1.6.1:
+ resolution:
+ {
+ integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==
+ }
+
+ undici-types@7.14.0:
+ resolution:
+ {
+ integrity: sha512-QQiYxHuyZ9gQUIrmPo3IA+hUl4KYk8uSA7cHrcKd/l3p1OTpZcM0Tbp9x7FAtXdAYhlasd60ncPpgu6ihG6TOA==
+ }
+
+ unicorn-magic@0.1.0:
+ resolution:
+ {
+ integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==
+ }
+ engines: { node: '>=18' }
+
+ unicorn-magic@0.3.0:
+ resolution:
+ {
+ integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==
+ }
+ engines: { node: '>=18' }
+
+ unimport@5.4.0:
+ resolution:
+ {
+ integrity: sha512-g/OLFZR2mEfqbC6NC9b2225eCJGvufxq34mj6kM3OmI5gdSL0qyqtnv+9qmsGpAmnzSl6x0IWZj4W+8j2hLkMA==
+ }
+ engines: { node: '>=18.12.0' }
+
+ universalify@2.0.1:
+ resolution:
+ {
+ integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
+ }
+ engines: { node: '>= 10.0.0' }
+
+ unplugin-auto-import@20.2.0:
+ resolution:
+ {
+ integrity: sha512-vfBI/SvD9hJqYNinipVOAj5n8dS8DJXFlCKFR5iLDp2SaQwsfdnfLXgZ+34Kd3YY3YEY9omk8XQg0bwos3Q8ug==
+ }
+ engines: { node: '>=14' }
+ peerDependencies:
+ '@nuxt/kit': ^4.0.0
+ '@vueuse/core': '*'
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+ '@vueuse/core':
+ optional: true
+
+ unplugin-element-plus@0.10.0:
+ resolution:
+ {
+ integrity: sha512-oRSW0x6U58xBOWKy8TcoVZNA8ElIpfp3TUJRLQI6ey/E9PpjHl9/deeTAZNt8D57Li4OA4pCJtM6p2cb4Ff4ZA==
+ }
+ engines: { node: '>=18.12.0' }
+
+ unplugin-utils@0.2.5:
+ resolution:
+ {
+ integrity: sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==
+ }
+ engines: { node: '>=18.12.0' }
+
+ unplugin-utils@0.3.0:
+ resolution:
+ {
+ integrity: sha512-JLoggz+PvLVMJo+jZt97hdIIIZ2yTzGgft9e9q8iMrC4ewufl62ekeW7mixBghonn2gVb/ICjyvlmOCUBnJLQg==
+ }
+ engines: { node: '>=20.19.0' }
+
+ unplugin-vue-components@29.1.0:
+ resolution:
+ {
+ integrity: sha512-z/9ACPXth199s9aCTCdKZAhe5QGOpvzJYP+Hkd0GN1/PpAmsu+W3UlRY3BJAewPqQxh5xi56+Og6mfiCV1Jzpg==
+ }
+ engines: { node: '>=14' }
+ peerDependencies:
+ '@babel/parser': ^7.15.8
+ '@nuxt/kit': ^3.2.2 || ^4.0.0
+ vue: 2 || 3
+ peerDependenciesMeta:
+ '@babel/parser':
+ optional: true
+ '@nuxt/kit':
+ optional: true
+
+ unplugin@2.3.10:
+ resolution:
+ {
+ integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==
+ }
+ engines: { node: '>=18.12.0' }
+
+ update-browserslist-db@1.1.3:
+ resolution:
+ {
+ integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==
+ }
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution:
+ {
+ integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ }
+
+ util-deprecate@1.0.2:
+ resolution:
+ {
+ integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+ }
+
+ vite-hot-client@2.1.0:
+ resolution:
+ {
+ integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==
+ }
+ peerDependencies:
+ vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+ vite-plugin-compression@0.5.1:
+ resolution:
+ {
+ integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==
+ }
+ peerDependencies:
+ vite: '>=2.0.0'
+
+ vite-plugin-inspect@0.8.9:
+ resolution:
+ {
+ integrity: sha512-22/8qn+LYonzibb1VeFZmISdVao5kC22jmEKm24vfFE8siEn47EpVcCLYMv6iKOYMJfjSvSJfueOwcFCkUnV3A==
+ }
+ engines: { node: '>=14' }
+ peerDependencies:
+ '@nuxt/kit': '*'
+ vite: ^3.1.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.1
+ peerDependenciesMeta:
+ '@nuxt/kit':
+ optional: true
+
+ vite-plugin-vue-devtools@7.7.7:
+ resolution:
+ {
+ integrity: sha512-d0fIh3wRcgSlr4Vz7bAk4va1MkdqhQgj9ANE/rBhsAjOnRfTLs2ocjFMvSUOsv6SRRXU9G+VM7yMgqDb6yI4iQ==
+ }
+ engines: { node: '>=v14.21.3' }
+ peerDependencies:
+ vite: ^3.1.0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+ vite-plugin-vue-inspector@5.3.2:
+ resolution:
+ {
+ integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==
+ }
+ peerDependencies:
+ vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0
+
+ vite@7.1.7:
+ resolution:
+ {
+ integrity: sha512-VbA8ScMvAISJNJVbRDTJdCwqQoAareR/wutevKanhR2/1EkoXVZVkkORaYm/tNVCjP/UDTKtcw3bAkwOUdedmA==
+ }
+ engines: { node: ^20.19.0 || >=22.12.0 }
+ hasBin: true
+ peerDependencies:
+ '@types/node': ^20.19.0 || >=22.12.0
+ jiti: '>=1.21.0'
+ less: ^4.0.0
+ lightningcss: ^1.21.0
+ sass: ^1.70.0
+ sass-embedded: ^1.70.0
+ stylus: '>=0.54.8'
+ sugarss: ^5.0.0
+ terser: ^5.16.0
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ '@types/node':
+ optional: true
+ jiti:
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+
+ vscode-uri@3.1.0:
+ resolution:
+ {
+ integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==
+ }
+
+ vue-demi@0.14.10:
+ resolution:
+ {
+ integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==
+ }
+ engines: { node: '>=12' }
+ hasBin: true
+ peerDependencies:
+ '@vue/composition-api': ^1.0.0-rc.1
+ vue: ^3.0.0-0 || ^2.6.0
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ vue-draggable-plus@0.6.0:
+ resolution:
+ {
+ integrity: sha512-G5TSfHrt9tX9EjdG49InoFJbt2NYk0h3kgjgKxkFWr3ulIUays0oFObr5KZ8qzD4+QnhtALiRwIqY6qul4egqw==
+ }
+ peerDependencies:
+ '@types/sortablejs': ^1.15.0
+ '@vue/composition-api': '*'
+ peerDependenciesMeta:
+ '@vue/composition-api':
+ optional: true
+
+ vue-eslint-parser@9.4.3:
+ resolution:
+ {
+ integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==
+ }
+ engines: { node: ^14.17.0 || >=16.0.0 }
+ peerDependencies:
+ eslint: '>=6.0.0'
+
+ vue-i18n@9.14.5:
+ resolution:
+ {
+ integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==
+ }
+ engines: { node: '>= 16' }
+ peerDependencies:
+ vue: ^3.0.0
+
+ vue-img-cutter@3.0.7:
+ resolution:
+ {
+ integrity: sha512-fNw3kimawg9XVXDZCw2bI74NI+Jq+H42wjymatZVVSY46wuBty6LbQsu4GeVfo/yzpS9AHY0tzckpYzX3D2fmA==
+ }
+
+ vue-router@4.5.1:
+ resolution:
+ {
+ integrity: sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==
+ }
+ peerDependencies:
+ vue: ^3.2.0
+
+ vue-tsc@2.1.10:
+ resolution:
+ {
+ integrity: sha512-RBNSfaaRHcN5uqVqJSZh++Gy/YUzryuv9u1aFWhsammDJXNtUiJMNoJ747lZcQ68wUQFx6E73y4FY3D8E7FGMA==
+ }
+ hasBin: true
+ peerDependencies:
+ typescript: '>=5.0.0'
+
+ vue@3.5.22:
+ resolution:
+ {
+ integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==
+ }
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ wcwidth@1.0.1:
+ resolution:
+ {
+ integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==
+ }
+
+ webpack-virtual-modules@0.6.2:
+ resolution:
+ {
+ integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==
+ }
+
+ which@1.3.1:
+ resolution:
+ {
+ integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+ }
+ hasBin: true
+
+ which@2.0.2:
+ resolution:
+ {
+ integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ }
+ engines: { node: '>= 8' }
+ hasBin: true
+
+ wildcard@1.1.2:
+ resolution:
+ {
+ integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==
+ }
+
+ wmf@1.0.2:
+ resolution:
+ {
+ integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==
+ }
+ engines: { node: '>=0.8' }
+
+ word-wrap@1.2.5:
+ resolution:
+ {
+ integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
+ }
+ engines: { node: '>=0.10.0' }
+
+ word@0.3.0:
+ resolution:
+ {
+ integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
+ }
+ engines: { node: '>=0.8' }
+
+ wrap-ansi@7.0.0:
+ resolution:
+ {
+ integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ }
+ engines: { node: '>=10' }
+
+ wrap-ansi@9.0.2:
+ resolution:
+ {
+ integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==
+ }
+ engines: { node: '>=18' }
+
+ wrappy@1.0.2:
+ resolution:
+ {
+ integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+ }
+
+ write-file-atomic@5.0.1:
+ resolution:
+ {
+ integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==
+ }
+ engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 }
+
+ wsl-utils@0.1.0:
+ resolution:
+ {
+ integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==
+ }
+ engines: { node: '>=18' }
+
+ xgplayer-subtitles@3.0.23:
+ resolution:
+ {
+ integrity: sha512-deGdV75giVzfTTdG9XATmji39NHwKTpEelWt2rRx/RyXGgU2bQFp0Ft7yWaK2Uu8A/WVrP5fpxEAj4MstREMkQ==
+ }
+ peerDependencies:
+ core-js: '>=3.12.1'
+
+ xgplayer@3.0.23:
+ resolution:
+ {
+ integrity: sha512-Bn3zQfMMAZimlVG9EeIDybMcklc+6FH8Sv47KpTq4K6ofCzyhPG/KenxailDedlHmxjb5B2o+240TpJtMQ3oJA==
+ }
+ peerDependencies:
+ core-js: '>=3.12.1'
+
+ xlsx@0.18.5:
+ resolution:
+ {
+ integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==
+ }
+ engines: { node: '>=0.8' }
+ hasBin: true
+
+ xml-name-validator@4.0.0:
+ resolution:
+ {
+ integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+ }
+ engines: { node: '>=12' }
+
+ y18n@5.0.8:
+ resolution:
+ {
+ integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+ }
+ engines: { node: '>=10' }
+
+ yallist@3.1.1:
+ resolution:
+ {
+ integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+ }
+
+ yallist@5.0.0:
+ resolution:
+ {
+ integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==
+ }
+ engines: { node: '>=18' }
+
+ yaml@2.8.1:
+ resolution:
+ {
+ integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==
+ }
+ engines: { node: '>= 14.6' }
+ hasBin: true
+
+ yargs-parser@21.1.1:
+ resolution:
+ {
+ integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+ }
+ engines: { node: '>=12' }
+
+ yargs@17.7.2:
+ resolution:
+ {
+ integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
+ }
+ engines: { node: '>=12' }
+
+ yocto-queue@0.1.0:
+ resolution:
+ {
+ integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+ }
+ engines: { node: '>=10' }
+
+ yocto-queue@1.2.1:
+ resolution:
+ {
+ integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==
+ }
+ engines: { node: '>=12.20' }
+
+ yoctocolors@2.1.2:
+ resolution:
+ {
+ integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==
+ }
+ engines: { node: '>=18' }
+
+ zrender@6.0.0:
+ resolution:
+ {
+ integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==
+ }
+
+snapshots:
+ '@antfu/utils@0.7.10': {}
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.4': {}
+
+ '@babel/core@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helpers': 7.28.4
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.3':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/helper-compilation-targets@7.27.2':
+ dependencies:
+ '@babel/compat-data': 7.28.4
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.26.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-member-expression-to-functions': 7.27.1
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/traverse': 7.28.4
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-member-expression-to-functions@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-imports@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/helper-plugin-utils@7.27.1': {}
+
+ '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-member-expression-to-functions': 7.27.1
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helpers@7.28.4':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+
+ '@babel/parser@7.28.4':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/runtime@7.28.4': {}
+
+ '@babel/template@7.27.2':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+
+ '@babel/traverse@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.4':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@cacheable/memoize@2.0.2':
+ dependencies:
+ '@cacheable/utils': 2.0.2
+
+ '@cacheable/memory@2.0.2':
+ dependencies:
+ '@cacheable/memoize': 2.0.2
+ '@cacheable/utils': 2.0.2
+ '@keyv/bigmap': 1.0.2
+ hookified: 1.12.1
+ keyv: 5.5.3
+
+ '@cacheable/utils@2.0.2': {}
+
+ '@commitlint/cli@19.8.1(@types/node@24.8.1)(typescript@5.6.3)':
+ dependencies:
+ '@commitlint/format': 19.8.1
+ '@commitlint/lint': 19.8.1
+ '@commitlint/load': 19.8.1(@types/node@24.8.1)(typescript@5.6.3)
+ '@commitlint/read': 19.8.1
+ '@commitlint/types': 19.8.1
+ tinyexec: 1.0.1
+ yargs: 17.7.2
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ '@commitlint/config-conventional@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ conventional-changelog-conventionalcommits: 7.0.2
+
+ '@commitlint/config-validator@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ ajv: 8.17.1
+
+ '@commitlint/config-validator@20.0.0':
+ dependencies:
+ '@commitlint/types': 20.0.0
+ ajv: 8.17.1
+ optional: true
+
+ '@commitlint/ensure@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ lodash.camelcase: 4.3.0
+ lodash.kebabcase: 4.1.1
+ lodash.snakecase: 4.1.1
+ lodash.startcase: 4.4.0
+ lodash.upperfirst: 4.3.1
+
+ '@commitlint/execute-rule@19.8.1': {}
+
+ '@commitlint/execute-rule@20.0.0':
+ optional: true
+
+ '@commitlint/format@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ chalk: 5.6.2
+
+ '@commitlint/is-ignored@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ semver: 7.7.2
+
+ '@commitlint/lint@19.8.1':
+ dependencies:
+ '@commitlint/is-ignored': 19.8.1
+ '@commitlint/parse': 19.8.1
+ '@commitlint/rules': 19.8.1
+ '@commitlint/types': 19.8.1
+
+ '@commitlint/load@19.8.1(@types/node@24.8.1)(typescript@5.6.3)':
+ dependencies:
+ '@commitlint/config-validator': 19.8.1
+ '@commitlint/execute-rule': 19.8.1
+ '@commitlint/resolve-extends': 19.8.1
+ '@commitlint/types': 19.8.1
+ chalk: 5.6.2
+ cosmiconfig: 9.0.0(typescript@5.6.3)
+ cosmiconfig-typescript-loader: 6.1.0(@types/node@24.8.1)(cosmiconfig@9.0.0(typescript@5.6.3))(typescript@5.6.3)
+ lodash.isplainobject: 4.0.6
+ lodash.merge: 4.6.2
+ lodash.uniq: 4.5.0
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ '@commitlint/load@20.0.0(@types/node@24.8.1)(typescript@5.6.3)':
+ dependencies:
+ '@commitlint/config-validator': 20.0.0
+ '@commitlint/execute-rule': 20.0.0
+ '@commitlint/resolve-extends': 20.0.0
+ '@commitlint/types': 20.0.0
+ chalk: 5.6.2
+ cosmiconfig: 9.0.0(typescript@5.6.3)
+ cosmiconfig-typescript-loader: 6.1.0(@types/node@24.8.1)(cosmiconfig@9.0.0(typescript@5.6.3))(typescript@5.6.3)
+ lodash.isplainobject: 4.0.6
+ lodash.merge: 4.6.2
+ lodash.uniq: 4.5.0
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+ optional: true
+
+ '@commitlint/message@19.8.1': {}
+
+ '@commitlint/parse@19.8.1':
+ dependencies:
+ '@commitlint/types': 19.8.1
+ conventional-changelog-angular: 7.0.0
+ conventional-commits-parser: 5.0.0
+
+ '@commitlint/read@19.8.1':
+ dependencies:
+ '@commitlint/top-level': 19.8.1
+ '@commitlint/types': 19.8.1
+ git-raw-commits: 4.0.0
+ minimist: 1.2.8
+ tinyexec: 1.0.1
+
+ '@commitlint/resolve-extends@19.8.1':
+ dependencies:
+ '@commitlint/config-validator': 19.8.1
+ '@commitlint/types': 19.8.1
+ global-directory: 4.0.1
+ import-meta-resolve: 4.2.0
+ lodash.mergewith: 4.6.2
+ resolve-from: 5.0.0
+
+ '@commitlint/resolve-extends@20.0.0':
+ dependencies:
+ '@commitlint/config-validator': 20.0.0
+ '@commitlint/types': 20.0.0
+ global-directory: 4.0.1
+ import-meta-resolve: 4.2.0
+ lodash.mergewith: 4.6.2
+ resolve-from: 5.0.0
+ optional: true
+
+ '@commitlint/rules@19.8.1':
+ dependencies:
+ '@commitlint/ensure': 19.8.1
+ '@commitlint/message': 19.8.1
+ '@commitlint/to-lines': 19.8.1
+ '@commitlint/types': 19.8.1
+
+ '@commitlint/to-lines@19.8.1': {}
+
+ '@commitlint/top-level@19.8.1':
+ dependencies:
+ find-up: 7.0.0
+
+ '@commitlint/types@19.8.1':
+ dependencies:
+ '@types/conventional-commits-parser': 5.0.1
+ chalk: 5.6.2
+
+ '@commitlint/types@20.0.0':
+ dependencies:
+ '@types/conventional-commits-parser': 5.0.1
+ chalk: 5.6.2
+ optional: true
+
+ '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-tokenizer@3.0.4': {}
+
+ '@csstools/media-query-list-parser@4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)':
+ dependencies:
+ postcss-selector-parser: 7.1.0
+
+ '@ctrl/tinycolor@3.6.1': {}
+
+ '@dual-bundle/import-meta-resolve@4.2.1': {}
+
+ '@element-plus/icons-vue@2.3.2(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ vue: 3.5.22(typescript@5.6.3)
+
+ '@esbuild/aix-ppc64@0.25.10':
+ optional: true
+
+ '@esbuild/android-arm64@0.25.10':
+ optional: true
+
+ '@esbuild/android-arm@0.25.10':
+ optional: true
+
+ '@esbuild/android-x64@0.25.10':
+ optional: true
+
+ '@esbuild/darwin-arm64@0.25.10':
+ optional: true
+
+ '@esbuild/darwin-x64@0.25.10':
+ optional: true
+
+ '@esbuild/freebsd-arm64@0.25.10':
+ optional: true
+
+ '@esbuild/freebsd-x64@0.25.10':
+ optional: true
+
+ '@esbuild/linux-arm64@0.25.10':
+ optional: true
+
+ '@esbuild/linux-arm@0.25.10':
+ optional: true
+
+ '@esbuild/linux-ia32@0.25.10':
+ optional: true
+
+ '@esbuild/linux-loong64@0.25.10':
+ optional: true
+
+ '@esbuild/linux-mips64el@0.25.10':
+ optional: true
+
+ '@esbuild/linux-ppc64@0.25.10':
+ optional: true
+
+ '@esbuild/linux-riscv64@0.25.10':
+ optional: true
+
+ '@esbuild/linux-s390x@0.25.10':
+ optional: true
+
+ '@esbuild/linux-x64@0.25.10':
+ optional: true
+
+ '@esbuild/netbsd-arm64@0.25.10':
+ optional: true
+
+ '@esbuild/netbsd-x64@0.25.10':
+ optional: true
+
+ '@esbuild/openbsd-arm64@0.25.10':
+ optional: true
+
+ '@esbuild/openbsd-x64@0.25.10':
+ optional: true
+
+ '@esbuild/openharmony-arm64@0.25.10':
+ optional: true
+
+ '@esbuild/sunos-x64@0.25.10':
+ optional: true
+
+ '@esbuild/win32-arm64@0.25.10':
+ optional: true
+
+ '@esbuild/win32-ia32@0.25.10':
+ optional: true
+
+ '@esbuild/win32-x64@0.25.10':
+ optional: true
+
+ '@eslint-community/eslint-utils@4.9.0(eslint@9.36.0(jiti@2.6.0))':
+ dependencies:
+ eslint: 9.36.0(jiti@2.6.0)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/config-array@0.21.0':
+ dependencies:
+ '@eslint/object-schema': 2.1.6
+ debug: 4.4.3
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.3.1': {}
+
+ '@eslint/core@0.15.2':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.1':
+ dependencies:
+ ajv: 6.12.6
+ debug: 4.4.3
+ espree: 10.4.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ minimatch: 3.1.2
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.36.0': {}
+
+ '@eslint/object-schema@2.1.6': {}
+
+ '@eslint/plugin-kit@0.3.5':
+ dependencies:
+ '@eslint/core': 0.15.2
+ levn: 0.4.1
+
+ '@floating-ui/core@1.7.3':
+ dependencies:
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/dom@1.7.4':
+ dependencies:
+ '@floating-ui/core': 1.7.3
+ '@floating-ui/utils': 0.2.10
+
+ '@floating-ui/utils@0.2.10': {}
+
+ '@humanfs/core@0.19.1': {}
+
+ '@humanfs/node@0.16.7':
+ dependencies:
+ '@humanfs/core': 0.19.1
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@iconify/types@2.0.0': {}
+
+ '@iconify/vue@5.0.0(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ '@iconify/types': 2.0.0
+ vue: 3.5.22(typescript@5.6.3)
+
+ '@intlify/core-base@9.14.5':
+ dependencies:
+ '@intlify/message-compiler': 9.14.5
+ '@intlify/shared': 9.14.5
+
+ '@intlify/message-compiler@9.14.5':
+ dependencies:
+ '@intlify/shared': 9.14.5
+ source-map-js: 1.2.1
+
+ '@intlify/shared@9.14.5': {}
+
+ '@isaacs/fs-minipass@4.0.1':
+ dependencies:
+ minipass: 7.1.2
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/source-map@0.3.11':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@keyv/bigmap@1.0.2':
+ dependencies:
+ hookified: 1.12.1
+
+ '@keyv/serialize@1.1.1': {}
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.19.1
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher@2.5.1':
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.1
+ '@parcel/watcher-darwin-arm64': 2.5.1
+ '@parcel/watcher-darwin-x64': 2.5.1
+ '@parcel/watcher-freebsd-x64': 2.5.1
+ '@parcel/watcher-linux-arm-glibc': 2.5.1
+ '@parcel/watcher-linux-arm-musl': 2.5.1
+ '@parcel/watcher-linux-arm64-glibc': 2.5.1
+ '@parcel/watcher-linux-arm64-musl': 2.5.1
+ '@parcel/watcher-linux-x64-glibc': 2.5.1
+ '@parcel/watcher-linux-x64-musl': 2.5.1
+ '@parcel/watcher-win32-arm64': 2.5.1
+ '@parcel/watcher-win32-ia32': 2.5.1
+ '@parcel/watcher-win32-x64': 2.5.1
+ optional: true
+
+ '@pkgr/core@0.2.9': {}
+
+ '@polka/url@1.0.0-next.29': {}
+
+ '@rolldown/pluginutils@1.0.0-beta.29': {}
+
+ '@rollup/pluginutils@5.3.0(rollup@4.52.3)':
+ dependencies:
+ '@types/estree': 1.0.8
+ estree-walker: 2.0.2
+ picomatch: 4.0.3
+ optionalDependencies:
+ rollup: 4.52.3
+
+ '@rollup/rollup-android-arm-eabi@4.52.3':
+ optional: true
+
+ '@rollup/rollup-android-arm64@4.52.3':
+ optional: true
+
+ '@rollup/rollup-darwin-arm64@4.52.3':
+ optional: true
+
+ '@rollup/rollup-darwin-x64@4.52.3':
+ optional: true
+
+ '@rollup/rollup-freebsd-arm64@4.52.3':
+ optional: true
+
+ '@rollup/rollup-freebsd-x64@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-arm-gnueabihf@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-arm-musleabihf@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-gnu@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-arm64-musl@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-loong64-gnu@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-ppc64-gnu@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-gnu@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-riscv64-musl@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-s390x-gnu@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-x64-gnu@4.52.3':
+ optional: true
+
+ '@rollup/rollup-linux-x64-musl@4.52.3':
+ optional: true
+
+ '@rollup/rollup-openharmony-arm64@4.52.3':
+ optional: true
+
+ '@rollup/rollup-win32-arm64-msvc@4.52.3':
+ optional: true
+
+ '@rollup/rollup-win32-ia32-msvc@4.52.3':
+ optional: true
+
+ '@rollup/rollup-win32-x64-gnu@4.52.3':
+ optional: true
+
+ '@rollup/rollup-win32-x64-msvc@4.52.3':
+ optional: true
+
+ '@sec-ant/readable-stream@0.4.1': {}
+
+ '@sindresorhus/merge-streams@4.0.0': {}
+
+ '@sxzz/popperjs-es@2.11.7': {}
+
+ '@tailwindcss/node@4.1.14':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.18.3
+ jiti: 2.6.0
+ lightningcss: 1.30.1
+ magic-string: 0.30.19
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.14
+
+ '@tailwindcss/oxide-android-arm64@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.14':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.14':
+ dependencies:
+ detect-libc: 2.1.2
+ tar: 7.5.1
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.14
+ '@tailwindcss/oxide-darwin-arm64': 4.1.14
+ '@tailwindcss/oxide-darwin-x64': 4.1.14
+ '@tailwindcss/oxide-freebsd-x64': 4.1.14
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.14
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.14
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.14
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.14
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.14
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.14
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.14
+
+ '@tailwindcss/vite@4.1.14(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))':
+ dependencies:
+ '@tailwindcss/node': 4.1.14
+ '@tailwindcss/oxide': 4.1.14
+ tailwindcss: 4.1.14
+ vite: 7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+
+ '@transloadit/prettier-bytes@0.0.7': {}
+
+ '@types/conventional-commits-parser@5.0.1':
+ dependencies:
+ '@types/node': 24.8.1
+
+ '@types/estree@1.0.8': {}
+
+ '@types/event-emitter@0.3.5': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/lodash-es@4.17.12':
+ dependencies:
+ '@types/lodash': 4.17.20
+
+ '@types/lodash@4.17.20': {}
+
+ '@types/node@24.8.1':
+ dependencies:
+ undici-types: 7.14.0
+
+ '@types/sortablejs@1.15.8': {}
+
+ '@types/web-bluetooth@0.0.16': {}
+
+ '@types/web-bluetooth@0.0.21': {}
+
+ '@typescript-eslint/eslint-plugin@8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3))(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ '@typescript-eslint/scope-manager': 8.44.1
+ '@typescript-eslint/type-utils': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ '@typescript-eslint/visitor-keys': 8.44.1
+ eslint: 9.36.0(jiti@2.6.0)
+ graphemer: 1.4.0
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.1.0(typescript@5.6.3)
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.44.1
+ '@typescript-eslint/types': 8.44.1
+ '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.6.3)
+ '@typescript-eslint/visitor-keys': 8.44.1
+ debug: 4.4.3
+ eslint: 9.36.0(jiti@2.6.0)
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.44.1(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.44.1(typescript@5.6.3)
+ '@typescript-eslint/types': 8.44.1
+ debug: 4.4.3
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.44.1':
+ dependencies:
+ '@typescript-eslint/types': 8.44.1
+ '@typescript-eslint/visitor-keys': 8.44.1
+
+ '@typescript-eslint/tsconfig-utils@8.44.1(typescript@5.6.3)':
+ dependencies:
+ typescript: 5.6.3
+
+ '@typescript-eslint/type-utils@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.44.1
+ '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.6.3)
+ '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ debug: 4.4.3
+ eslint: 9.36.0(jiti@2.6.0)
+ ts-api-utils: 2.1.0(typescript@5.6.3)
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.44.1': {}
+
+ '@typescript-eslint/typescript-estree@8.44.1(typescript@5.6.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.44.1(typescript@5.6.3)
+ '@typescript-eslint/tsconfig-utils': 8.44.1(typescript@5.6.3)
+ '@typescript-eslint/types': 8.44.1
+ '@typescript-eslint/visitor-keys': 8.44.1
+ debug: 4.4.3
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.2
+ ts-api-utils: 2.1.0(typescript@5.6.3)
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0))
+ '@typescript-eslint/scope-manager': 8.44.1
+ '@typescript-eslint/types': 8.44.1
+ '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.6.3)
+ eslint: 9.36.0(jiti@2.6.0)
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.44.1':
+ dependencies:
+ '@typescript-eslint/types': 8.44.1
+ eslint-visitor-keys: 4.2.1
+
+ '@uppy/companion-client@2.2.2':
+ dependencies:
+ '@uppy/utils': 4.1.3
+ namespace-emitter: 2.0.1
+
+ '@uppy/core@2.3.4':
+ dependencies:
+ '@transloadit/prettier-bytes': 0.0.7
+ '@uppy/store-default': 2.1.1
+ '@uppy/utils': 4.1.3
+ lodash.throttle: 4.1.1
+ mime-match: 1.0.2
+ namespace-emitter: 2.0.1
+ nanoid: 3.3.11
+ preact: 10.27.2
+
+ '@uppy/store-default@2.1.1': {}
+
+ '@uppy/utils@4.1.3':
+ dependencies:
+ lodash.throttle: 4.1.1
+
+ '@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4)':
+ dependencies:
+ '@uppy/companion-client': 2.2.2
+ '@uppy/core': 2.3.4
+ '@uppy/utils': 4.1.3
+ nanoid: 3.3.11
+
+ '@vitejs/plugin-vue@6.0.1(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ '@rolldown/pluginutils': 1.0.0-beta.29
+ vite: 7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ vue: 3.5.22(typescript@5.6.3)
+
+ '@volar/language-core@2.4.23':
+ dependencies:
+ '@volar/source-map': 2.4.23
+
+ '@volar/source-map@2.4.23': {}
+
+ '@volar/typescript@2.4.23':
+ dependencies:
+ '@volar/language-core': 2.4.23
+ path-browserify: 1.0.1
+ vscode-uri: 3.1.0
+
+ '@vue/babel-helper-vue-transform-on@1.5.0': {}
+
+ '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ '@vue/babel-helper-vue-transform-on': 1.5.0
+ '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.28.4)
+ '@vue/shared': 3.5.22
+ optionalDependencies:
+ '@babel/core': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/parser': 7.28.4
+ '@vue/compiler-sfc': 3.5.22
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vue/compiler-core@3.5.22':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@vue/shared': 3.5.22
+ entities: 4.5.0
+ estree-walker: 2.0.2
+ source-map-js: 1.2.1
+
+ '@vue/compiler-dom@3.5.22':
+ dependencies:
+ '@vue/compiler-core': 3.5.22
+ '@vue/shared': 3.5.22
+
+ '@vue/compiler-sfc@3.5.22':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@vue/compiler-core': 3.5.22
+ '@vue/compiler-dom': 3.5.22
+ '@vue/compiler-ssr': 3.5.22
+ '@vue/shared': 3.5.22
+ estree-walker: 2.0.2
+ magic-string: 0.30.19
+ postcss: 8.5.6
+ source-map-js: 1.2.1
+
+ '@vue/compiler-ssr@3.5.22':
+ dependencies:
+ '@vue/compiler-dom': 3.5.22
+ '@vue/shared': 3.5.22
+
+ '@vue/compiler-vue2@2.7.16':
+ dependencies:
+ de-indent: 1.0.2
+ he: 1.2.0
+
+ '@vue/devtools-api@6.6.4': {}
+
+ '@vue/devtools-api@7.7.7':
+ dependencies:
+ '@vue/devtools-kit': 7.7.7
+
+ '@vue/devtools-core@7.7.7(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ '@vue/devtools-kit': 7.7.7
+ '@vue/devtools-shared': 7.7.7
+ mitt: 3.0.1
+ nanoid: 5.1.6
+ pathe: 2.0.3
+ vite-hot-client: 2.1.0(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ vue: 3.5.22(typescript@5.6.3)
+ transitivePeerDependencies:
+ - vite
+
+ '@vue/devtools-kit@7.7.7':
+ dependencies:
+ '@vue/devtools-shared': 7.7.7
+ birpc: 2.6.1
+ hookable: 5.5.3
+ mitt: 3.0.1
+ perfect-debounce: 1.0.0
+ speakingurl: 14.0.1
+ superjson: 2.2.2
+
+ '@vue/devtools-shared@7.7.7':
+ dependencies:
+ rfdc: 1.4.1
+
+ '@vue/language-core@2.1.10(typescript@5.6.3)':
+ dependencies:
+ '@volar/language-core': 2.4.23
+ '@vue/compiler-dom': 3.5.22
+ '@vue/compiler-vue2': 2.7.16
+ '@vue/shared': 3.5.22
+ alien-signals: 0.2.2
+ minimatch: 9.0.5
+ muggle-string: 0.4.1
+ path-browserify: 1.0.1
+ optionalDependencies:
+ typescript: 5.6.3
+
+ '@vue/reactivity@3.5.22':
+ dependencies:
+ '@vue/shared': 3.5.22
+
+ '@vue/runtime-core@3.5.22':
+ dependencies:
+ '@vue/reactivity': 3.5.22
+ '@vue/shared': 3.5.22
+
+ '@vue/runtime-dom@3.5.22':
+ dependencies:
+ '@vue/reactivity': 3.5.22
+ '@vue/runtime-core': 3.5.22
+ '@vue/shared': 3.5.22
+ csstype: 3.1.3
+
+ '@vue/server-renderer@3.5.22(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ '@vue/compiler-ssr': 3.5.22
+ '@vue/shared': 3.5.22
+ vue: 3.5.22(typescript@5.6.3)
+
+ '@vue/shared@3.5.22': {}
+
+ '@vueuse/core@13.9.0(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ '@types/web-bluetooth': 0.0.21
+ '@vueuse/metadata': 13.9.0
+ '@vueuse/shared': 13.9.0(vue@3.5.22(typescript@5.6.3))
+ vue: 3.5.22(typescript@5.6.3)
+
+ '@vueuse/core@9.13.0(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ '@types/web-bluetooth': 0.0.16
+ '@vueuse/metadata': 9.13.0
+ '@vueuse/shared': 9.13.0(vue@3.5.22(typescript@5.6.3))
+ vue-demi: 0.14.10(vue@3.5.22(typescript@5.6.3))
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+
+ '@vueuse/metadata@13.9.0': {}
+
+ '@vueuse/metadata@9.13.0': {}
+
+ '@vueuse/shared@13.9.0(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ vue: 3.5.22(typescript@5.6.3)
+
+ '@vueuse/shared@9.13.0(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ vue-demi: 0.14.10(vue@3.5.22(typescript@5.6.3))
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+ - vue
+
+ '@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)':
+ dependencies:
+ '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ dom7: 3.0.0
+ is-url: 1.2.4
+ lodash.throttle: 4.1.1
+ nanoid: 3.3.11
+ slate: 0.72.8
+ snabbdom: 3.6.2
+
+ '@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)':
+ dependencies:
+ '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ dom7: 3.0.0
+ prismjs: 1.30.0
+ slate: 0.72.8
+ snabbdom: 3.6.2
+
+ '@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)':
+ dependencies:
+ '@types/event-emitter': 0.3.5
+ '@uppy/core': 2.3.4
+ '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
+ dom7: 3.0.0
+ event-emitter: 0.3.5
+ html-void-elements: 2.0.1
+ i18next: 20.6.1
+ is-hotkey: 0.2.0
+ lodash.camelcase: 4.3.0
+ lodash.clonedeep: 4.5.0
+ lodash.debounce: 4.0.8
+ lodash.foreach: 4.5.0
+ lodash.isequal: 4.5.0
+ lodash.throttle: 4.1.1
+ lodash.toarray: 4.4.0
+ nanoid: 3.3.11
+ scroll-into-view-if-needed: 2.2.31
+ slate: 0.72.8
+ slate-history: 0.66.0(slate@0.72.8)
+ snabbdom: 3.6.2
+
+ '@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.5.22(typescript@5.6.3))':
+ dependencies:
+ '@wangeditor/editor': 5.1.23
+ vue: 3.5.22(typescript@5.6.3)
+
+ '@wangeditor/editor@5.1.23':
+ dependencies:
+ '@uppy/core': 2.3.4
+ '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
+ '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ '@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)
+ '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ '@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)
+ '@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ '@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2)
+ '@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ dom7: 3.0.0
+ is-hotkey: 0.2.0
+ lodash.camelcase: 4.3.0
+ lodash.clonedeep: 4.5.0
+ lodash.debounce: 4.0.8
+ lodash.foreach: 4.5.0
+ lodash.isequal: 4.5.0
+ lodash.throttle: 4.1.1
+ lodash.toarray: 4.4.0
+ nanoid: 3.3.11
+ slate: 0.72.8
+ snabbdom: 3.6.2
+
+ '@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)':
+ dependencies:
+ '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ dom7: 3.0.0
+ slate: 0.72.8
+ snabbdom: 3.6.2
+
+ '@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)':
+ dependencies:
+ '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ dom7: 3.0.0
+ lodash.isequal: 4.5.0
+ lodash.throttle: 4.1.1
+ nanoid: 3.3.11
+ slate: 0.72.8
+ snabbdom: 3.6.2
+
+ '@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2)':
+ dependencies:
+ '@uppy/core': 2.3.4
+ '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
+ '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ dom7: 3.0.0
+ lodash.foreach: 4.5.0
+ slate: 0.72.8
+ snabbdom: 3.6.2
+
+ '@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)':
+ dependencies:
+ '@uppy/core': 2.3.4
+ '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4)
+ '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.2)
+ dom7: 3.0.0
+ nanoid: 3.3.11
+ slate: 0.72.8
+ snabbdom: 3.6.2
+
+ JSONStream@1.3.5:
+ dependencies:
+ jsonparse: 1.3.1
+ through: 2.3.8
+
+ acorn-jsx@5.3.2(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn@8.15.0: {}
+
+ adler-32@1.3.1: {}
+
+ ajv@6.12.6:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ajv@8.17.1:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-uri: 3.1.0
+ json-schema-traverse: 1.0.0
+ require-from-string: 2.0.2
+
+ alien-signals@0.2.2: {}
+
+ ansi-escapes@4.3.2:
+ dependencies:
+ type-fest: 0.21.3
+
+ ansi-escapes@7.1.1:
+ dependencies:
+ environment: 1.1.0
+
+ ansi-regex@5.0.1: {}
+
+ ansi-regex@6.2.2: {}
+
+ ansi-styles@3.2.1:
+ dependencies:
+ color-convert: 1.9.3
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.3: {}
+
+ anymatch@3.1.3:
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ argparse@2.0.1: {}
+
+ array-ify@1.0.0: {}
+
+ array-union@2.1.0: {}
+
+ astral-regex@2.0.0: {}
+
+ async-validator@4.2.5: {}
+
+ asynckit@0.4.0: {}
+
+ at-least-node@1.0.0: {}
+
+ axios@1.12.2:
+ dependencies:
+ follow-redirects: 1.15.11
+ form-data: 4.0.4
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+
+ balanced-match@1.0.2: {}
+
+ balanced-match@2.0.0: {}
+
+ base64-js@1.5.1: {}
+
+ baseline-browser-mapping@2.8.8: {}
+
+ binary-extensions@2.3.0: {}
+
+ birpc@2.6.1: {}
+
+ bl@4.1.0:
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+
+ boolbase@1.0.0: {}
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@2.0.2:
+ dependencies:
+ balanced-match: 1.0.2
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.26.2:
+ dependencies:
+ baseline-browser-mapping: 2.8.8
+ caniuse-lite: 1.0.30001745
+ electron-to-chromium: 1.5.227
+ node-releases: 2.0.21
+ update-browserslist-db: 1.1.3(browserslist@4.26.2)
+
+ buffer-from@1.1.2: {}
+
+ buffer@5.7.1:
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+
+ bundle-name@4.1.0:
+ dependencies:
+ run-applescript: 7.1.0
+
+ cacheable@2.0.2:
+ dependencies:
+ '@cacheable/memoize': 2.0.2
+ '@cacheable/memory': 2.0.2
+ '@cacheable/utils': 2.0.2
+ hookified: 1.12.1
+ keyv: 5.5.3
+
+ cachedir@2.3.0: {}
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ callsites@3.1.0: {}
+
+ caniuse-lite@1.0.30001745: {}
+
+ cfb@1.2.2:
+ dependencies:
+ adler-32: 1.3.1
+ crc-32: 1.2.2
+
+ chalk@2.4.2:
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chalk@5.6.2: {}
+
+ chardet@0.7.0: {}
+
+ chokidar@3.6.0:
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.3
+ glob-parent: 5.1.2
+ is-binary-path: 2.1.0
+ is-glob: 4.0.3
+ normalize-path: 3.0.0
+ readdirp: 3.6.0
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
+ chownr@3.0.0: {}
+
+ cli-cursor@3.1.0:
+ dependencies:
+ restore-cursor: 3.1.0
+
+ cli-cursor@5.0.0:
+ dependencies:
+ restore-cursor: 5.1.0
+
+ cli-spinners@2.9.2: {}
+
+ cli-truncate@4.0.0:
+ dependencies:
+ slice-ansi: 5.0.0
+ string-width: 7.2.0
+
+ cli-width@3.0.0: {}
+
+ cliui@8.0.1:
+ dependencies:
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ wrap-ansi: 7.0.0
+
+ clone@1.0.4: {}
+
+ codepage@1.15.0: {}
+
+ color-convert@1.9.3:
+ dependencies:
+ color-name: 1.1.3
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.3: {}
+
+ color-name@1.1.4: {}
+
+ colord@2.9.3: {}
+
+ colorette@2.0.20: {}
+
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
+ commander@13.1.0: {}
+
+ commander@2.20.3: {}
+
+ commitizen@4.3.1(@types/node@24.8.1)(typescript@5.6.3):
+ dependencies:
+ cachedir: 2.3.0
+ cz-conventional-changelog: 3.3.0(@types/node@24.8.1)(typescript@5.6.3)
+ dedent: 0.7.0
+ detect-indent: 6.1.0
+ find-node-modules: 2.1.3
+ find-root: 1.1.0
+ fs-extra: 9.1.0
+ glob: 7.2.3
+ inquirer: 8.2.5
+ is-utf8: 0.2.1
+ lodash: 4.17.21
+ minimist: 1.2.7
+ strip-bom: 4.0.0
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ compare-func@2.0.0:
+ dependencies:
+ array-ify: 1.0.0
+ dot-prop: 5.3.0
+
+ compute-scroll-into-view@1.0.20: {}
+
+ concat-map@0.0.1: {}
+
+ confbox@0.1.8: {}
+
+ confbox@0.2.2: {}
+
+ conventional-changelog-angular@7.0.0:
+ dependencies:
+ compare-func: 2.0.0
+
+ conventional-changelog-conventionalcommits@7.0.2:
+ dependencies:
+ compare-func: 2.0.0
+
+ conventional-commit-types@3.0.0: {}
+
+ conventional-commits-parser@5.0.0:
+ dependencies:
+ JSONStream: 1.3.5
+ is-text-path: 2.0.0
+ meow: 12.1.1
+ split2: 4.2.0
+
+ convert-source-map@2.0.0: {}
+
+ copy-anything@3.0.5:
+ dependencies:
+ is-what: 4.1.16
+
+ core-js@3.45.1: {}
+
+ cosmiconfig-typescript-loader@6.1.0(@types/node@24.8.1)(cosmiconfig@9.0.0(typescript@5.6.3))(typescript@5.6.3):
+ dependencies:
+ '@types/node': 24.8.1
+ cosmiconfig: 9.0.0(typescript@5.6.3)
+ jiti: 2.6.0
+ typescript: 5.6.3
+
+ cosmiconfig@9.0.0(typescript@5.6.3):
+ dependencies:
+ env-paths: 2.2.1
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ parse-json: 5.2.0
+ optionalDependencies:
+ typescript: 5.6.3
+
+ crc-32@1.2.2: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ crypto-js@4.2.0: {}
+
+ css-functions-list@3.2.3: {}
+
+ css-tree@3.1.0:
+ dependencies:
+ mdn-data: 2.12.2
+ source-map-js: 1.2.1
+
+ cssesc@3.0.0: {}
+
+ csstype@3.1.3: {}
+
+ cz-conventional-changelog@3.3.0(@types/node@24.8.1)(typescript@5.6.3):
+ dependencies:
+ chalk: 2.4.2
+ commitizen: 4.3.1(@types/node@24.8.1)(typescript@5.6.3)
+ conventional-commit-types: 3.0.0
+ lodash.map: 4.6.0
+ longest: 2.0.1
+ word-wrap: 1.2.5
+ optionalDependencies:
+ '@commitlint/load': 20.0.0(@types/node@24.8.1)(typescript@5.6.3)
+ transitivePeerDependencies:
+ - '@types/node'
+ - typescript
+
+ cz-git@1.12.0: {}
+
+ d@1.0.2:
+ dependencies:
+ es5-ext: 0.10.64
+ type: 2.7.3
+
+ danmu.js@1.1.13:
+ dependencies:
+ event-emitter: 0.3.5
+
+ dargs@8.1.0: {}
+
+ dayjs@1.11.18: {}
+
+ de-indent@1.0.2: {}
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ dedent@0.7.0: {}
+
+ deep-is@0.1.4: {}
+
+ deep-pick-omit@1.2.1: {}
+
+ default-browser-id@5.0.0: {}
+
+ default-browser@5.2.1:
+ dependencies:
+ bundle-name: 4.1.0
+ default-browser-id: 5.0.0
+
+ defaults@1.0.4:
+ dependencies:
+ clone: 1.0.4
+
+ define-lazy-prop@2.0.0: {}
+
+ define-lazy-prop@3.0.0: {}
+
+ defu@6.1.4: {}
+
+ delayed-stream@1.0.0: {}
+
+ delegate@3.2.0: {}
+
+ destr@2.0.5: {}
+
+ detect-file@1.0.0: {}
+
+ detect-indent@6.1.0: {}
+
+ detect-libc@1.0.3:
+ optional: true
+
+ detect-libc@2.1.2: {}
+
+ dir-glob@3.0.1:
+ dependencies:
+ path-type: 4.0.0
+
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
+ dom7@3.0.0:
+ dependencies:
+ ssr-window: 3.0.0
+
+ domelementtype@2.3.0: {}
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
+ domutils@3.2.2:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dot-prop@5.3.0:
+ dependencies:
+ is-obj: 2.0.0
+
+ downloadjs@1.4.7: {}
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ echarts@6.0.0:
+ dependencies:
+ tslib: 2.3.0
+ zrender: 6.0.0
+
+ electron-to-chromium@1.5.227: {}
+
+ element-plus@2.11.4(vue@3.5.22(typescript@5.6.3)):
+ dependencies:
+ '@ctrl/tinycolor': 3.6.1
+ '@element-plus/icons-vue': 2.3.2(vue@3.5.22(typescript@5.6.3))
+ '@floating-ui/dom': 1.7.4
+ '@popperjs/core': '@sxzz/popperjs-es@2.11.7'
+ '@types/lodash': 4.17.20
+ '@types/lodash-es': 4.17.12
+ '@vueuse/core': 9.13.0(vue@3.5.22(typescript@5.6.3))
+ async-validator: 4.2.5
+ dayjs: 1.11.18
+ escape-html: 1.0.3
+ lodash: 4.17.21
+ lodash-es: 4.17.21
+ lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
+ memoize-one: 6.0.0
+ normalize-wheel-es: 1.2.0
+ vue: 3.5.22(typescript@5.6.3)
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+
+ emoji-regex@10.5.0: {}
+
+ emoji-regex@8.0.0: {}
+
+ enhanced-resolve@5.18.3:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.0
+
+ entities@4.5.0: {}
+
+ env-paths@2.2.1: {}
+
+ environment@1.1.0: {}
+
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ error-stack-parser-es@0.1.5: {}
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-module-lexer@1.7.0: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.2
+
+ es5-ext@0.10.64:
+ dependencies:
+ es6-iterator: 2.0.3
+ es6-symbol: 3.1.4
+ esniff: 2.0.1
+ next-tick: 1.1.0
+
+ es6-iterator@2.0.3:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ es6-symbol: 3.1.4
+
+ es6-symbol@3.1.4:
+ dependencies:
+ d: 1.0.2
+ ext: 1.7.0
+
+ esbuild@0.25.10:
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.25.10
+ '@esbuild/android-arm': 0.25.10
+ '@esbuild/android-arm64': 0.25.10
+ '@esbuild/android-x64': 0.25.10
+ '@esbuild/darwin-arm64': 0.25.10
+ '@esbuild/darwin-x64': 0.25.10
+ '@esbuild/freebsd-arm64': 0.25.10
+ '@esbuild/freebsd-x64': 0.25.10
+ '@esbuild/linux-arm': 0.25.10
+ '@esbuild/linux-arm64': 0.25.10
+ '@esbuild/linux-ia32': 0.25.10
+ '@esbuild/linux-loong64': 0.25.10
+ '@esbuild/linux-mips64el': 0.25.10
+ '@esbuild/linux-ppc64': 0.25.10
+ '@esbuild/linux-riscv64': 0.25.10
+ '@esbuild/linux-s390x': 0.25.10
+ '@esbuild/linux-x64': 0.25.10
+ '@esbuild/netbsd-arm64': 0.25.10
+ '@esbuild/netbsd-x64': 0.25.10
+ '@esbuild/openbsd-arm64': 0.25.10
+ '@esbuild/openbsd-x64': 0.25.10
+ '@esbuild/openharmony-arm64': 0.25.10
+ '@esbuild/sunos-x64': 0.25.10
+ '@esbuild/win32-arm64': 0.25.10
+ '@esbuild/win32-ia32': 0.25.10
+ '@esbuild/win32-x64': 0.25.10
+
+ escalade@3.2.0: {}
+
+ escape-html@1.0.3: {}
+
+ escape-string-regexp@1.0.5: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ escape-string-regexp@5.0.0: {}
+
+ eslint-config-prettier@9.1.2(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ eslint: 9.36.0(jiti@2.6.0)
+
+ eslint-plugin-prettier@5.5.4(eslint-config-prettier@9.1.2(eslint@9.36.0(jiti@2.6.0)))(eslint@9.36.0(jiti@2.6.0))(prettier@3.6.2):
+ dependencies:
+ eslint: 9.36.0(jiti@2.6.0)
+ prettier: 3.6.2
+ prettier-linter-helpers: 1.0.0
+ synckit: 0.11.11
+ optionalDependencies:
+ eslint-config-prettier: 9.1.2(eslint@9.36.0(jiti@2.6.0))
+
+ eslint-plugin-vue@9.33.0(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0))
+ eslint: 9.36.0(jiti@2.6.0)
+ globals: 13.24.0
+ natural-compare: 1.4.0
+ nth-check: 2.1.1
+ postcss-selector-parser: 6.1.2
+ semver: 7.7.2
+ vue-eslint-parser: 9.4.3(eslint@9.36.0(jiti@2.6.0))
+ xml-name-validator: 4.0.0
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-scope@7.2.2:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-scope@8.4.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.1: {}
+
+ eslint@9.36.0(jiti@2.6.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0))
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/config-array': 0.21.0
+ '@eslint/config-helpers': 0.3.1
+ '@eslint/core': 0.15.2
+ '@eslint/eslintrc': 3.3.1
+ '@eslint/js': 9.36.0
+ '@eslint/plugin-kit': 0.3.5
+ '@humanfs/node': 0.16.7
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.8
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.6.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.6.0
+ transitivePeerDependencies:
+ - supports-color
+
+ esniff@2.0.1:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+ event-emitter: 0.3.5
+ type: 2.7.3
+
+ espree@10.4.0:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 4.2.1
+
+ espree@9.6.1:
+ dependencies:
+ acorn: 8.15.0
+ acorn-jsx: 5.3.2(acorn@8.15.0)
+ eslint-visitor-keys: 3.4.3
+
+ esquery@1.6.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ estree-walker@2.0.2: {}
+
+ estree-walker@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
+ esutils@2.0.3: {}
+
+ event-emitter@0.3.5:
+ dependencies:
+ d: 1.0.2
+ es5-ext: 0.10.64
+
+ eventemitter3@4.0.7: {}
+
+ eventemitter3@5.0.1: {}
+
+ execa@8.0.1:
+ dependencies:
+ cross-spawn: 7.0.6
+ get-stream: 8.0.1
+ human-signals: 5.0.0
+ is-stream: 3.0.0
+ merge-stream: 2.0.0
+ npm-run-path: 5.3.0
+ onetime: 6.0.0
+ signal-exit: 4.1.0
+ strip-final-newline: 3.0.0
+
+ execa@9.6.0:
+ dependencies:
+ '@sindresorhus/merge-streams': 4.0.0
+ cross-spawn: 7.0.6
+ figures: 6.1.0
+ get-stream: 9.0.1
+ human-signals: 8.0.1
+ is-plain-obj: 4.1.0
+ is-stream: 4.0.1
+ npm-run-path: 6.0.0
+ pretty-ms: 9.3.0
+ signal-exit: 4.1.0
+ strip-final-newline: 4.0.0
+ yoctocolors: 2.1.2
+
+ expand-tilde@2.0.2:
+ dependencies:
+ homedir-polyfill: 1.0.3
+
+ exsolve@1.0.7: {}
+
+ ext@1.7.0:
+ dependencies:
+ type: 2.7.3
+
+ external-editor@3.1.0:
+ dependencies:
+ chardet: 0.7.0
+ iconv-lite: 0.4.24
+ tmp: 0.0.33
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-diff@1.3.0: {}
+
+ fast-glob@3.3.3:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fast-uri@3.1.0: {}
+
+ fastest-levenshtein@1.0.16: {}
+
+ fastq@1.19.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fdir@6.5.0(picomatch@4.0.3):
+ optionalDependencies:
+ picomatch: 4.0.3
+
+ figures@3.2.0:
+ dependencies:
+ escape-string-regexp: 1.0.5
+
+ figures@6.1.0:
+ dependencies:
+ is-unicode-supported: 2.1.0
+
+ file-entry-cache@10.1.4:
+ dependencies:
+ flat-cache: 6.1.14
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ file-saver@2.0.5: {}
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-node-modules@2.1.3:
+ dependencies:
+ findup-sync: 4.0.0
+ merge: 2.1.1
+
+ find-root@1.1.0: {}
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ find-up@7.0.0:
+ dependencies:
+ locate-path: 7.2.0
+ path-exists: 5.0.0
+ unicorn-magic: 0.1.0
+
+ findup-sync@4.0.0:
+ dependencies:
+ detect-file: 1.0.0
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ resolve-dir: 1.0.1
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.3.3
+ keyv: 4.5.4
+
+ flat-cache@6.1.14:
+ dependencies:
+ cacheable: 2.0.2
+ flatted: 3.3.3
+ hookified: 1.12.1
+
+ flatted@3.3.3: {}
+
+ follow-redirects@1.15.11: {}
+
+ form-data@4.0.4:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ es-set-tostringtag: 2.1.0
+ hasown: 2.0.2
+ mime-types: 2.1.35
+
+ frac@1.1.2: {}
+
+ fs-extra@10.1.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fs-extra@11.3.2:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fs-extra@9.1.0:
+ dependencies:
+ at-least-node: 1.0.0
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
+ fs.realpath@1.0.0: {}
+
+ fsevents@2.3.3:
+ optional: true
+
+ function-bind@1.1.2: {}
+
+ gensync@1.0.0-beta.2: {}
+
+ get-caller-file@2.0.5: {}
+
+ get-east-asian-width@1.4.0: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-stream@8.0.1: {}
+
+ get-stream@9.0.1:
+ dependencies:
+ '@sec-ant/readable-stream': 0.4.1
+ is-stream: 4.0.1
+
+ get-tsconfig@4.10.1:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ git-raw-commits@4.0.0:
+ dependencies:
+ dargs: 8.1.0
+ meow: 12.1.1
+ split2: 4.2.0
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob@7.2.3:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ global-directory@4.0.1:
+ dependencies:
+ ini: 4.1.1
+
+ global-modules@1.0.0:
+ dependencies:
+ global-prefix: 1.0.2
+ is-windows: 1.0.2
+ resolve-dir: 1.0.1
+
+ global-modules@2.0.0:
+ dependencies:
+ global-prefix: 3.0.0
+
+ global-prefix@1.0.2:
+ dependencies:
+ expand-tilde: 2.0.2
+ homedir-polyfill: 1.0.3
+ ini: 1.3.8
+ is-windows: 1.0.2
+ which: 1.3.1
+
+ global-prefix@3.0.0:
+ dependencies:
+ ini: 1.3.8
+ kind-of: 6.0.3
+ which: 1.3.1
+
+ globals@13.24.0:
+ dependencies:
+ type-fest: 0.20.2
+
+ globals@14.0.0: {}
+
+ globals@15.15.0: {}
+
+ globby@11.1.0:
+ dependencies:
+ array-union: 2.1.0
+ dir-glob: 3.0.1
+ fast-glob: 3.3.3
+ ignore: 5.3.2
+ merge2: 1.4.1
+ slash: 3.0.0
+
+ globjoin@0.1.4: {}
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ graphemer@1.4.0: {}
+
+ has-flag@3.0.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ he@1.2.0: {}
+
+ highlight.js@11.11.1: {}
+
+ homedir-polyfill@1.0.3:
+ dependencies:
+ parse-passwd: 1.0.0
+
+ hookable@5.5.3: {}
+
+ hookified@1.12.1: {}
+
+ html-tags@3.3.1: {}
+
+ html-void-elements@2.0.1: {}
+
+ htmlparser2@8.0.2:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ entities: 4.5.0
+
+ human-signals@5.0.0: {}
+
+ human-signals@8.0.1: {}
+
+ husky@9.1.7: {}
+
+ i18next@20.6.1:
+ dependencies:
+ '@babel/runtime': 7.28.4
+
+ iconv-lite@0.4.24:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ieee754@1.2.1: {}
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ immer@9.0.21: {}
+
+ immutable@5.1.3: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ import-meta-resolve@4.2.0: {}
+
+ imurmurhash@0.1.4: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ini@1.3.8: {}
+
+ ini@4.1.1: {}
+
+ inquirer@8.2.5:
+ dependencies:
+ ansi-escapes: 4.3.2
+ chalk: 4.1.2
+ cli-cursor: 3.1.0
+ cli-width: 3.0.0
+ external-editor: 3.1.0
+ figures: 3.2.0
+ lodash: 4.17.21
+ mute-stream: 0.0.8
+ ora: 5.4.1
+ run-async: 2.4.1
+ rxjs: 7.8.2
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+ through: 2.3.8
+ wrap-ansi: 7.0.0
+
+ is-arrayish@0.2.1: {}
+
+ is-binary-path@2.1.0:
+ dependencies:
+ binary-extensions: 2.3.0
+
+ is-docker@2.2.1: {}
+
+ is-docker@3.0.0: {}
+
+ is-extglob@2.1.1: {}
+
+ is-fullwidth-code-point@3.0.0: {}
+
+ is-fullwidth-code-point@4.0.0: {}
+
+ is-fullwidth-code-point@5.1.0:
+ dependencies:
+ get-east-asian-width: 1.4.0
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-hotkey@0.2.0: {}
+
+ is-inside-container@1.0.0:
+ dependencies:
+ is-docker: 3.0.0
+
+ is-interactive@1.0.0: {}
+
+ is-number@7.0.0: {}
+
+ is-obj@2.0.0: {}
+
+ is-plain-obj@4.1.0: {}
+
+ is-plain-object@5.0.0: {}
+
+ is-stream@3.0.0: {}
+
+ is-stream@4.0.1: {}
+
+ is-text-path@2.0.0:
+ dependencies:
+ text-extensions: 2.4.0
+
+ is-unicode-supported@0.1.0: {}
+
+ is-unicode-supported@2.1.0: {}
+
+ is-url@1.2.4: {}
+
+ is-utf8@0.2.1: {}
+
+ is-what@4.1.16: {}
+
+ is-windows@1.0.2: {}
+
+ is-wsl@2.2.0:
+ dependencies:
+ is-docker: 2.2.1
+
+ is-wsl@3.1.0:
+ dependencies:
+ is-inside-container: 1.0.0
+
+ isexe@2.0.0: {}
+
+ jiti@2.6.0: {}
+
+ js-tokens@4.0.0: {}
+
+ js-tokens@9.0.1: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-parse-even-better-errors@2.3.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-schema-traverse@1.0.0: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@2.2.3: {}
+
+ jsonfile@6.2.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ jsonparse@1.3.1: {}
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ keyv@5.5.3:
+ dependencies:
+ '@keyv/serialize': 1.1.1
+
+ kind-of@6.0.3: {}
+
+ known-css-properties@0.36.0: {}
+
+ known-css-properties@0.37.0: {}
+
+ kolorist@1.8.0: {}
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lightningcss-darwin-arm64@1.30.1:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.1:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.1:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ optional: true
+
+ lightningcss@1.30.1:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.30.1
+ lightningcss-darwin-x64: 1.30.1
+ lightningcss-freebsd-x64: 1.30.1
+ lightningcss-linux-arm-gnueabihf: 1.30.1
+ lightningcss-linux-arm64-gnu: 1.30.1
+ lightningcss-linux-arm64-musl: 1.30.1
+ lightningcss-linux-x64-gnu: 1.30.1
+ lightningcss-linux-x64-musl: 1.30.1
+ lightningcss-win32-arm64-msvc: 1.30.1
+ lightningcss-win32-x64-msvc: 1.30.1
+
+ lilconfig@3.1.3: {}
+
+ lines-and-columns@1.2.4: {}
+
+ lint-staged@15.5.2:
+ dependencies:
+ chalk: 5.6.2
+ commander: 13.1.0
+ debug: 4.4.3
+ execa: 8.0.1
+ lilconfig: 3.1.3
+ listr2: 8.3.3
+ micromatch: 4.0.8
+ pidtree: 0.6.0
+ string-argv: 0.3.2
+ yaml: 2.8.1
+ transitivePeerDependencies:
+ - supports-color
+
+ listr2@8.3.3:
+ dependencies:
+ cli-truncate: 4.0.0
+ colorette: 2.0.20
+ eventemitter3: 5.0.1
+ log-update: 6.1.0
+ rfdc: 1.4.1
+ wrap-ansi: 9.0.2
+
+ local-pkg@1.1.2:
+ dependencies:
+ mlly: 1.8.0
+ pkg-types: 2.3.0
+ quansync: 0.2.11
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ locate-path@7.2.0:
+ dependencies:
+ p-locate: 6.0.0
+
+ lodash-es@4.17.21: {}
+
+ lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21):
+ dependencies:
+ '@types/lodash-es': 4.17.12
+ lodash: 4.17.21
+ lodash-es: 4.17.21
+
+ lodash.camelcase@4.3.0: {}
+
+ lodash.clonedeep@4.5.0: {}
+
+ lodash.debounce@4.0.8: {}
+
+ lodash.foreach@4.5.0: {}
+
+ lodash.isequal@4.5.0: {}
+
+ lodash.isplainobject@4.0.6: {}
+
+ lodash.kebabcase@4.1.1: {}
+
+ lodash.map@4.6.0: {}
+
+ lodash.merge@4.6.2: {}
+
+ lodash.mergewith@4.6.2: {}
+
+ lodash.snakecase@4.1.1: {}
+
+ lodash.startcase@4.4.0: {}
+
+ lodash.throttle@4.1.1: {}
+
+ lodash.toarray@4.4.0: {}
+
+ lodash.truncate@4.4.2: {}
+
+ lodash.uniq@4.5.0: {}
+
+ lodash.upperfirst@4.3.1: {}
+
+ lodash@4.17.21: {}
+
+ log-symbols@4.1.0:
+ dependencies:
+ chalk: 4.1.2
+ is-unicode-supported: 0.1.0
+
+ log-update@6.1.0:
+ dependencies:
+ ansi-escapes: 7.1.1
+ cli-cursor: 5.0.0
+ slice-ansi: 7.1.2
+ strip-ansi: 7.1.2
+ wrap-ansi: 9.0.2
+
+ longest@2.0.1: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ magic-string@0.30.19:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ math-intrinsics@1.1.0: {}
+
+ mathml-tag-names@2.1.3: {}
+
+ mdn-data@2.12.2: {}
+
+ mdn-data@2.24.0: {}
+
+ memoize-one@6.0.0: {}
+
+ meow@12.1.1: {}
+
+ meow@13.2.0: {}
+
+ merge-stream@2.0.0: {}
+
+ merge2@1.4.1: {}
+
+ merge@2.1.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.1
+
+ mime-db@1.52.0: {}
+
+ mime-match@1.0.2:
+ dependencies:
+ wildcard: 1.1.2
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
+ mimic-fn@2.1.0: {}
+
+ mimic-fn@4.0.0: {}
+
+ mimic-function@5.0.1: {}
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimist@1.2.7: {}
+
+ minimist@1.2.8: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@3.1.0:
+ dependencies:
+ minipass: 7.1.2
+
+ mitt@3.0.1: {}
+
+ mlly@1.8.0:
+ dependencies:
+ acorn: 8.15.0
+ pathe: 2.0.3
+ pkg-types: 1.3.1
+ ufo: 1.6.1
+
+ mrmime@2.0.1: {}
+
+ ms@2.1.3: {}
+
+ muggle-string@0.4.1: {}
+
+ mute-stream@0.0.8: {}
+
+ namespace-emitter@2.0.1: {}
+
+ nanoid@3.3.11: {}
+
+ nanoid@5.1.6: {}
+
+ natural-compare@1.4.0: {}
+
+ next-tick@1.1.0: {}
+
+ node-addon-api@7.1.1:
+ optional: true
+
+ node-releases@2.0.21: {}
+
+ normalize-path@3.0.0: {}
+
+ normalize-wheel-es@1.2.0: {}
+
+ npm-run-path@5.3.0:
+ dependencies:
+ path-key: 4.0.0
+
+ npm-run-path@6.0.0:
+ dependencies:
+ path-key: 4.0.0
+ unicorn-magic: 0.3.0
+
+ nprogress@0.2.0: {}
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ ohash@2.0.11: {}
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ onetime@5.1.2:
+ dependencies:
+ mimic-fn: 2.1.0
+
+ onetime@6.0.0:
+ dependencies:
+ mimic-fn: 4.0.0
+
+ onetime@7.0.0:
+ dependencies:
+ mimic-function: 5.0.1
+
+ open@10.2.0:
+ dependencies:
+ default-browser: 5.2.1
+ define-lazy-prop: 3.0.0
+ is-inside-container: 1.0.0
+ wsl-utils: 0.1.0
+
+ open@8.4.2:
+ dependencies:
+ define-lazy-prop: 2.0.0
+ is-docker: 2.2.1
+ is-wsl: 2.2.0
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ ora@5.4.1:
+ dependencies:
+ bl: 4.1.0
+ chalk: 4.1.2
+ cli-cursor: 3.1.0
+ cli-spinners: 2.9.2
+ is-interactive: 1.0.0
+ is-unicode-supported: 0.1.0
+ log-symbols: 4.1.0
+ strip-ansi: 6.0.1
+ wcwidth: 1.0.1
+
+ os-tmpdir@1.0.2: {}
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-limit@4.0.0:
+ dependencies:
+ yocto-queue: 1.2.1
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ p-locate@6.0.0:
+ dependencies:
+ p-limit: 4.0.0
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
+ parse-ms@4.0.0: {}
+
+ parse-passwd@1.0.0: {}
+
+ path-browserify@1.0.1: {}
+
+ path-exists@4.0.0: {}
+
+ path-exists@5.0.0: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-key@3.1.1: {}
+
+ path-key@4.0.0: {}
+
+ path-type@4.0.0: {}
+
+ pathe@2.0.3: {}
+
+ perfect-debounce@1.0.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ pidtree@0.6.0: {}
+
+ pinia-plugin-persistedstate@4.5.0(pinia@3.0.3(typescript@5.6.3)(vue@3.5.22(typescript@5.6.3))):
+ dependencies:
+ deep-pick-omit: 1.2.1
+ defu: 6.1.4
+ destr: 2.0.5
+ optionalDependencies:
+ pinia: 3.0.3(typescript@5.6.3)(vue@3.5.22(typescript@5.6.3))
+
+ pinia@3.0.3(typescript@5.6.3)(vue@3.5.22(typescript@5.6.3)):
+ dependencies:
+ '@vue/devtools-api': 7.7.7
+ vue: 3.5.22(typescript@5.6.3)
+ optionalDependencies:
+ typescript: 5.6.3
+
+ pkg-types@1.3.1:
+ dependencies:
+ confbox: 0.1.8
+ mlly: 1.8.0
+ pathe: 2.0.3
+
+ pkg-types@2.3.0:
+ dependencies:
+ confbox: 0.2.2
+ exsolve: 1.0.7
+ pathe: 2.0.3
+
+ postcss-html@1.8.0:
+ dependencies:
+ htmlparser2: 8.0.2
+ js-tokens: 9.0.1
+ postcss: 8.5.6
+ postcss-safe-parser: 6.0.0(postcss@8.5.6)
+
+ postcss-media-query-parser@0.2.3: {}
+
+ postcss-resolve-nested-selector@0.1.6: {}
+
+ postcss-safe-parser@6.0.0(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
+ postcss-safe-parser@7.0.1(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
+ postcss-scss@4.0.9(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
+ postcss-selector-parser@6.1.2:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-selector-parser@7.1.0:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ postcss-sorting@8.0.2(postcss@8.5.6):
+ dependencies:
+ postcss: 8.5.6
+
+ postcss-value-parser@4.2.0: {}
+
+ postcss@8.5.6:
+ dependencies:
+ nanoid: 3.3.11
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ preact@10.27.2: {}
+
+ prelude-ls@1.2.1: {}
+
+ prettier-linter-helpers@1.0.0:
+ dependencies:
+ fast-diff: 1.3.0
+
+ prettier@3.6.2: {}
+
+ pretty-ms@9.3.0:
+ dependencies:
+ parse-ms: 4.0.0
+
+ prismjs@1.30.0: {}
+
+ proxy-from-env@1.1.0: {}
+
+ punycode@2.3.1: {}
+
+ qrcode.vue@3.6.0(vue@3.5.22(typescript@5.6.3)):
+ dependencies:
+ vue: 3.5.22(typescript@5.6.3)
+
+ quansync@0.2.11: {}
+
+ queue-microtask@1.2.3: {}
+
+ readable-stream@3.6.2:
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+
+ readdirp@3.6.0:
+ dependencies:
+ picomatch: 2.3.1
+
+ readdirp@4.1.2: {}
+
+ require-directory@2.1.1: {}
+
+ require-from-string@2.0.2: {}
+
+ resolve-dir@1.0.1:
+ dependencies:
+ expand-tilde: 2.0.2
+ global-modules: 1.0.0
+
+ resolve-from@4.0.0: {}
+
+ resolve-from@5.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ restore-cursor@3.1.0:
+ dependencies:
+ onetime: 5.1.2
+ signal-exit: 3.0.7
+
+ restore-cursor@5.1.0:
+ dependencies:
+ onetime: 7.0.0
+ signal-exit: 4.1.0
+
+ reusify@1.1.0: {}
+
+ rfdc@1.4.1: {}
+
+ rollup-plugin-visualizer@5.14.0(rollup@4.52.3):
+ dependencies:
+ open: 8.4.2
+ picomatch: 4.0.3
+ source-map: 0.7.6
+ yargs: 17.7.2
+ optionalDependencies:
+ rollup: 4.52.3
+
+ rollup@4.52.3:
+ dependencies:
+ '@types/estree': 1.0.8
+ optionalDependencies:
+ '@rollup/rollup-android-arm-eabi': 4.52.3
+ '@rollup/rollup-android-arm64': 4.52.3
+ '@rollup/rollup-darwin-arm64': 4.52.3
+ '@rollup/rollup-darwin-x64': 4.52.3
+ '@rollup/rollup-freebsd-arm64': 4.52.3
+ '@rollup/rollup-freebsd-x64': 4.52.3
+ '@rollup/rollup-linux-arm-gnueabihf': 4.52.3
+ '@rollup/rollup-linux-arm-musleabihf': 4.52.3
+ '@rollup/rollup-linux-arm64-gnu': 4.52.3
+ '@rollup/rollup-linux-arm64-musl': 4.52.3
+ '@rollup/rollup-linux-loong64-gnu': 4.52.3
+ '@rollup/rollup-linux-ppc64-gnu': 4.52.3
+ '@rollup/rollup-linux-riscv64-gnu': 4.52.3
+ '@rollup/rollup-linux-riscv64-musl': 4.52.3
+ '@rollup/rollup-linux-s390x-gnu': 4.52.3
+ '@rollup/rollup-linux-x64-gnu': 4.52.3
+ '@rollup/rollup-linux-x64-musl': 4.52.3
+ '@rollup/rollup-openharmony-arm64': 4.52.3
+ '@rollup/rollup-win32-arm64-msvc': 4.52.3
+ '@rollup/rollup-win32-ia32-msvc': 4.52.3
+ '@rollup/rollup-win32-x64-gnu': 4.52.3
+ '@rollup/rollup-win32-x64-msvc': 4.52.3
+ fsevents: 2.3.3
+
+ run-applescript@7.1.0: {}
+
+ run-async@2.4.1: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ rxjs@7.8.2:
+ dependencies:
+ tslib: 2.8.1
+
+ safe-buffer@5.2.1: {}
+
+ safer-buffer@2.1.2: {}
+
+ sass@1.93.2:
+ dependencies:
+ chokidar: 4.0.3
+ immutable: 5.1.3
+ source-map-js: 1.2.1
+ optionalDependencies:
+ '@parcel/watcher': 2.5.1
+
+ scroll-into-view-if-needed@2.2.31:
+ dependencies:
+ compute-scroll-into-view: 1.0.20
+
+ scule@1.3.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.7.2: {}
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ signal-exit@3.0.7: {}
+
+ signal-exit@4.1.0: {}
+
+ sirv@3.0.2:
+ dependencies:
+ '@polka/url': 1.0.0-next.29
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
+ slash@3.0.0: {}
+
+ slate-history@0.66.0(slate@0.72.8):
+ dependencies:
+ is-plain-object: 5.0.0
+ slate: 0.72.8
+
+ slate@0.72.8:
+ dependencies:
+ immer: 9.0.21
+ is-plain-object: 5.0.0
+ tiny-warning: 1.0.3
+
+ slice-ansi@4.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ astral-regex: 2.0.0
+ is-fullwidth-code-point: 3.0.0
+
+ slice-ansi@5.0.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 4.0.0
+
+ slice-ansi@7.1.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 5.1.0
+
+ snabbdom@3.6.2: {}
+
+ source-map-js@1.2.1: {}
+
+ source-map-support@0.5.21:
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ source-map@0.6.1: {}
+
+ source-map@0.7.6: {}
+
+ speakingurl@14.0.1: {}
+
+ split2@4.2.0: {}
+
+ ssf@0.11.2:
+ dependencies:
+ frac: 1.1.2
+
+ ssr-window@3.0.0: {}
+
+ string-argv@0.3.2: {}
+
+ string-width@4.2.3:
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.1
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.5.0
+ get-east-asian-width: 1.4.0
+ strip-ansi: 7.1.2
+
+ string_decoder@1.3.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ strip-ansi@6.0.1:
+ dependencies:
+ ansi-regex: 5.0.1
+
+ strip-ansi@7.1.2:
+ dependencies:
+ ansi-regex: 6.2.2
+
+ strip-bom@4.0.0: {}
+
+ strip-final-newline@3.0.0: {}
+
+ strip-final-newline@4.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ strip-literal@3.1.0:
+ dependencies:
+ js-tokens: 9.0.1
+
+ stylelint-config-html@1.1.0(postcss-html@1.8.0)(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ postcss-html: 1.8.0
+ stylelint: 16.24.0(typescript@5.6.3)
+
+ stylelint-config-recess-order@4.6.0(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ stylelint: 16.24.0(typescript@5.6.3)
+ stylelint-order: 6.0.4(stylelint@16.24.0(typescript@5.6.3))
+
+ stylelint-config-recommended-scss@14.1.0(postcss@8.5.6)(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ postcss-scss: 4.0.9(postcss@8.5.6)
+ stylelint: 16.24.0(typescript@5.6.3)
+ stylelint-config-recommended: 14.0.1(stylelint@16.24.0(typescript@5.6.3))
+ stylelint-scss: 6.12.1(stylelint@16.24.0(typescript@5.6.3))
+ optionalDependencies:
+ postcss: 8.5.6
+
+ stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.0)(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ postcss-html: 1.8.0
+ semver: 7.7.2
+ stylelint: 16.24.0(typescript@5.6.3)
+ stylelint-config-html: 1.1.0(postcss-html@1.8.0)(stylelint@16.24.0(typescript@5.6.3))
+ stylelint-config-recommended: 17.0.0(stylelint@16.24.0(typescript@5.6.3))
+
+ stylelint-config-recommended@14.0.1(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ stylelint: 16.24.0(typescript@5.6.3)
+
+ stylelint-config-recommended@17.0.0(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ stylelint: 16.24.0(typescript@5.6.3)
+
+ stylelint-config-standard@36.0.1(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ stylelint: 16.24.0(typescript@5.6.3)
+ stylelint-config-recommended: 14.0.1(stylelint@16.24.0(typescript@5.6.3))
+
+ stylelint-order@6.0.4(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ postcss: 8.5.6
+ postcss-sorting: 8.0.2(postcss@8.5.6)
+ stylelint: 16.24.0(typescript@5.6.3)
+
+ stylelint-scss@6.12.1(stylelint@16.24.0(typescript@5.6.3)):
+ dependencies:
+ css-tree: 3.1.0
+ is-plain-object: 5.0.0
+ known-css-properties: 0.36.0
+ mdn-data: 2.24.0
+ postcss-media-query-parser: 0.2.3
+ postcss-resolve-nested-selector: 0.1.6
+ postcss-selector-parser: 7.1.0
+ postcss-value-parser: 4.2.0
+ stylelint: 16.24.0(typescript@5.6.3)
+
+ stylelint@16.24.0(typescript@5.6.3):
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+ '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0)
+ '@dual-bundle/import-meta-resolve': 4.2.1
+ balanced-match: 2.0.0
+ colord: 2.9.3
+ cosmiconfig: 9.0.0(typescript@5.6.3)
+ css-functions-list: 3.2.3
+ css-tree: 3.1.0
+ debug: 4.4.3
+ fast-glob: 3.3.3
+ fastest-levenshtein: 1.0.16
+ file-entry-cache: 10.1.4
+ global-modules: 2.0.0
+ globby: 11.1.0
+ globjoin: 0.1.4
+ html-tags: 3.3.1
+ ignore: 7.0.5
+ imurmurhash: 0.1.4
+ is-plain-object: 5.0.0
+ known-css-properties: 0.37.0
+ mathml-tag-names: 2.1.3
+ meow: 13.2.0
+ micromatch: 4.0.8
+ normalize-path: 3.0.0
+ picocolors: 1.1.1
+ postcss: 8.5.6
+ postcss-resolve-nested-selector: 0.1.6
+ postcss-safe-parser: 7.0.1(postcss@8.5.6)
+ postcss-selector-parser: 7.1.0
+ postcss-value-parser: 4.2.0
+ resolve-from: 5.0.0
+ string-width: 4.2.3
+ supports-hyperlinks: 3.2.0
+ svg-tags: 1.0.0
+ table: 6.9.0
+ write-file-atomic: 5.0.1
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ superjson@2.2.2:
+ dependencies:
+ copy-anything: 3.0.5
+
+ supports-color@5.5.0:
+ dependencies:
+ has-flag: 3.0.0
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-hyperlinks@3.2.0:
+ dependencies:
+ has-flag: 4.0.0
+ supports-color: 7.2.0
+
+ svg-tags@1.0.0: {}
+
+ synckit@0.11.11:
+ dependencies:
+ '@pkgr/core': 0.2.9
+
+ table@6.9.0:
+ dependencies:
+ ajv: 8.17.1
+ lodash.truncate: 4.4.2
+ slice-ansi: 4.0.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ tailwindcss@4.1.14: {}
+
+ tapable@2.3.0: {}
+
+ tar@7.5.1:
+ dependencies:
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.2
+ minizlib: 3.1.0
+ yallist: 5.0.0
+
+ terser@5.44.0:
+ dependencies:
+ '@jridgewell/source-map': 0.3.11
+ acorn: 8.15.0
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
+ text-extensions@2.4.0: {}
+
+ through@2.3.8: {}
+
+ tiny-warning@1.0.3: {}
+
+ tinyexec@1.0.1: {}
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ tmp@0.0.33:
+ dependencies:
+ os-tmpdir: 1.0.2
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ totalist@3.0.1: {}
+
+ ts-api-utils@2.1.0(typescript@5.6.3):
+ dependencies:
+ typescript: 5.6.3
+
+ tslib@2.3.0: {}
+
+ tslib@2.8.1: {}
+
+ tsx@4.20.6:
+ dependencies:
+ esbuild: 0.25.10
+ get-tsconfig: 4.10.1
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ type-fest@0.20.2: {}
+
+ type-fest@0.21.3: {}
+
+ type@2.7.3: {}
+
+ typescript-eslint@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.44.1(@typescript-eslint/parser@8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3))(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ '@typescript-eslint/parser': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ '@typescript-eslint/typescript-estree': 8.44.1(typescript@5.6.3)
+ '@typescript-eslint/utils': 8.44.1(eslint@9.36.0(jiti@2.6.0))(typescript@5.6.3)
+ eslint: 9.36.0(jiti@2.6.0)
+ typescript: 5.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@5.6.3: {}
+
+ ufo@1.6.1: {}
+
+ undici-types@7.14.0: {}
+
+ unicorn-magic@0.1.0: {}
+
+ unicorn-magic@0.3.0: {}
+
+ unimport@5.4.0:
+ dependencies:
+ acorn: 8.15.0
+ escape-string-regexp: 5.0.0
+ estree-walker: 3.0.3
+ local-pkg: 1.1.2
+ magic-string: 0.30.19
+ mlly: 1.8.0
+ pathe: 2.0.3
+ picomatch: 4.0.3
+ pkg-types: 2.3.0
+ scule: 1.3.0
+ strip-literal: 3.1.0
+ tinyglobby: 0.2.15
+ unplugin: 2.3.10
+ unplugin-utils: 0.3.0
+
+ universalify@2.0.1: {}
+
+ unplugin-auto-import@20.2.0(@vueuse/core@13.9.0(vue@3.5.22(typescript@5.6.3))):
+ dependencies:
+ local-pkg: 1.1.2
+ magic-string: 0.30.19
+ picomatch: 4.0.3
+ unimport: 5.4.0
+ unplugin: 2.3.10
+ unplugin-utils: 0.3.0
+ optionalDependencies:
+ '@vueuse/core': 13.9.0(vue@3.5.22(typescript@5.6.3))
+
+ unplugin-element-plus@0.10.0:
+ dependencies:
+ es-module-lexer: 1.7.0
+ magic-string: 0.30.19
+ unplugin: 2.3.10
+ unplugin-utils: 0.2.5
+
+ unplugin-utils@0.2.5:
+ dependencies:
+ pathe: 2.0.3
+ picomatch: 4.0.3
+
+ unplugin-utils@0.3.0:
+ dependencies:
+ pathe: 2.0.3
+ picomatch: 4.0.3
+
+ unplugin-vue-components@29.1.0(@babel/parser@7.28.4)(vue@3.5.22(typescript@5.6.3)):
+ dependencies:
+ chokidar: 3.6.0
+ debug: 4.4.3
+ local-pkg: 1.1.2
+ magic-string: 0.30.19
+ mlly: 1.8.0
+ tinyglobby: 0.2.15
+ unplugin: 2.3.10
+ unplugin-utils: 0.3.0
+ vue: 3.5.22(typescript@5.6.3)
+ optionalDependencies:
+ '@babel/parser': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ unplugin@2.3.10:
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ acorn: 8.15.0
+ picomatch: 4.0.3
+ webpack-virtual-modules: 0.6.2
+
+ update-browserslist-db@1.1.3(browserslist@4.26.2):
+ dependencies:
+ browserslist: 4.26.2
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ util-deprecate@1.0.2: {}
+
+ vite-hot-client@2.1.0(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
+ dependencies:
+ vite: 7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+
+ vite-plugin-compression@0.5.1(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
+ dependencies:
+ chalk: 4.1.2
+ debug: 4.4.3
+ fs-extra: 10.1.0
+ vite: 7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ vite-plugin-inspect@0.8.9(rollup@4.52.3)(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
+ dependencies:
+ '@antfu/utils': 0.7.10
+ '@rollup/pluginutils': 5.3.0(rollup@4.52.3)
+ debug: 4.4.3
+ error-stack-parser-es: 0.1.5
+ fs-extra: 11.3.2
+ open: 10.2.0
+ perfect-debounce: 1.0.0
+ picocolors: 1.1.1
+ sirv: 3.0.2
+ vite: 7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ transitivePeerDependencies:
+ - rollup
+ - supports-color
+
+ vite-plugin-vue-devtools@7.7.7(rollup@4.52.3)(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.6.3)):
+ dependencies:
+ '@vue/devtools-core': 7.7.7(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))(vue@3.5.22(typescript@5.6.3))
+ '@vue/devtools-kit': 7.7.7
+ '@vue/devtools-shared': 7.7.7
+ execa: 9.6.0
+ sirv: 3.0.2
+ vite: 7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ vite-plugin-inspect: 0.8.9(rollup@4.52.3)(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ vite-plugin-vue-inspector: 5.3.2(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))
+ transitivePeerDependencies:
+ - '@nuxt/kit'
+ - rollup
+ - supports-color
+ - vue
+
+ vite-plugin-vue-inspector@5.3.2(vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4)
+ '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.4)
+ '@vue/compiler-dom': 3.5.22
+ kolorist: 1.8.0
+ magic-string: 0.30.19
+ vite: 7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ vite@7.1.7(@types/node@24.8.1)(jiti@2.6.0)(lightningcss@1.30.1)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1):
+ dependencies:
+ esbuild: 0.25.10
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+ postcss: 8.5.6
+ rollup: 4.52.3
+ tinyglobby: 0.2.15
+ optionalDependencies:
+ '@types/node': 24.8.1
+ fsevents: 2.3.3
+ jiti: 2.6.0
+ lightningcss: 1.30.1
+ sass: 1.93.2
+ terser: 5.44.0
+ tsx: 4.20.6
+ yaml: 2.8.1
+
+ vscode-uri@3.1.0: {}
+
+ vue-demi@0.14.10(vue@3.5.22(typescript@5.6.3)):
+ dependencies:
+ vue: 3.5.22(typescript@5.6.3)
+
+ vue-draggable-plus@0.6.0(@types/sortablejs@1.15.8):
+ dependencies:
+ '@types/sortablejs': 1.15.8
+
+ vue-eslint-parser@9.4.3(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ debug: 4.4.3
+ eslint: 9.36.0(jiti@2.6.0)
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ esquery: 1.6.0
+ lodash: 4.17.21
+ semver: 7.7.2
+ transitivePeerDependencies:
+ - supports-color
+
+ vue-i18n@9.14.5(vue@3.5.22(typescript@5.6.3)):
+ dependencies:
+ '@intlify/core-base': 9.14.5
+ '@intlify/shared': 9.14.5
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.22(typescript@5.6.3)
+
+ vue-img-cutter@3.0.7(typescript@5.6.3):
+ dependencies:
+ core-js: 3.45.1
+ vue: 3.5.22(typescript@5.6.3)
+ vue-i18n: 9.14.5(vue@3.5.22(typescript@5.6.3))
+ transitivePeerDependencies:
+ - typescript
+
+ vue-router@4.5.1(vue@3.5.22(typescript@5.6.3)):
+ dependencies:
+ '@vue/devtools-api': 6.6.4
+ vue: 3.5.22(typescript@5.6.3)
+
+ vue-tsc@2.1.10(typescript@5.6.3):
+ dependencies:
+ '@volar/typescript': 2.4.23
+ '@vue/language-core': 2.1.10(typescript@5.6.3)
+ semver: 7.7.2
+ typescript: 5.6.3
+
+ vue@3.5.22(typescript@5.6.3):
+ dependencies:
+ '@vue/compiler-dom': 3.5.22
+ '@vue/compiler-sfc': 3.5.22
+ '@vue/runtime-dom': 3.5.22
+ '@vue/server-renderer': 3.5.22(vue@3.5.22(typescript@5.6.3))
+ '@vue/shared': 3.5.22
+ optionalDependencies:
+ typescript: 5.6.3
+
+ wcwidth@1.0.1:
+ dependencies:
+ defaults: 1.0.4
+
+ webpack-virtual-modules@0.6.2: {}
+
+ which@1.3.1:
+ dependencies:
+ isexe: 2.0.0
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ wildcard@1.1.2: {}
+
+ wmf@1.0.2: {}
+
+ word-wrap@1.2.5: {}
+
+ word@0.3.0: {}
+
+ wrap-ansi@7.0.0:
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.3
+ strip-ansi: 6.0.1
+
+ wrap-ansi@9.0.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 7.2.0
+ strip-ansi: 7.1.2
+
+ wrappy@1.0.2: {}
+
+ write-file-atomic@5.0.1:
+ dependencies:
+ imurmurhash: 0.1.4
+ signal-exit: 4.1.0
+
+ wsl-utils@0.1.0:
+ dependencies:
+ is-wsl: 3.1.0
+
+ xgplayer-subtitles@3.0.23(core-js@3.45.1):
+ dependencies:
+ core-js: 3.45.1
+ eventemitter3: 4.0.7
+
+ xgplayer@3.0.23(core-js@3.45.1):
+ dependencies:
+ core-js: 3.45.1
+ danmu.js: 1.1.13
+ delegate: 3.2.0
+ downloadjs: 1.4.7
+ eventemitter3: 4.0.7
+ xgplayer-subtitles: 3.0.23(core-js@3.45.1)
+
+ xlsx@0.18.5:
+ dependencies:
+ adler-32: 1.3.1
+ cfb: 1.2.2
+ codepage: 1.15.0
+ crc-32: 1.2.2
+ ssf: 0.11.2
+ wmf: 1.0.2
+ word: 0.3.0
+
+ xml-name-validator@4.0.0: {}
+
+ y18n@5.0.8: {}
+
+ yallist@3.1.1: {}
+
+ yallist@5.0.0: {}
+
+ yaml@2.8.1: {}
+
+ yargs-parser@21.1.1: {}
+
+ yargs@17.7.2:
+ dependencies:
+ cliui: 8.0.1
+ escalade: 3.2.0
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.3
+ y18n: 5.0.8
+ yargs-parser: 21.1.1
+
+ yocto-queue@0.1.0: {}
+
+ yocto-queue@1.2.1: {}
+
+ yoctocolors@2.1.2: {}
+
+ zrender@6.0.0:
+ dependencies:
+ tslib: 2.3.0
diff --git a/adminSystem/public/favicon.ico b/adminSystem/public/favicon.ico
new file mode 100644
index 0000000..df36fcf
Binary files /dev/null and b/adminSystem/public/favicon.ico differ
diff --git a/adminSystem/scripts/clean-dev.ts b/adminSystem/scripts/clean-dev.ts
new file mode 100644
index 0000000..cc0b9bc
--- /dev/null
+++ b/adminSystem/scripts/clean-dev.ts
@@ -0,0 +1,838 @@
+// scripts/clean-dev.ts
+import fs from 'fs/promises'
+import path from 'path'
+
+// 现代化颜色主题
+const theme = {
+ // 基础颜色
+ reset: '\x1b[0m',
+ bold: '\x1b[1m',
+ dim: '\x1b[2m',
+
+ // 前景色
+ primary: '\x1b[38;5;75m', // 亮蓝色
+ success: '\x1b[38;5;82m', // 亮绿色
+ warning: '\x1b[38;5;220m', // 亮黄色
+ error: '\x1b[38;5;196m', // 亮红色
+ info: '\x1b[38;5;159m', // 青色
+ purple: '\x1b[38;5;141m', // 紫色
+ orange: '\x1b[38;5;208m', // 橙色
+ gray: '\x1b[38;5;245m', // 灰色
+ white: '\x1b[38;5;255m', // 白色
+
+ // 背景色
+ bgDark: '\x1b[48;5;235m', // 深灰背景
+ bgBlue: '\x1b[48;5;24m', // 蓝色背景
+ bgGreen: '\x1b[48;5;22m', // 绿色背景
+ bgRed: '\x1b[48;5;52m' // 红色背景
+}
+
+// 现代化图标集
+const icons = {
+ rocket: '🚀',
+ fire: '🔥',
+ star: '⭐',
+ gem: '💎',
+ crown: '👑',
+ magic: '✨',
+ warning: '⚠️',
+ success: '✅',
+ error: '❌',
+ info: 'ℹ️',
+ folder: '📁',
+ file: '📄',
+ image: '🖼️',
+ code: '💻',
+ data: '📊',
+ globe: '🌐',
+ map: '🗺️',
+ chat: '💬',
+ bolt: '⚡',
+ shield: '🛡️',
+ key: '🔑',
+ link: '🔗',
+ clean: '🧹',
+ trash: '🗑️',
+ check: '✓',
+ cross: '✗',
+ arrow: '→',
+ loading: '⏳'
+}
+
+// 格式化工具
+const fmt = {
+ title: (text: string) => `${theme.bold}${theme.primary}${text}${theme.reset}`,
+ subtitle: (text: string) => `${theme.purple}${text}${theme.reset}`,
+ success: (text: string) => `${theme.success}${text}${theme.reset}`,
+ error: (text: string) => `${theme.error}${text}${theme.reset}`,
+ warning: (text: string) => `${theme.warning}${text}${theme.reset}`,
+ info: (text: string) => `${theme.info}${text}${theme.reset}`,
+ highlight: (text: string) => `${theme.bold}${theme.white}${text}${theme.reset}`,
+ dim: (text: string) => `${theme.dim}${theme.gray}${text}${theme.reset}`,
+ orange: (text: string) => `${theme.orange}${text}${theme.reset}`,
+
+ // 带背景的文本
+ badge: (text: string, bg: string = theme.bgBlue) =>
+ `${bg}${theme.white}${theme.bold} ${text} ${theme.reset}`,
+
+ // 渐变效果模拟
+ gradient: (text: string) => {
+ const colors = ['\x1b[38;5;75m', '\x1b[38;5;81m', '\x1b[38;5;87m', '\x1b[38;5;159m']
+ const chars = text.split('')
+ return chars.map((char, i) => `${colors[i % colors.length]}${char}`).join('') + theme.reset
+ }
+}
+
+// 创建现代化标题横幅
+function createModernBanner() {
+ console.log()
+ console.log(
+ fmt.gradient(' ╔══════════════════════════════════════════════════════════════════╗')
+ )
+ console.log(
+ fmt.gradient(' ║ ║')
+ )
+ console.log(
+ ` ║ ${icons.rocket} ${fmt.title('ART DESIGN PRO')} ${fmt.subtitle('· 代码精简程序')} ${icons.magic} ║`
+ )
+ console.log(
+ ` ║ ${fmt.dim('为项目移除演示数据,快速切换至开发模式')} ║`
+ )
+ console.log(
+ fmt.gradient(' ║ ║')
+ )
+ console.log(
+ fmt.gradient(' ╚══════════════════════════════════════════════════════════════════╝')
+ )
+ console.log()
+}
+
+// 创建分割线
+function createDivider(char = '─', color = theme.primary) {
+ console.log(`${color}${' ' + char.repeat(66)}${theme.reset}`)
+}
+
+// 创建卡片样式容器
+function createCard(title: string, content: string[]) {
+ console.log(` ${fmt.badge('', theme.bgBlue)} ${fmt.title(title)}`)
+ console.log()
+ content.forEach((line) => {
+ console.log(` ${line}`)
+ })
+ console.log()
+}
+
+// 进度条动画
+function createProgressBar(current: number, total: number, text: string, width = 40) {
+ const percentage = Math.round((current / total) * 100)
+ const filled = Math.round((current / total) * width)
+ const empty = width - filled
+
+ const filledBar = '█'.repeat(filled)
+ const emptyBar = '░'.repeat(empty)
+
+ process.stdout.write(
+ `\r ${fmt.info('进度')} [${theme.success}${filledBar}${theme.gray}${emptyBar}${theme.reset}] ${fmt.highlight(percentage + '%')})}`
+ )
+
+ if (current === total) {
+ console.log()
+ }
+}
+
+// 统计信息
+const stats = {
+ deletedFiles: 0,
+ deletedPaths: 0,
+ failedPaths: 0,
+ startTime: Date.now(),
+ totalFiles: 0
+}
+
+// 清理目标
+const targets = [
+ 'README.md',
+ 'README.zh-CN.md',
+ 'CHANGELOG.md',
+ 'CHANGELOG.zh-CN.md',
+ 'src/views/change',
+ 'src/views/safeguard',
+ 'src/views/article',
+ 'src/views/examples',
+ 'src/views/system/nested',
+ 'src/views/widgets',
+ 'src/views/template',
+ 'src/views/dashboard/analysis',
+ 'src/views/dashboard/ecommerce',
+ 'src/mock/json',
+ 'src/mock/temp/articleList.ts',
+ 'src/mock/temp/commentDetail.ts',
+ 'src/mock/temp/commentList.ts',
+ 'src/assets/images/cover',
+ 'src/assets/images/safeguard',
+ 'src/assets/images/3d',
+ 'src/components/core/charts/art-map-chart',
+ 'src/components/business/comment-widget'
+]
+
+// 递归统计文件数量
+async function countFiles(targetPath: string): Promise {
+ const fullPath = path.resolve(process.cwd(), targetPath)
+
+ try {
+ const stat = await fs.stat(fullPath)
+
+ if (stat.isFile()) {
+ return 1
+ } else if (stat.isDirectory()) {
+ const entries = await fs.readdir(fullPath)
+ let count = 0
+
+ for (const entry of entries) {
+ const entryPath = path.join(targetPath, entry)
+ count += await countFiles(entryPath)
+ }
+
+ return count
+ }
+ } catch {
+ return 0
+ }
+
+ return 0
+}
+
+// 统计所有目标的文件数量
+async function countAllFiles(): Promise {
+ let totalCount = 0
+
+ for (const target of targets) {
+ const count = await countFiles(target)
+ totalCount += count
+ }
+
+ return totalCount
+}
+
+// 删除文件和目录
+async function remove(targetPath: string, index: number) {
+ const fullPath = path.resolve(process.cwd(), targetPath)
+
+ createProgressBar(index + 1, targets.length, targetPath)
+
+ try {
+ const fileCount = await countFiles(targetPath)
+ await fs.rm(fullPath, { recursive: true, force: true })
+ stats.deletedFiles += fileCount
+ stats.deletedPaths++
+ await new Promise((resolve) => setTimeout(resolve, 50))
+ } catch (err) {
+ stats.failedPaths++
+ console.log()
+ console.log(` ${icons.error} ${fmt.error('删除失败')}: ${fmt.highlight(targetPath)}`)
+ console.log(` ${fmt.dim('错误详情: ' + err)}`)
+ }
+}
+
+// 清理路由模块
+async function cleanRouteModules() {
+ const modulesPath = path.resolve(process.cwd(), 'src/router/modules')
+
+ try {
+ // 删除演示相关的路由模块
+ const modulesToRemove = [
+ 'template.ts',
+ 'widgets.ts',
+ 'examples.ts',
+ 'article.ts',
+ 'safeguard.ts',
+ 'help.ts'
+ ]
+
+ for (const module of modulesToRemove) {
+ const modulePath = path.join(modulesPath, module)
+ try {
+ await fs.rm(modulePath, { force: true })
+ } catch {
+ // 文件不存在时忽略错误
+ }
+ }
+
+ // 重写 dashboard.ts - 只保留 console
+ const dashboardContent = `import { AppRouteRecord } from '@/types/router'
+
+export const dashboardRoutes: AppRouteRecord = {
+ name: 'Dashboard',
+ path: '/dashboard',
+ component: '/index/index',
+ meta: {
+ title: 'menus.dashboard.title',
+ icon: 'ri:pie-chart-line',
+ roles: ['R_SUPER', 'R_ADMIN']
+ },
+ children: [
+ {
+ path: 'console',
+ name: 'Console',
+ component: '/dashboard/console',
+ meta: {
+ title: 'menus.dashboard.console',
+ keepAlive: false,
+ fixedTab: true
+ }
+ }
+ ]
+}
+`
+ await fs.writeFile(path.join(modulesPath, 'dashboard.ts'), dashboardContent, 'utf-8')
+
+ // 重写 system.ts - 移除 nested 嵌套菜单
+ const systemContent = `import { AppRouteRecord } from '@/types/router'
+
+export const systemRoutes: AppRouteRecord = {
+ path: '/system',
+ name: 'System',
+ component: '/index/index',
+ meta: {
+ title: 'menus.system.title',
+ icon: 'ri:user-3-line',
+ roles: ['R_SUPER', 'R_ADMIN']
+ },
+ children: [
+ {
+ path: 'user',
+ name: 'User',
+ component: '/system/user',
+ meta: {
+ title: 'menus.system.user',
+ keepAlive: true,
+ roles: ['R_SUPER', 'R_ADMIN']
+ }
+ },
+ {
+ path: 'role',
+ name: 'Role',
+ component: '/system/role',
+ meta: {
+ title: 'menus.system.role',
+ keepAlive: true,
+ roles: ['R_SUPER']
+ }
+ },
+ {
+ path: 'user-center',
+ name: 'UserCenter',
+ component: '/system/user-center',
+ meta: {
+ title: 'menus.system.userCenter',
+ isHide: true,
+ keepAlive: true,
+ isHideTab: true
+ }
+ },
+ {
+ path: 'menu',
+ name: 'Menus',
+ component: '/system/menu',
+ meta: {
+ title: 'menus.system.menu',
+ keepAlive: true,
+ roles: ['R_SUPER'],
+ authList: [
+ { title: '新增', authMark: 'add' },
+ { title: '编辑', authMark: 'edit' },
+ { title: '删除', authMark: 'delete' }
+ ]
+ }
+ }
+ ]
+}
+`
+ await fs.writeFile(path.join(modulesPath, 'system.ts'), systemContent, 'utf-8')
+
+ // 重写 index.ts - 只导入保留的模块
+ const indexContent = `import { AppRouteRecord } from '@/types/router'
+import { dashboardRoutes } from './dashboard'
+import { systemRoutes } from './system'
+import { resultRoutes } from './result'
+import { exceptionRoutes } from './exception'
+
+/**
+ * 导出所有模块化路由
+ */
+export const routeModules: AppRouteRecord[] = [
+ dashboardRoutes,
+ systemRoutes,
+ resultRoutes,
+ exceptionRoutes
+]
+`
+ await fs.writeFile(path.join(modulesPath, 'index.ts'), indexContent, 'utf-8')
+
+ console.log(` ${icons.success} ${fmt.success('清理路由模块完成')}`)
+ } catch (err) {
+ console.log(` ${icons.error} ${fmt.error('清理路由模块失败')}`)
+ console.log(` ${fmt.dim('错误详情: ' + err)}`)
+ }
+}
+
+// 清理路由别名
+async function cleanRoutesAlias() {
+ const routesAliasPath = path.resolve(process.cwd(), 'src/router/routesAlias.ts')
+
+ try {
+ const cleanedAlias = `/**
+ * 公共路由别名
+ # 存放系统级公共路由路径,如布局容器、登录页等
+ */
+export enum RoutesAlias {
+ Layout = '/index/index', // 布局容器
+ Login = '/auth/login' // 登录页
+}
+`
+
+ await fs.writeFile(routesAliasPath, cleanedAlias, 'utf-8')
+ console.log(` ${icons.success} ${fmt.success('重写路由别名配置完成')}`)
+ } catch (err) {
+ console.log(` ${icons.error} ${fmt.error('清理路由别名失败')}`)
+ console.log(` ${fmt.dim('错误详情: ' + err)}`)
+ }
+}
+
+// 清理变更日志
+async function cleanChangeLog() {
+ const changeLogPath = path.resolve(process.cwd(), 'src/mock/upgrade/changeLog.ts')
+
+ try {
+ const cleanedChangeLog = `import { ref } from 'vue'
+
+interface UpgradeLog {
+ version: string // 版本号
+ title: string // 更新标题
+ date: string // 更新日期
+ detail?: string[] // 更新内容
+ requireReLogin?: boolean // 是否需要重新登录
+ remark?: string // 备注
+}
+
+export const upgradeLogList = ref([])
+`
+
+ await fs.writeFile(changeLogPath, cleanedChangeLog, 'utf-8')
+ console.log(` ${icons.success} ${fmt.success('清空变更日志数据完成')}`)
+ } catch (err) {
+ console.log(` ${icons.error} ${fmt.error('清理变更日志失败')}`)
+ console.log(` ${fmt.dim('错误详情: ' + err)}`)
+ }
+}
+
+// 清理语言文件
+async function cleanLanguageFiles() {
+ const languageFiles = [
+ { path: 'src/locales/langs/zh.json', name: '中文语言文件' },
+ { path: 'src/locales/langs/en.json', name: '英文语言文件' }
+ ]
+
+ for (const { path: langPath, name } of languageFiles) {
+ try {
+ const fullPath = path.resolve(process.cwd(), langPath)
+ const content = await fs.readFile(fullPath, 'utf-8')
+ const langData = JSON.parse(content)
+
+ const menusToRemove = [
+ 'widgets',
+ 'template',
+ 'article',
+ 'examples',
+ 'safeguard',
+ 'plan',
+ 'help'
+ ]
+
+ if (langData.menus) {
+ menusToRemove.forEach((menuKey) => {
+ if (langData.menus[menuKey]) {
+ delete langData.menus[menuKey]
+ }
+ })
+
+ if (langData.menus.dashboard) {
+ if (langData.menus.dashboard.analysis) {
+ delete langData.menus.dashboard.analysis
+ }
+ if (langData.menus.dashboard.ecommerce) {
+ delete langData.menus.dashboard.ecommerce
+ }
+ }
+
+ if (langData.menus.system) {
+ const systemKeysToRemove = [
+ 'nested',
+ 'menu1',
+ 'menu2',
+ 'menu21',
+ 'menu3',
+ 'menu31',
+ 'menu32',
+ 'menu321'
+ ]
+ systemKeysToRemove.forEach((key) => {
+ if (langData.menus.system[key]) {
+ delete langData.menus.system[key]
+ }
+ })
+ }
+ }
+
+ await fs.writeFile(fullPath, JSON.stringify(langData, null, 2), 'utf-8')
+ console.log(` ${icons.success} ${fmt.success(`清理${name}完成`)}`)
+ } catch (err) {
+ console.log(` ${icons.error} ${fmt.error(`清理${name}失败`)}`)
+ console.log(` ${fmt.dim('错误详情: ' + err)}`)
+ }
+ }
+}
+
+// 清理快速入口组件
+async function cleanFastEnterComponent() {
+ const fastEnterPath = path.resolve(process.cwd(), 'src/config/fastEnter.ts')
+
+ try {
+ const cleanedFastEnter = `/**
+ * 快速入口配置
+ * 包含:应用列表、快速链接等配置
+ */
+import { WEB_LINKS } from '@/utils/constants'
+import type { FastEnterConfig } from '@/types/config'
+
+const fastEnterConfig: FastEnterConfig = {
+ // 显示条件(屏幕宽度)
+ minWidth: 1200,
+ // 应用列表
+ applications: [
+ {
+ name: '工作台',
+ description: '系统概览与数据统计',
+ icon: 'ri:pie-chart-line',
+ iconColor: '#377dff',
+ enabled: true,
+ order: 1,
+ routeName: 'Console'
+ },
+ {
+ name: '官方文档',
+ description: '使用指南与开发文档',
+ icon: 'ri:bill-line',
+ iconColor: '#ffb100',
+ enabled: true,
+ order: 2,
+ link: WEB_LINKS.DOCS
+ },
+ {
+ name: '技术支持',
+ description: '技术支持与问题反馈',
+ icon: 'ri:user-location-line',
+ iconColor: '#ff6b6b',
+ enabled: true,
+ order: 3,
+ link: WEB_LINKS.COMMUNITY
+ },
+ {
+ name: '哔哩哔哩',
+ description: '技术分享与交流',
+ icon: 'ri:bilibili-line',
+ iconColor: '#FB7299',
+ enabled: true,
+ order: 4,
+ link: WEB_LINKS.BILIBILI
+ }
+ ],
+ // 快速链接
+ quickLinks: [
+ {
+ name: '登录',
+ enabled: true,
+ order: 1,
+ routeName: 'Login'
+ },
+ {
+ name: '注册',
+ enabled: true,
+ order: 2,
+ routeName: 'Register'
+ },
+ {
+ name: '忘记密码',
+ enabled: true,
+ order: 3,
+ routeName: 'ForgetPassword'
+ },
+ {
+ name: '个人中心',
+ enabled: true,
+ order: 4,
+ routeName: 'UserCenter'
+ }
+ ]
+}
+
+export default Object.freeze(fastEnterConfig)
+`
+
+ await fs.writeFile(fastEnterPath, cleanedFastEnter, 'utf-8')
+ console.log(` ${icons.success} ${fmt.success('清理快速入口配置完成')}`)
+ } catch (err) {
+ console.log(` ${icons.error} ${fmt.error('清理快速入口配置失败')}`)
+ console.log(` ${fmt.dim('错误详情: ' + err)}`)
+ }
+}
+
+// 更新菜单接口
+async function updateMenuApi() {
+ const apiPath = path.resolve(process.cwd(), 'src/api/system-manage.ts')
+
+ try {
+ const content = await fs.readFile(apiPath, 'utf-8')
+ const updatedContent = content.replace(
+ "url: '/api/v3/system/menus'",
+ "url: '/api/v3/system/menus/simple'"
+ )
+
+ await fs.writeFile(apiPath, updatedContent, 'utf-8')
+ console.log(` ${icons.success} ${fmt.success('更新菜单接口完成')}`)
+ } catch (err) {
+ console.log(` ${icons.error} ${fmt.error('更新菜单接口失败')}`)
+ console.log(` ${fmt.dim('错误详情: ' + err)}`)
+ }
+}
+
+// 用户确认函数
+async function getUserConfirmation(): Promise {
+ const { createInterface } = await import('readline')
+
+ return new Promise((resolve) => {
+ const rl = createInterface({
+ input: process.stdin,
+ output: process.stdout
+ })
+
+ console.log(
+ ` ${fmt.highlight('请输入')} ${fmt.success('yes')} ${fmt.highlight('确认执行清理操作,或按 Enter 取消')}`
+ )
+ console.log()
+ process.stdout.write(` ${icons.arrow} `)
+
+ rl.question('', (answer: string) => {
+ rl.close()
+ resolve(answer.toLowerCase().trim() === 'yes')
+ })
+ })
+}
+
+// 显示清理警告
+async function showCleanupWarning() {
+ createCard('安全警告', [
+ `${fmt.warning('此操作将永久删除以下演示内容,且无法恢复!')}`,
+ `${fmt.dim('请仔细阅读清理列表,确认后再继续操作')}`
+ ])
+
+ const cleanupItems = [
+ {
+ icon: icons.image,
+ name: '图片资源',
+ desc: '演示用的封面图片、3D图片、运维图片等',
+ color: theme.orange
+ },
+ {
+ icon: icons.file,
+ name: '演示页面',
+ desc: 'widgets、template、article、examples、safeguard等页面',
+ color: theme.purple
+ },
+ {
+ icon: icons.code,
+ name: '路由模块文件',
+ desc: '删除演示路由模块,只保留核心模块(dashboard、system、result、exception)',
+ color: theme.primary
+ },
+ {
+ icon: icons.link,
+ name: '路由别名',
+ desc: '重写routesAlias.ts,移除演示路由别名',
+ color: theme.info
+ },
+ {
+ icon: icons.data,
+ name: 'Mock数据',
+ desc: '演示用的JSON数据、文章列表、评论数据等',
+ color: theme.success
+ },
+ {
+ icon: icons.globe,
+ name: '多语言文件',
+ desc: '清理中英文语言包中的演示菜单项',
+ color: theme.warning
+ },
+ { icon: icons.map, name: '地图组件', desc: '移除art-map-chart地图组件', color: theme.error },
+ { icon: icons.chat, name: '评论组件', desc: '移除comment-widget评论组件', color: theme.orange },
+ {
+ icon: icons.bolt,
+ name: '快速入口',
+ desc: '移除分析页、礼花效果、聊天、更新日志、定价、留言管理等无效项目',
+ color: theme.purple
+ }
+ ]
+
+ console.log(` ${fmt.badge('', theme.bgRed)} ${fmt.title('将要清理的内容')}`)
+ console.log()
+
+ cleanupItems.forEach((item, index) => {
+ console.log(` ${item.color}${theme.reset} ${fmt.highlight(`${index + 1}. ${item.name}`)}`)
+ console.log(` ${fmt.dim(item.desc)}`)
+ })
+
+ console.log()
+ console.log(` ${fmt.badge('', theme.bgGreen)} ${fmt.title('保留的功能模块')}`)
+ console.log()
+
+ const preservedModules = [
+ { name: 'Dashboard', desc: '工作台页面' },
+ { name: 'System', desc: '系统管理模块' },
+ { name: 'Result', desc: '结果页面' },
+ { name: 'Exception', desc: '异常页面' },
+ { name: 'Auth', desc: '登录注册功能' },
+ { name: 'Core Components', desc: '核心组件库' }
+ ]
+
+ preservedModules.forEach((module) => {
+ console.log(` ${icons.check} ${fmt.success(module.name)} ${fmt.dim(`- ${module.desc}`)}`)
+ })
+
+ console.log()
+ createDivider()
+ console.log()
+}
+
+// 显示统计信息
+async function showStats() {
+ const duration = Date.now() - stats.startTime
+ const seconds = (duration / 1000).toFixed(2)
+
+ console.log()
+ createCard('清理统计', [
+ `${fmt.success('成功删除')}: ${fmt.highlight(stats.deletedFiles.toString())} 个文件`,
+ `${fmt.info('涉及路径')}: ${fmt.highlight(stats.deletedPaths.toString())} 个目录/文件`,
+ ...(stats.failedPaths > 0
+ ? [
+ `${icons.error} ${fmt.error('删除失败')}: ${fmt.highlight(stats.failedPaths.toString())} 个路径`
+ ]
+ : []),
+ `${fmt.info('耗时')}: ${fmt.highlight(seconds)} 秒`
+ ])
+}
+
+// 创建成功横幅
+function createSuccessBanner() {
+ console.log()
+ console.log(
+ fmt.gradient(' ╔══════════════════════════════════════════════════════════════════╗')
+ )
+ console.log(
+ fmt.gradient(' ║ ║')
+ )
+ console.log(
+ ` ║ ${icons.star} ${fmt.success('清理完成!项目已准备就绪')} ${icons.rocket} ║`
+ )
+ console.log(
+ ` ║ ${fmt.dim('现在可以开始您的开发之旅了!')} ║`
+ )
+ console.log(
+ fmt.gradient(' ║ ║')
+ )
+ console.log(
+ fmt.gradient(' ╚══════════════════════════════════════════════════════════════════╝')
+ )
+ console.log()
+}
+
+// 主函数
+async function main() {
+ // 清屏并显示横幅
+ console.clear()
+ createModernBanner()
+
+ // 显示清理警告
+ await showCleanupWarning()
+
+ // 统计文件数量
+ console.log(` ${fmt.info('正在统计文件数量...')}`)
+ stats.totalFiles = await countAllFiles()
+
+ console.log(` ${fmt.info('即将清理')}: ${fmt.highlight(stats.totalFiles.toString())} 个文件`)
+ console.log(` ${fmt.dim(`涉及 ${targets.length} 个目录/文件路径`)}`)
+ console.log()
+
+ // 用户确认
+ const confirmed = await getUserConfirmation()
+
+ if (!confirmed) {
+ console.log(` ${fmt.warning('操作已取消,清理中止')}`)
+ console.log()
+ return
+ }
+
+ console.log()
+ console.log(` ${icons.check} ${fmt.success('确认成功,开始清理...')}`)
+ console.log()
+
+ // 开始清理过程
+ console.log(` ${fmt.badge('步骤 1/6', theme.bgBlue)} ${fmt.title('删除演示文件')}`)
+ console.log()
+ for (let i = 0; i < targets.length; i++) {
+ await remove(targets[i], i)
+ }
+ console.log()
+
+ console.log(` ${fmt.badge('步骤 2/6', theme.bgBlue)} ${fmt.title('清理路由模块')}`)
+ console.log()
+ await cleanRouteModules()
+ console.log()
+
+ console.log(` ${fmt.badge('步骤 3/6', theme.bgBlue)} ${fmt.title('重写路由别名')}`)
+ console.log()
+ await cleanRoutesAlias()
+ console.log()
+
+ console.log(` ${fmt.badge('步骤 4/6', theme.bgBlue)} ${fmt.title('清空变更日志')}`)
+ console.log()
+ await cleanChangeLog()
+ console.log()
+
+ console.log(` ${fmt.badge('步骤 5/6', theme.bgBlue)} ${fmt.title('清理语言文件')}`)
+ console.log()
+ await cleanLanguageFiles()
+ console.log()
+
+ console.log(` ${fmt.badge('步骤 6/7', theme.bgBlue)} ${fmt.title('清理快速入口')}`)
+ console.log()
+ await cleanFastEnterComponent()
+ console.log()
+
+ console.log(` ${fmt.badge('步骤 7/7', theme.bgBlue)} ${fmt.title('更新菜单接口')}`)
+ console.log()
+ await updateMenuApi()
+
+ // 显示统计信息
+ await showStats()
+
+ // 显示成功横幅
+ createSuccessBanner()
+}
+
+main().catch((err) => {
+ console.log()
+ console.log(` ${icons.error} ${fmt.error('清理脚本执行出错')}`)
+ console.log(` ${fmt.dim('错误详情: ' + err)}`)
+ console.log()
+ process.exit(1)
+})
diff --git a/adminSystem/src/App.vue b/adminSystem/src/App.vue
new file mode 100644
index 0000000..3433913
--- /dev/null
+++ b/adminSystem/src/App.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
diff --git a/adminSystem/src/api/auth.ts b/adminSystem/src/api/auth.ts
new file mode 100644
index 0000000..9dc7b6a
--- /dev/null
+++ b/adminSystem/src/api/auth.ts
@@ -0,0 +1,29 @@
+import request from '@/utils/http'
+
+/**
+ * 登录
+ * @param params 登录参数
+ * @returns 登录响应
+ */
+export function fetchLogin(params: Api.Auth.LoginParams) {
+ return request.post({
+ url: '/api/auth/login',
+ params
+ // showSuccessMessage: true // 显示成功消息
+ // showErrorMessage: false // 不显示错误消息
+ })
+}
+
+/**
+ * 获取用户信息
+ * @returns 用户信息
+ */
+export function fetchGetUserInfo() {
+ return request.get({
+ url: '/api/user/info'
+ // 自定义请求头
+ // headers: {
+ // 'X-Custom-Header': 'your-custom-value'
+ // }
+ })
+}
diff --git a/adminSystem/src/api/system-manage.ts b/adminSystem/src/api/system-manage.ts
new file mode 100644
index 0000000..8f4a8e6
--- /dev/null
+++ b/adminSystem/src/api/system-manage.ts
@@ -0,0 +1,25 @@
+import request from '@/utils/http'
+import { AppRouteRecord } from '@/types/router'
+
+// 获取用户列表
+export function fetchGetUserList(params: Api.SystemManage.UserSearchParams) {
+ return request.get({
+ url: '/api/user/list',
+ params
+ })
+}
+
+// 获取角色列表
+export function fetchGetRoleList(params: Api.SystemManage.RoleSearchParams) {
+ return request.get({
+ url: '/api/role/list',
+ params
+ })
+}
+
+// 获取菜单列表
+export function fetchGetMenuList() {
+ return request.get({
+ url: '/api/v3/system/menus/simple'
+ })
+}
diff --git a/adminSystem/src/assets/images/avatar/avatar.webp b/adminSystem/src/assets/images/avatar/avatar.webp
new file mode 100644
index 0000000..bea307b
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar1.webp b/adminSystem/src/assets/images/avatar/avatar1.webp
new file mode 100644
index 0000000..68e256c
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar1.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar10.webp b/adminSystem/src/assets/images/avatar/avatar10.webp
new file mode 100644
index 0000000..a813d4c
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar10.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar2.webp b/adminSystem/src/assets/images/avatar/avatar2.webp
new file mode 100644
index 0000000..6716e3f
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar2.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar3.webp b/adminSystem/src/assets/images/avatar/avatar3.webp
new file mode 100644
index 0000000..7355ad4
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar3.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar4.webp b/adminSystem/src/assets/images/avatar/avatar4.webp
new file mode 100644
index 0000000..56a9549
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar4.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar5.webp b/adminSystem/src/assets/images/avatar/avatar5.webp
new file mode 100644
index 0000000..f78400c
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar5.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar6.webp b/adminSystem/src/assets/images/avatar/avatar6.webp
new file mode 100644
index 0000000..9771b78
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar6.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar7.webp b/adminSystem/src/assets/images/avatar/avatar7.webp
new file mode 100644
index 0000000..e5ef6fe
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar7.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar8.webp b/adminSystem/src/assets/images/avatar/avatar8.webp
new file mode 100644
index 0000000..b66e48f
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar8.webp differ
diff --git a/adminSystem/src/assets/images/avatar/avatar9.webp b/adminSystem/src/assets/images/avatar/avatar9.webp
new file mode 100644
index 0000000..7974139
Binary files /dev/null and b/adminSystem/src/assets/images/avatar/avatar9.webp differ
diff --git a/adminSystem/src/assets/images/ceremony/hb.png b/adminSystem/src/assets/images/ceremony/hb.png
new file mode 100644
index 0000000..4103324
Binary files /dev/null and b/adminSystem/src/assets/images/ceremony/hb.png differ
diff --git a/adminSystem/src/assets/images/ceremony/sd.png b/adminSystem/src/assets/images/ceremony/sd.png
new file mode 100644
index 0000000..75ec838
Binary files /dev/null and b/adminSystem/src/assets/images/ceremony/sd.png differ
diff --git a/adminSystem/src/assets/images/ceremony/xc.png b/adminSystem/src/assets/images/ceremony/xc.png
new file mode 100644
index 0000000..9c7ab67
Binary files /dev/null and b/adminSystem/src/assets/images/ceremony/xc.png differ
diff --git a/adminSystem/src/assets/images/ceremony/yd.png b/adminSystem/src/assets/images/ceremony/yd.png
new file mode 100644
index 0000000..426912d
Binary files /dev/null and b/adminSystem/src/assets/images/ceremony/yd.png differ
diff --git a/adminSystem/src/assets/images/common/logo.webp b/adminSystem/src/assets/images/common/logo.webp
new file mode 100644
index 0000000..71542b5
Binary files /dev/null and b/adminSystem/src/assets/images/common/logo.webp differ
diff --git a/adminSystem/src/assets/images/draw/draw1.png b/adminSystem/src/assets/images/draw/draw1.png
new file mode 100644
index 0000000..da5d87a
Binary files /dev/null and b/adminSystem/src/assets/images/draw/draw1.png differ
diff --git a/adminSystem/src/assets/images/favicon.ico b/adminSystem/src/assets/images/favicon.ico
new file mode 100644
index 0000000..21e7063
Binary files /dev/null and b/adminSystem/src/assets/images/favicon.ico differ
diff --git a/adminSystem/src/assets/images/lock/bg_dark.webp b/adminSystem/src/assets/images/lock/bg_dark.webp
new file mode 100644
index 0000000..1c33435
Binary files /dev/null and b/adminSystem/src/assets/images/lock/bg_dark.webp differ
diff --git a/adminSystem/src/assets/images/lock/bg_light.webp b/adminSystem/src/assets/images/lock/bg_light.webp
new file mode 100644
index 0000000..00efbd9
Binary files /dev/null and b/adminSystem/src/assets/images/lock/bg_light.webp differ
diff --git a/adminSystem/src/assets/images/login/lf_icon2.webp b/adminSystem/src/assets/images/login/lf_icon2.webp
new file mode 100644
index 0000000..5e4f3fd
Binary files /dev/null and b/adminSystem/src/assets/images/login/lf_icon2.webp differ
diff --git a/adminSystem/src/assets/images/settings/menu_layouts/dual_column.png b/adminSystem/src/assets/images/settings/menu_layouts/dual_column.png
new file mode 100644
index 0000000..9b868ca
Binary files /dev/null and b/adminSystem/src/assets/images/settings/menu_layouts/dual_column.png differ
diff --git a/adminSystem/src/assets/images/settings/menu_layouts/horizontal.png b/adminSystem/src/assets/images/settings/menu_layouts/horizontal.png
new file mode 100644
index 0000000..ca779bc
Binary files /dev/null and b/adminSystem/src/assets/images/settings/menu_layouts/horizontal.png differ
diff --git a/adminSystem/src/assets/images/settings/menu_layouts/mixed.png b/adminSystem/src/assets/images/settings/menu_layouts/mixed.png
new file mode 100644
index 0000000..c82b580
Binary files /dev/null and b/adminSystem/src/assets/images/settings/menu_layouts/mixed.png differ
diff --git a/adminSystem/src/assets/images/settings/menu_layouts/vertical.png b/adminSystem/src/assets/images/settings/menu_layouts/vertical.png
new file mode 100644
index 0000000..16e942b
Binary files /dev/null and b/adminSystem/src/assets/images/settings/menu_layouts/vertical.png differ
diff --git a/adminSystem/src/assets/images/settings/menu_styles/dark.png b/adminSystem/src/assets/images/settings/menu_styles/dark.png
new file mode 100644
index 0000000..e1653b7
Binary files /dev/null and b/adminSystem/src/assets/images/settings/menu_styles/dark.png differ
diff --git a/adminSystem/src/assets/images/settings/menu_styles/design.png b/adminSystem/src/assets/images/settings/menu_styles/design.png
new file mode 100644
index 0000000..7681aa8
Binary files /dev/null and b/adminSystem/src/assets/images/settings/menu_styles/design.png differ
diff --git a/adminSystem/src/assets/images/settings/menu_styles/light.png b/adminSystem/src/assets/images/settings/menu_styles/light.png
new file mode 100644
index 0000000..3007b99
Binary files /dev/null and b/adminSystem/src/assets/images/settings/menu_styles/light.png differ
diff --git a/adminSystem/src/assets/images/settings/theme_styles/dark.png b/adminSystem/src/assets/images/settings/theme_styles/dark.png
new file mode 100644
index 0000000..e8c6e44
Binary files /dev/null and b/adminSystem/src/assets/images/settings/theme_styles/dark.png differ
diff --git a/adminSystem/src/assets/images/settings/theme_styles/light.png b/adminSystem/src/assets/images/settings/theme_styles/light.png
new file mode 100644
index 0000000..6754238
Binary files /dev/null and b/adminSystem/src/assets/images/settings/theme_styles/light.png differ
diff --git a/adminSystem/src/assets/images/settings/theme_styles/system.png b/adminSystem/src/assets/images/settings/theme_styles/system.png
new file mode 100644
index 0000000..6a6baa9
Binary files /dev/null and b/adminSystem/src/assets/images/settings/theme_styles/system.png differ
diff --git a/adminSystem/src/assets/images/svg/403.svg b/adminSystem/src/assets/images/svg/403.svg
new file mode 100644
index 0000000..68790ad
--- /dev/null
+++ b/adminSystem/src/assets/images/svg/403.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/adminSystem/src/assets/images/svg/404.svg b/adminSystem/src/assets/images/svg/404.svg
new file mode 100644
index 0000000..48e1ca3
--- /dev/null
+++ b/adminSystem/src/assets/images/svg/404.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/adminSystem/src/assets/images/svg/500.svg b/adminSystem/src/assets/images/svg/500.svg
new file mode 100644
index 0000000..512429f
--- /dev/null
+++ b/adminSystem/src/assets/images/svg/500.svg
@@ -0,0 +1,5 @@
+
diff --git a/adminSystem/src/assets/images/svg/login_icon.svg b/adminSystem/src/assets/images/svg/login_icon.svg
new file mode 100644
index 0000000..4beb3ab
--- /dev/null
+++ b/adminSystem/src/assets/images/svg/login_icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/adminSystem/src/assets/images/user/avatar.webp b/adminSystem/src/assets/images/user/avatar.webp
new file mode 100644
index 0000000..6d7234b
Binary files /dev/null and b/adminSystem/src/assets/images/user/avatar.webp differ
diff --git a/adminSystem/src/assets/images/user/bg.webp b/adminSystem/src/assets/images/user/bg.webp
new file mode 100644
index 0000000..762b22d
Binary files /dev/null and b/adminSystem/src/assets/images/user/bg.webp differ
diff --git a/adminSystem/src/assets/styles/core/app.scss b/adminSystem/src/assets/styles/core/app.scss
new file mode 100644
index 0000000..c0efeed
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/app.scss
@@ -0,0 +1,292 @@
+// 全局样式
+// 顶部进度条颜色
+#nprogress .bar {
+ z-index: 2400;
+ background-color: color-mix(in srgb, var(--theme-color) 70%, white);
+}
+
+#nprogress .peg {
+ box-shadow:
+ 0 0 10px var(--theme-color),
+ 0 0 5px var(--theme-color) !important;
+}
+
+#nprogress .spinner-icon {
+ border-top-color: var(--theme-color) !important;
+ border-left-color: var(--theme-color) !important;
+}
+
+// 处理移动端组件兼容性
+@media screen and (max-width: 640px) {
+ * {
+ cursor: default !important;
+ }
+}
+
+// 背景滤镜
+*,
+::before,
+::after {
+ --tw-backdrop-blur: ;
+ --tw-backdrop-brightness: ;
+ --tw-backdrop-contrast: ;
+ --tw-backdrop-grayscale: ;
+ --tw-backdrop-hue-rotate: ;
+ --tw-backdrop-invert: ;
+ --tw-backdrop-opacity: ;
+ --tw-backdrop-saturate: ;
+ --tw-backdrop-sepia: ;
+}
+
+// 色弱模式
+.color-weak {
+ filter: invert(80%);
+ -webkit-filter: invert(80%);
+}
+
+#noop {
+ display: none;
+}
+
+// 语言切换选中样式
+.langDropDownStyle {
+ // 选中项背景颜色
+ .is-selected {
+ background-color: var(--art-el-active-color) !important;
+ }
+
+ // 语言切换按钮菜单样式优化
+ .lang-btn-item {
+ .el-dropdown-menu__item {
+ padding-left: 13px !important;
+ padding-right: 6px !important;
+ margin-bottom: 3px !important;
+ }
+
+ &:last-child {
+ .el-dropdown-menu__item {
+ margin-bottom: 0 !important;
+ }
+ }
+
+ .menu-txt {
+ min-width: 60px;
+ display: block;
+ }
+
+ i {
+ font-size: 10px;
+ margin-left: 10px;
+ }
+ }
+}
+
+// 盒子默认边框
+.page-content {
+ border: 1px solid var(--art-card-border) !important;
+}
+
+@mixin art-card-base($border-color, $shadow: none, $radius-diff: 4px) {
+ background: var(--default-box-color);
+ border: 1px solid #{$border-color} !important;
+ border-radius: calc(var(--custom-radius) + #{$radius-diff}) !important;
+ box-shadow: #{$shadow} !important;
+
+ --el-card-border-color: var(--default-border) !important;
+}
+
+.art-card,
+.art-card-sm,
+.art-card-xs {
+ border: 1px solid var(--art-card-border);
+}
+
+// 盒子边框
+[data-box-mode='border-mode'] {
+ .page-content,
+ .art-table-card {
+ border: 1px solid var(--art-card-border) !important;
+ }
+
+ .art-card {
+ @include art-card-base(var(--art-card-border), none, 4px);
+ }
+
+ .art-card-sm {
+ @include art-card-base(var(--art-card-border), none, 0px);
+ }
+
+ .art-card-xs {
+ @include art-card-base(var(--art-card-border), none, -4px);
+ }
+}
+
+// 盒子阴影
+[data-box-mode='shadow-mode'] {
+ .page-content,
+ .art-table-card {
+ box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.04) !important;
+ border: 1px solid var(--art-gray-200) !important;
+ }
+
+ .layout-sidebar {
+ border-right: 1px solid var(--art-card-border) !important;
+ }
+
+ .art-card {
+ @include art-card-base(
+ var(--art-gray-200),
+ (0 1px 3px 0 rgba(0, 0, 0, 0.03), 0 1px 2px -1px rgba(0, 0, 0, 0.08)),
+ 4px
+ );
+ }
+
+ .art-card-sm {
+ @include art-card-base(
+ var(--art-gray-200),
+ (0 1px 3px 0 rgba(0, 0, 0, 0.03), 0 1px 2px -1px rgba(0, 0, 0, 0.08)),
+ 2px
+ );
+ }
+
+ .art-card-xs {
+ @include art-card-base(
+ var(--art-gray-200),
+ (0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 1px -1px rgba(0, 0, 0, 0.08)),
+ -4px
+ );
+ }
+}
+
+// 元素全屏
+.el-full-screen {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ width: 100vw !important;
+ height: 100% !important;
+ z-index: 2300;
+ margin-top: 0;
+ padding: 15px;
+ box-sizing: border-box;
+ background-color: var(--default-box-color);
+ display: flex;
+ flex-direction: column;
+}
+
+// 表格卡片
+.art-table-card {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ margin-top: 12px;
+ border-radius: calc(var(--custom-radius) / 2 + 2px) !important;
+
+ .el-card__body {
+ height: 100%;
+ overflow: hidden;
+ }
+}
+
+// 容器全高
+.art-full-height {
+ height: var(--art-full-height);
+ display: flex;
+ flex-direction: column;
+
+ @media (max-width: 640px) {
+ height: auto;
+ }
+}
+
+// 徽章样式
+.art-badge {
+ position: absolute;
+ top: 0;
+ right: 20px;
+ bottom: 0;
+ width: 6px;
+ height: 6px;
+ margin: auto;
+ background: #ff3860;
+ border-radius: 50%;
+ animation: breathe 1.5s ease-in-out infinite;
+
+ &.art-badge-horizontal {
+ right: 0;
+ }
+
+ &.art-badge-mixed {
+ right: 0;
+ }
+
+ &.art-badge-dual {
+ right: 5px;
+ top: 5px;
+ bottom: auto;
+ }
+}
+
+// 文字徽章样式
+.art-text-badge {
+ position: absolute;
+ top: 0;
+ right: 12px;
+ bottom: 0;
+ min-width: 20px;
+ height: 18px;
+ line-height: 17px;
+ padding: 0 5px;
+ margin: auto;
+ font-size: 10px;
+ color: #fff;
+ text-align: center;
+ background: #fd4e4e;
+ border-radius: 4px;
+}
+
+@keyframes breathe {
+ 0% {
+ opacity: 0.7;
+ transform: scale(1);
+ }
+
+ 50% {
+ opacity: 1;
+ transform: scale(1.1);
+ }
+
+ 100% {
+ opacity: 0.7;
+ transform: scale(1);
+ }
+}
+
+// 修复老机型 loading 定位问题
+.art-loading-fix {
+ position: fixed !important;
+ top: 0 !important;
+ left: 0 !important;
+ right: 0 !important;
+ bottom: 0 !important;
+ width: 100vw !important;
+ height: 100vh !important;
+ display: flex !important;
+ align-items: center !important;
+ justify-content: center !important;
+}
+
+.art-loading-fix .el-loading-spinner {
+ position: static !important;
+ top: auto !important;
+ left: auto !important;
+ transform: none !important;
+}
+
+// 去除移动端点击背景色
+@media screen and (max-width: 1180px) {
+ * {
+ -webkit-tap-highlight-color: transparent;
+ }
+}
diff --git a/adminSystem/src/assets/styles/core/dark.scss b/adminSystem/src/assets/styles/core/dark.scss
new file mode 100644
index 0000000..c52abc3
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/dark.scss
@@ -0,0 +1,93 @@
+/*
+* 深色主题
+* 单页面移除深色主题 document.getElementsByTagName("html")[0].removeAttribute('class')
+*/
+
+$font-color: rgba(#ffffff, 0.85);
+
+/* 覆盖element-plus默认深色背景色 */
+html.dark {
+ // element-plus
+ --el-bg-color: var(--default-box-color);
+ --el-text-color-regular: #{$font-color};
+
+ // 富文本编辑器
+ // 工具栏背景颜色
+ --w-e-toolbar-bg-color: #18191c;
+ // 输入区域背景颜色
+ --w-e-textarea-bg-color: #090909;
+ // 工具栏文字颜色
+ --w-e-toolbar-color: var(--art-gray-600);
+ // 选中菜单颜色
+ --w-e-toolbar-active-bg-color: #25262b;
+ // 弹窗边框颜色
+ --w-e-toolbar-border-color: var(--default-border-dashed);
+ // 分割线颜色
+ --w-e-textarea-border-color: var(--default-border-dashed);
+ // 链接输入框边框颜色
+ --w-e-modal-button-border-color: var(--default-border-dashed);
+ // 表格头颜色
+ --w-e-textarea-slight-bg-color: #090909;
+ // 按钮背景颜色
+ --w-e-modal-button-bg-color: #090909;
+ // hover toolbar 背景颜色
+ --w-e-toolbar-active-color: var(--art-gray-800);
+}
+
+.dark {
+ .page-content .article-list .item .left .outer > div {
+ border-right-color: var(--dark-border-color) !important;
+ }
+
+ // 富文本编辑器
+ .editor-wrapper {
+ *:not(pre code *) {
+ color: inherit !important;
+ }
+ }
+ // 分隔线
+ .w-e-bar-divider {
+ background-color: var(--art-gray-300) !important;
+ }
+
+ .w-e-select-list,
+ .w-e-drop-panel,
+ .w-e-bar-item-group .w-e-bar-item-menus-container,
+ .w-e-text-container [data-slate-editor] pre > code {
+ border: 1px solid var(--default-border) !important;
+ }
+
+ // 下拉选择框
+ .w-e-select-list {
+ background-color: var(--default-box-color) !important;
+ }
+
+ /* 下拉选择框 hover 样式调整 */
+ .w-e-select-list ul li:hover,
+ /* 工具栏 hover 按钮背景颜色 */
+ .w-e-bar-item button:hover {
+ background-color: #090909 !important;
+ }
+
+ /* 代码块 */
+ .w-e-text-container [data-slate-editor] pre > code {
+ background-color: #25262b !important;
+ text-shadow: none !important;
+ }
+
+ /* 引用 */
+ .w-e-text-container [data-slate-editor] blockquote {
+ border-left: 4px solid var(--default-border-dashed) !important;
+ background-color: var(--art-color);
+ }
+
+ .editor-wrapper {
+ .w-e-text-container [data-slate-editor] .table-container th:last-of-type {
+ border-right: 1px solid var(--default-border-dashed) !important;
+ }
+
+ .w-e-modal {
+ background-color: var(--art-color);
+ }
+ }
+}
diff --git a/adminSystem/src/assets/styles/core/el-dark.scss b/adminSystem/src/assets/styles/core/el-dark.scss
new file mode 100644
index 0000000..8f81cdf
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/el-dark.scss
@@ -0,0 +1,2 @@
+// 导入暗黑主题
+@use 'element-plus/theme-chalk/src/dark/css-vars.scss' as *;
diff --git a/adminSystem/src/assets/styles/core/el-light.scss b/adminSystem/src/assets/styles/core/el-light.scss
new file mode 100644
index 0000000..ddf2bc5
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/el-light.scss
@@ -0,0 +1,34 @@
+// https://github.com/element-plus/element-plus/blob/dev/packages/theme-chalk/src/common/var.scss
+// 自定义Element 亮色主题
+
+@forward 'element-plus/theme-chalk/src/common/var.scss' with (
+ $colors: (
+ 'white': #ffffff,
+ 'black': #000000,
+ 'success': (
+ 'base': #13deb9
+ ),
+ 'warning': (
+ 'base': #ffae1f
+ ),
+ 'danger': (
+ 'base': #ff4d4f
+ ),
+ 'error': (
+ 'base': #fa896b
+ )
+ ),
+ $button: (
+ 'hover-bg-color': var(--el-color-primary-light-9),
+ 'hover-border-color': var(--el-color-primary),
+ 'border-color': var(--el-color-primary),
+ 'text-color': var(--el-color-primary)
+ ),
+ $messagebox: (
+ 'border-radius': '12px'
+ ),
+ $popover: (
+ 'padding': '14px',
+ 'border-radius': '10px'
+ )
+);
diff --git a/adminSystem/src/assets/styles/core/el-ui.scss b/adminSystem/src/assets/styles/core/el-ui.scss
new file mode 100644
index 0000000..7e8e150
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/el-ui.scss
@@ -0,0 +1,524 @@
+// 优化 Element Plus 组件库默认样式
+
+:root {
+ // 系统主色
+ --main-color: var(--el-color-primary);
+ --el-color-white: white !important;
+ --el-color-black: white !important;
+ // 输入框边框颜色
+ // --el-border-color: #E4E4E7 !important; // DCDFE6
+ // 按钮粗度
+ --el-font-weight-primary: 400 !important;
+
+ --el-component-custom-height: 36px !important;
+
+ --el-component-size: var(--el-component-custom-height) !important;
+
+ // 边框、按钮圆角...
+ --el-border-radius-base: calc(var(--custom-radius) / 3 + 2px) !important;
+
+ --el-border-radius-small: calc(var(--custom-radius) / 3 + 4px) !important;
+ --el-messagebox-border-radius: calc(var(--custom-radius) / 3 + 4px) !important;
+ --el-popover-border-radius: calc(var(--custom-radius) / 3 + 4px) !important;
+
+ .region .el-radio-button__original-radio:checked + .el-radio-button__inner {
+ color: var(--theme-color);
+ }
+}
+
+// 优化 el-form-item 标签高度
+.el-form-item__label {
+ height: var(--el-component-custom-height) !important;
+ line-height: var(--el-component-custom-height) !important;
+}
+
+// 日期选择器
+.el-date-range-picker {
+ --el-datepicker-inrange-bg-color: var(--art-gray-200) !important;
+}
+
+// el-card 背景色跟系统背景色保持一致
+html.dark .el-card {
+ --el-card-bg-color: var(--default-box-color) !important;
+}
+
+// 修改 el-pagination 大小
+.el-pagination--default {
+ & {
+ --el-pagination-button-width: 32px !important;
+ --el-pagination-button-height: var(--el-pagination-button-width) !important;
+ }
+
+ @media (max-width: 1180px) {
+ & {
+ --el-pagination-button-width: 28px !important;
+ }
+ }
+
+ .el-select--default .el-select__wrapper {
+ min-height: var(--el-pagination-button-width) !important;
+ }
+
+ .el-pagination__jump .el-input {
+ height: var(--el-pagination-button-width) !important;
+ }
+}
+
+.el-pager li {
+ padding: 0 10px !important;
+ // border: 1px solid red !important;
+}
+
+// 优化菜单折叠展开动画(提升动画流畅度)
+.el-menu.el-menu--inline {
+ transition: max-height 0.26s cubic-bezier(0.4, 0, 0.2, 1) !important;
+}
+
+// 优化菜单 item hover 动画(提升鼠标跟手感)
+.el-sub-menu__title,
+.el-menu-item {
+ transition: background-color 0s !important;
+}
+
+// -------------------------------- 修改 el-size=default 组件默认高度 start --------------------------------
+// 修改 el-button 高度
+.el-button--default {
+ height: var(--el-component-custom-height) !important;
+}
+
+// circle 按钮宽度优化
+.el-button--default.is-circle {
+ width: var(--el-component-custom-height) !important;
+}
+
+// 修改 el-select 高度
+.el-select--default {
+ .el-select__wrapper {
+ min-height: var(--el-component-custom-height) !important;
+ }
+}
+
+// 修改 el-checkbox-button 高度
+.el-checkbox-button--default .el-checkbox-button__inner,
+// 修改 el-radio-button 高度
+.el-radio-button--default .el-radio-button__inner {
+ padding: 10px 15px !important;
+}
+// -------------------------------- 修改 el-size=default 组件默认高度 end --------------------------------
+
+.el-pagination.is-background .btn-next,
+.el-pagination.is-background .btn-prev,
+.el-pagination.is-background .el-pager li {
+ border-radius: 6px;
+}
+
+.el-popover {
+ min-width: 80px;
+ border-radius: var(--el-border-radius-small) !important;
+}
+
+.el-dialog {
+ border-radius: 100px !important;
+ border-radius: calc(var(--custom-radius) / 1.2 + 2px) !important;
+ overflow: hidden;
+}
+
+.el-dialog__header {
+ .el-dialog__title {
+ font-size: 16px;
+ }
+}
+
+.el-dialog__body {
+ padding: 25px 0 !important;
+ position: relative; // 为了兼容 el-pagination 样式,需要设置 relative,不然会影响 el-pagination 的样式,比如 el-pagination__jump--small 会被影响,导致 el-pagination__jump--small 按钮无法点击,详见 URL_ADDRESS.com/element-plus/element-plus/issues/5684#issuecomment-1176299275;
+}
+
+.el-dialog.el-dialog-border {
+ .el-dialog__body {
+ // 上边框
+ &::before,
+ // 下边框
+ &::after {
+ content: '';
+ position: absolute;
+ left: -16px;
+ width: calc(100% + 32px);
+ height: 1px;
+ background-color: var(--art-gray-300);
+ }
+
+ &::before {
+ top: 0;
+ }
+
+ &::after {
+ bottom: 0;
+ }
+ }
+}
+
+// el-message 样式优化
+.el-message {
+ background-color: var(--default-box-color) !important;
+ border: 0 !important;
+ box-shadow:
+ 0 6px 16px 0 rgba(0, 0, 0, 0.08),
+ 0 3px 6px -4px rgba(0, 0, 0, 0.12),
+ 0 9px 28px 8px rgba(0, 0, 0, 0.05) !important;
+
+ p {
+ font-size: 13px;
+ }
+}
+
+// 修改 el-dropdown 样式
+.el-dropdown-menu {
+ padding: 6px !important;
+ border-radius: 10px !important;
+ border: none !important;
+
+ .el-dropdown-menu__item {
+ padding: 6px 16px !important;
+ border-radius: 6px !important;
+
+ &:hover:not(.is-disabled) {
+ color: var(--art-gray-900) !important;
+ background-color: var(--art-el-active-color) !important;
+ }
+
+ &:focus:not(.is-disabled) {
+ color: var(--art-gray-900) !important;
+ background-color: var(--art-gray-200) !important;
+ }
+ }
+}
+
+// 隐藏 select、dropdown 的三角
+.el-select__popper,
+.el-dropdown__popper {
+ margin-top: -6px !important;
+
+ .el-popper__arrow {
+ display: none;
+ }
+}
+
+.el-dropdown-selfdefine:focus {
+ outline: none !important;
+}
+
+// 处理移动端组件兼容性
+@media screen and (max-width: 640px) {
+ .el-message-box,
+ .el-message,
+ .el-dialog {
+ width: calc(100% - 24px) !important;
+ }
+
+ .el-date-picker.has-sidebar.has-time {
+ width: calc(100% - 24px);
+ left: 12px !important;
+ }
+
+ .el-picker-panel *[slot='sidebar'],
+ .el-picker-panel__sidebar {
+ display: none;
+ }
+
+ .el-picker-panel *[slot='sidebar'] + .el-picker-panel__body,
+ .el-picker-panel__sidebar + .el-picker-panel__body {
+ margin-left: 0;
+ }
+}
+
+// 修改el-button样式
+.el-button {
+ &.el-button--text {
+ background-color: transparent !important;
+ padding: 0 !important;
+
+ span {
+ margin-left: 0 !important;
+ }
+ }
+}
+
+// 修改el-tag样式
+.el-tag {
+ font-weight: 500;
+ transition: all 0s !important;
+
+ &.el-tag--default {
+ height: 26px !important;
+ }
+}
+
+.el-checkbox-group {
+ &.el-table-filter__checkbox-group label.el-checkbox {
+ height: 17px !important;
+
+ .el-checkbox__label {
+ font-weight: 400 !important;
+ }
+ }
+}
+
+.el-radio--default {
+ // 优化单选按钮大小
+ .el-radio__input {
+ .el-radio__inner {
+ width: 16px;
+ height: 16px;
+
+ &::after {
+ width: 6px;
+ height: 6px;
+ }
+ }
+ }
+}
+
+.el-checkbox {
+ .el-checkbox__inner {
+ border-radius: 2px !important;
+ }
+}
+
+// 优化复选框样式
+.el-checkbox--default {
+ .el-checkbox__inner {
+ width: 16px !important;
+ height: 16px !important;
+ border-radius: 4px !important;
+
+ &::before {
+ content: '';
+ height: 4px !important;
+ top: 5px !important;
+ background-color: #fff !important;
+ transform: scale(0.6) !important;
+ }
+ }
+
+ .is-checked {
+ .el-checkbox__inner {
+ &::after {
+ width: 3px;
+ height: 8px;
+ margin: auto;
+ border: 2px solid var(--el-checkbox-checked-icon-color);
+ border-left: 0;
+ border-top: 0;
+ transform: translate(-45%, -60%) rotate(45deg) scale(0.86) !important;
+ transform-origin: center;
+ }
+ }
+ }
+}
+
+.el-notification .el-notification__icon {
+ font-size: 22px !important;
+}
+
+// 修改 el-message-box 样式
+.el-message-box__headerbtn .el-message-box__close,
+.el-dialog__headerbtn .el-dialog__close {
+ top: 7px;
+ right: 7px;
+ width: 30px;
+ height: 30px;
+ border-radius: 5px;
+ transition: all 0.3s;
+
+ &:hover {
+ background-color: var(--art-hover-color) !important;
+ color: var(--art-gray-900) !important;
+ }
+}
+
+.el-message-box {
+ padding: 25px 20px !important;
+}
+
+.el-message-box__title {
+ font-weight: 500 !important;
+}
+
+.el-table__column-filter-trigger i {
+ color: var(--theme-color) !important;
+ margin: -3px 0 0 2px;
+}
+
+// 去除 el-dropdown 鼠标放上去出现的边框
+.el-tooltip__trigger:focus-visible {
+ outline: unset;
+}
+
+// ipad 表单右侧按钮优化
+@media screen and (max-width: 1180px) {
+ .el-table-fixed-column--right {
+ padding-right: 0 !important;
+
+ .el-button {
+ margin: 5px 10px 5px 0 !important;
+ }
+ }
+}
+
+.login-out-dialog {
+ padding: 30px 20px !important;
+ border-radius: 10px !important;
+}
+
+// 修改 dialog 动画
+.dialog-fade-enter-active {
+ .el-dialog:not(.is-draggable) {
+ animation: dialog-open 0.3s cubic-bezier(0.32, 0.14, 0.15, 0.86);
+
+ // 修复 el-dialog 动画后宽度不自适应问题
+ .el-select__selected-item {
+ display: inline-block;
+ }
+ }
+}
+
+.dialog-fade-leave-active {
+ animation: fade-out 0.2s linear;
+
+ .el-dialog:not(.is-draggable) {
+ animation: dialog-close 0.5s;
+ }
+}
+
+@keyframes dialog-open {
+ 0% {
+ opacity: 0;
+ transform: scale(0.2);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes dialog-close {
+ 0% {
+ opacity: 1;
+ transform: scale(1);
+ }
+
+ 100% {
+ opacity: 0;
+ transform: scale(0.2);
+ }
+}
+
+// 遮罩层动画
+@keyframes fade-out {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+// 修改 el-select 样式
+.el-select__popper:not(.el-tree-select__popper) {
+ .el-select-dropdown__list {
+ padding: 5px !important;
+
+ .el-select-dropdown__item {
+ height: 34px !important;
+ line-height: 34px !important;
+ border-radius: 6px !important;
+
+ &.is-selected {
+ color: var(--art-gray-900) !important;
+ font-weight: 400 !important;
+ background-color: var(--art-el-active-color) !important;
+ margin-bottom: 4px !important;
+ }
+
+ &:hover {
+ background-color: var(--art-hover-color) !important;
+ }
+ }
+
+ .el-select-dropdown__item:hover ~ .is-selected,
+ .el-select-dropdown__item.is-selected:has(~ .el-select-dropdown__item:hover) {
+ background-color: transparent !important;
+ }
+ }
+}
+
+// 修改 el-tree-select 样式
+.el-tree-select__popper {
+ .el-select-dropdown__list {
+ padding: 5px !important;
+
+ .el-tree-node {
+ .el-tree-node__content {
+ height: 36px !important;
+ border-radius: 6px !important;
+
+ &:hover {
+ background-color: var(--art-gray-200) !important;
+ }
+ }
+ }
+ }
+}
+
+// 实现水波纹在文字下面效果
+.el-button > span {
+ position: relative;
+ z-index: 10;
+}
+
+// 优化颜色选择器圆角
+.el-color-picker__color {
+ border-radius: 2px !important;
+}
+
+// 优化日期时间选择器底部圆角
+.el-picker-panel {
+ .el-picker-panel__footer {
+ border-radius: 0 0 var(--el-border-radius-base) var(--el-border-radius-base);
+ }
+}
+
+// 优化树型菜单样式
+.el-tree-node__content {
+ border-radius: 4px;
+ margin-bottom: 4px;
+ padding: 1px 0;
+
+ &:hover {
+ background-color: var(--art-hover-color) !important;
+ }
+}
+
+.dark {
+ .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
+ background-color: var(--art-gray-300) !important;
+ }
+}
+
+// 隐藏折叠菜单弹窗 hover 出现的边框
+.menu-left-popper:focus-within,
+.horizontal-menu-popper:focus-within {
+ box-shadow: none !important;
+ outline: none !important;
+}
+
+// 数字输入组件右侧按钮高度跟随自定义组件高度
+.el-input-number--default.is-controls-right {
+ .el-input-number__decrease,
+ .el-input-number__increase {
+ height: calc((var(--el-component-size) / 2)) !important;
+ }
+}
diff --git a/adminSystem/src/assets/styles/core/md.scss b/adminSystem/src/assets/styles/core/md.scss
new file mode 100644
index 0000000..b22fdc2
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/md.scss
@@ -0,0 +1,1036 @@
+/* 文章标题设置(h1-h6)*/
+/* ------------------------------------------------ */
+$font-color: #24292e;
+
+.markdown-body h1,
+.markdown-body h2,
+.markdown-body h3,
+.markdown-body h4,
+.markdown-body h5,
+.markdown-body h6 {
+ color: var(--art-gray-800) !important;
+ margin: 30px 0 10px 0;
+ font-weight: 600;
+}
+
+.markdown-body h1 {
+ font-size: 30px;
+}
+
+@media only screen and (max-width: 550px) {
+ .markdown-body h1 {
+ font-size: 26px;
+ }
+
+ .markdown-body h2 {
+ font-size: 22px;
+ }
+
+ .markdown-body h3 {
+ font-size: 18px;
+ }
+}
+
+/* 块引用 */
+/* ------------------------------------------------ */
+.markdown-body blockquote {
+ color: rgba(60, 60, 67, 0.7);
+ font-size: 15px !important;
+ border-left: 0.18em solid #e7e7e8;
+ background: #f8f8f8;
+ padding: 15px 1em;
+ font-weight: 400 !important;
+}
+
+/* 详情页文章字体颜色 */
+/* ------------------------------------------------ */
+.markdown-body p {
+ line-height: 28px;
+ margin-bottom: 10px;
+}
+
+.markdown-body li,
+.markdown-body p {
+ color: var(--art-gray-800) !important;
+ font-size: 16px !important;
+}
+
+.dark .markdown-body li span {
+ color: var(--art-gray-800) !important;
+ background-color: transparent !important;
+}
+
+.dark .markdown-body p span {
+ color: var(--art-gray-800) !important;
+ background-color: transparent !important;
+}
+
+.line-numbers-mode {
+ background-color: var(--art-code-bg);
+ border-radius: 8px;
+ position: relative;
+ padding-left: 32px;
+ box-sizing: border-box;
+}
+
+.line-numbers-mode pre {
+ flex: 1;
+ border-radius: 0 8px 8px 0;
+ background-color: var(--art-code-bg);
+}
+
+.line-numbers-mode .line-numbers-wrapper {
+ width: 32px;
+ height: 100%;
+ text-align: center;
+ padding: 16px 0;
+ box-sizing: border-box;
+ border-right: 1px solid #000000;
+ position: absolute;
+ left: 0;
+ top: 0;
+}
+
+.line-numbers-mode .line-numbers-wrapper span {
+ height: 23.6px;
+ line-height: 23.6px;
+ display: block;
+ color: #72747b;
+ font-size: 13px;
+ box-sizing: border-box;
+}
+
+.line-numbers-mode .copy-btn {
+ display: inline-block;
+ display: flex;
+ position: absolute;
+ right: 10px;
+ top: 10px;
+ cursor: pointer;
+ opacity: 0;
+ background-color: #000;
+ border-radius: 5px;
+ text-align: center;
+ color: rgba(255, 255, 255, 0.6);
+ transition: opacity 0.3s;
+}
+
+.line-numbers-mode .copy-btn div {
+ width: 34px;
+ height: 34px;
+ line-height: 34px;
+ cursor: pointer;
+ text-align: center;
+ font-size: 20px;
+}
+
+.line-numbers-mode:hover .copy-btn {
+ opacity: 1;
+}
+
+.line-numbers-mode .copy-btn span {
+ height: 34px;
+ line-height: 34px;
+ font-size: 13px;
+ padding-left: 10px;
+ display: none;
+}
+
+.line-numbers-mode .copy-btn .show-copy {
+ opacity: 1;
+ display: block;
+}
+
+.line-numbers-mode ::-webkit-scrollbar-track {
+ background-color: #292b30 !important;
+}
+
+.markdown-body .anchor {
+ float: left;
+ line-height: 1;
+ margin-left: -20px;
+ padding-right: 4px;
+}
+
+.markdown-body .anchor:focus {
+ outline: none;
+}
+
+.markdown-body h1 .octicon-link,
+.markdown-body h2 .octicon-link,
+.markdown-body h3 .octicon-link,
+.markdown-body h4 .octicon-link,
+.markdown-body h5 .octicon-link,
+.markdown-body h6 .octicon-link {
+ color: #1b1f23;
+ vertical-align: middle;
+ visibility: hidden;
+}
+
+.markdown-body h1:hover .anchor,
+.markdown-body h2:hover .anchor,
+.markdown-body h3:hover .anchor,
+.markdown-body h4:hover .anchor,
+.markdown-body h5:hover .anchor,
+.markdown-body h6:hover .anchor {
+ text-decoration: none;
+}
+
+.markdown-body h1:hover .anchor .octicon-link,
+.markdown-body h2:hover .anchor .octicon-link,
+.markdown-body h3:hover .anchor .octicon-link,
+.markdown-body h4:hover .anchor .octicon-link,
+.markdown-body h5:hover .anchor .octicon-link,
+.markdown-body h6:hover .anchor .octicon-link {
+ visibility: visible;
+}
+
+.markdown-body h1:hover .anchor .octicon-link:before,
+.markdown-body h2:hover .anchor .octicon-link:before,
+.markdown-body h3:hover .anchor .octicon-link:before,
+.markdown-body h4:hover .anchor .octicon-link:before,
+.markdown-body h5:hover .anchor .octicon-link:before,
+.markdown-body h6:hover .anchor .octicon-link:before {
+ width: 16px;
+ height: 16px;
+ content: ' ';
+ display: inline-block;
+}
+
+.markdown-body {
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ line-height: 1.5;
+ color: $font-color;
+ font-size: 16px;
+ line-height: 1.5;
+ word-wrap: break-word;
+}
+
+.markdown-body details {
+ display: block;
+}
+
+.markdown-body summary {
+ display: list-item;
+}
+
+.markdown-body a {
+ background-color: initial;
+}
+
+.markdown-body a:active,
+.markdown-body a:hover {
+ outline-width: 0;
+}
+
+.markdown-body strong {
+ font-weight: inherit;
+ font-weight: bolder;
+}
+
+.markdown-body p br {
+ display: inline;
+ line-height: 11px;
+}
+
+.markdown-body img {
+ border-style: none;
+}
+
+.markdown-body hr {
+ box-sizing: initial;
+ height: 0;
+ overflow: visible;
+}
+
+.markdown-body input {
+ font: inherit;
+ margin: 0;
+}
+
+.markdown-body input {
+ overflow: visible;
+}
+
+.markdown-body [type='checkbox'] {
+ box-sizing: border-box;
+ padding: 0;
+}
+
+.markdown-body * {
+ box-sizing: border-box;
+}
+
+.markdown-body input {
+ font-size: inherit;
+ line-height: inherit;
+}
+
+.markdown-body a {
+ color: #0366d6;
+ text-decoration: none;
+}
+
+.markdown-body a:hover {
+ text-decoration: underline;
+}
+
+.markdown-body strong {
+ font-weight: 600;
+}
+
+.markdown-body hr {
+ height: 0;
+ margin: 15px 0;
+ overflow: hidden;
+ background: transparent;
+ border: 0;
+ border-bottom: 1px solid #dfe2e5;
+}
+
+.markdown-body hr:after,
+.markdown-body hr:before {
+ display: table;
+ content: '';
+}
+
+.markdown-body hr:after {
+ clear: both;
+}
+
+.markdown-body table {
+ border-spacing: 0;
+ border-collapse: collapse;
+}
+
+.markdown-body td,
+.markdown-body th {
+ padding: 0;
+}
+
+.markdown-body details summary {
+ cursor: pointer;
+}
+
+.markdown-body kbd {
+ display: inline-block;
+ padding: 3px 5px;
+ font:
+ 11px SFMono-Regular,
+ Consolas,
+ Liberation Mono,
+ Menlo,
+ monospace;
+ line-height: 10px;
+ color: #444d56;
+ vertical-align: middle;
+ background-color: #fafbfc;
+ border: 1px solid #d1d5da;
+ border-radius: 3px;
+ box-shadow: inset 0 -1px 0 #d1d5da;
+}
+
+.markdown-body blockquote {
+ margin: 0;
+}
+
+.markdown-body ol,
+.markdown-body ul {
+ padding-left: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.markdown-body ol ol,
+.markdown-body ul ol {
+ list-style-type: lower-roman;
+}
+
+.markdown-body ol ol ol,
+.markdown-body ol ul ol,
+.markdown-body ul ol ol,
+.markdown-body ul ul ol {
+ list-style-type: lower-alpha;
+}
+
+.markdown-body dd {
+ margin-left: 0;
+}
+
+.markdown-body code,
+.markdown-body pre,
+.markdown-body .line-number {
+ font-size: 14px !important;
+ border-radius: 8px;
+ background-color: #282c34;
+}
+
+.dark {
+ .markdown-body code,
+ .markdown-body pre,
+ .markdown-body .line-number {
+ background-color: #252525;
+ }
+}
+
+.markdown-body pre {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.markdown-body input::-webkit-inner-spin-button,
+.markdown-body input::-webkit-outer-spin-button {
+ margin: 0;
+ -webkit-appearance: none;
+ appearance: none;
+}
+
+.markdown-body :checked + .radio-label {
+ position: relative;
+ z-index: 1;
+ border-color: #0366d6;
+}
+
+.markdown-body .border {
+ border: 1px solid #e1e4e8 !important;
+}
+
+.markdown-body .border-0 {
+ border: 0 !important;
+}
+
+.markdown-body .border-bottom {
+ border-bottom: 1px solid #e1e4e8 !important;
+}
+
+.markdown-body .rounded-1 {
+ border-radius: 3px !important;
+}
+
+.markdown-body .bg-white {
+ background-color: #fff !important;
+}
+
+.markdown-body .bg-gray-light {
+ background-color: #fafbfc !important;
+}
+
+.markdown-body .text-gray-light {
+ color: #6a737d !important;
+}
+
+.markdown-body .mb-0 {
+ margin-bottom: 0 !important;
+}
+
+.markdown-body .my-2 {
+ margin-top: 8px !important;
+ margin-bottom: 8px !important;
+}
+
+.markdown-body .pl-0 {
+ padding-left: 0 !important;
+}
+
+.markdown-body .py-0 {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+}
+
+.markdown-body .pl-1 {
+ padding-left: 4px !important;
+}
+
+.markdown-body .pl-2 {
+ padding-left: 8px !important;
+}
+
+.markdown-body .py-2 {
+ padding-top: 8px !important;
+ padding-bottom: 8px !important;
+}
+
+.markdown-body .pl-3,
+.markdown-body .px-3 {
+ padding-left: 16px !important;
+}
+
+.markdown-body .px-3 {
+ padding-right: 16px !important;
+}
+
+.markdown-body .pl-4 {
+ padding-left: 24px !important;
+}
+
+.markdown-body .pl-5 {
+ padding-left: 32px !important;
+}
+
+.markdown-body .pl-6 {
+ padding-left: 40px !important;
+}
+
+.markdown-body .f6 {
+ font-size: 12px !important;
+}
+
+.markdown-body .lh-condensed {
+ line-height: 1.25 !important;
+}
+
+.markdown-body .text-bold {
+ font-weight: 600 !important;
+}
+
+.markdown-body .pl-c {
+ color: #6a737d;
+}
+
+.markdown-body .pl-c1,
+.markdown-body .pl-s .pl-v {
+ color: #005cc5;
+}
+
+.markdown-body .pl-e,
+.markdown-body .pl-en {
+ color: #6f42c1;
+}
+
+.markdown-body .pl-s .pl-s1,
+.markdown-body .pl-smi {
+ color: $font-color;
+}
+
+.markdown-body .pl-ent {
+ color: #22863a;
+}
+
+.markdown-body .pl-k {
+ color: #d73a49;
+}
+
+.markdown-body .pl-pds,
+.markdown-body .pl-s,
+.markdown-body .pl-s .pl-pse .pl-s1,
+.markdown-body .pl-sr,
+.markdown-body .pl-sr .pl-cce,
+.markdown-body .pl-sr .pl-sra,
+.markdown-body .pl-sr .pl-sre {
+ color: #032f62;
+}
+
+.markdown-body .pl-smw,
+.markdown-body .pl-v {
+ color: #e36209;
+}
+
+.markdown-body .pl-bu {
+ color: #b31d28;
+}
+
+.markdown-body .pl-ii {
+ color: #fafbfc;
+ background-color: #b31d28;
+}
+
+.markdown-body .pl-c2 {
+ color: #fafbfc;
+ background-color: #d73a49;
+}
+
+.markdown-body .pl-c2:before {
+ content: '^M';
+}
+
+.markdown-body .pl-sr .pl-cce {
+ font-weight: 700;
+ color: #22863a;
+}
+
+.markdown-body .pl-ml {
+ color: #735c0f;
+}
+
+.markdown-body .pl-mh,
+.markdown-body .pl-mh .pl-en,
+.markdown-body .pl-ms {
+ font-weight: 700;
+ color: #005cc5;
+}
+
+.markdown-body .pl-mi {
+ font-style: italic;
+ color: $font-color;
+}
+
+.markdown-body .pl-mb {
+ font-weight: 700;
+ color: $font-color;
+}
+
+.markdown-body .pl-md {
+ color: #b31d28;
+ background-color: #ffeef0;
+}
+
+.markdown-body .pl-mi1 {
+ color: #22863a;
+ background-color: #f0fff4;
+}
+
+.markdown-body .pl-mc {
+ color: #e36209;
+ background-color: #ffebda;
+}
+
+.markdown-body .pl-mi2 {
+ color: #f6f8fa;
+ background-color: #005cc5;
+}
+
+.markdown-body .pl-mdr {
+ font-weight: 700;
+ color: #6f42c1;
+}
+
+.markdown-body .pl-ba {
+ color: #586069;
+}
+
+.markdown-body .pl-sg {
+ color: #959da5;
+}
+
+.markdown-body .pl-corl {
+ text-decoration: underline;
+ color: #032f62;
+}
+
+.markdown-body .mb-0 {
+ margin-bottom: 0 !important;
+}
+
+.markdown-body .my-2 {
+ margin-bottom: 8px !important;
+}
+
+.markdown-body .my-2 {
+ margin-top: 8px !important;
+}
+
+.markdown-body .pl-0 {
+ padding-left: 0 !important;
+}
+
+.markdown-body .py-0 {
+ padding-top: 0 !important;
+ padding-bottom: 0 !important;
+}
+
+.markdown-body .pl-1 {
+ padding-left: 4px !important;
+}
+
+.markdown-body .pl-2 {
+ padding-left: 8px !important;
+}
+
+.markdown-body .py-2 {
+ padding-top: 8px !important;
+ padding-bottom: 8px !important;
+}
+
+.markdown-body .pl-3 {
+ padding-left: 16px !important;
+}
+
+.markdown-body .pl-4 {
+ padding-left: 24px !important;
+}
+
+.markdown-body .pl-5 {
+ padding-left: 32px !important;
+}
+
+.markdown-body .pl-6 {
+ padding-left: 40px !important;
+}
+
+.markdown-body .pl-7 {
+ padding-left: 48px !important;
+}
+
+.markdown-body .pl-8 {
+ padding-left: 64px !important;
+}
+
+.markdown-body .pl-9 {
+ padding-left: 80px !important;
+}
+
+.markdown-body .pl-10 {
+ padding-left: 96px !important;
+}
+
+.markdown-body .pl-11 {
+ padding-left: 112px !important;
+}
+
+.markdown-body .pl-12 {
+ padding-left: 128px !important;
+}
+
+.markdown-body hr {
+ border-bottom-color: #eee;
+}
+
+.markdown-body kbd {
+ display: inline-block;
+ padding: 3px 5px;
+ font:
+ 11px SFMono-Regular,
+ Consolas,
+ Liberation Mono,
+ Menlo,
+ monospace;
+ line-height: 10px;
+ color: #444d56;
+ vertical-align: middle;
+ background-color: #fafbfc;
+ border: 1px solid #d1d5da;
+ border-radius: 3px;
+ box-shadow: inset 0 -1px 0 #d1d5da;
+}
+
+.markdown-body:after,
+.markdown-body:before {
+ display: table;
+ content: '';
+}
+
+.markdown-body:after {
+ clear: both;
+}
+
+.markdown-body > :first-child {
+ margin-top: 0 !important;
+}
+
+.markdown-body > :last-child {
+ margin-bottom: 0 !important;
+}
+
+.markdown-body a:not([href]) {
+ color: inherit;
+ text-decoration: none;
+}
+
+.markdown-body blockquote,
+.markdown-body details,
+.markdown-body dl,
+.markdown-body ol,
+.markdown-body pre,
+.markdown-body table,
+.markdown-body ul {
+ margin-top: 0;
+ margin-bottom: 16px;
+}
+
+.markdown-body hr {
+ height: 0.25em;
+ padding: 0;
+ margin: 24px 0;
+ background-color: #e1e4e8;
+ border: 0;
+}
+
+.markdown-body blockquote > :first-child {
+ margin-top: 0;
+}
+
+.markdown-body blockquote > :last-child {
+ margin-bottom: 0;
+}
+
+.markdown-body ol,
+.markdown-body ul {
+ padding-left: 1em;
+}
+
+.markdown-body ol ol,
+.markdown-body ol ul,
+.markdown-body ul ol,
+.markdown-body ul ul {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.markdown-body li {
+ line-height: 28px;
+ font-size: 14px;
+ word-wrap: break-all;
+ list-style: disc;
+ margin-left: 10px;
+}
+
+.markdown-body li > p {
+ margin-top: 16px;
+}
+
+.markdown-body li + li {
+ margin-top: 0.25em;
+}
+
+.markdown-body dl {
+ padding: 0;
+}
+
+.markdown-body dl dt {
+ padding: 0;
+ margin-top: 16px;
+ font-size: 1em;
+ font-style: italic;
+ font-weight: 600;
+}
+
+.markdown-body dl dd {
+ padding: 0 16px;
+ margin-bottom: 16px;
+}
+
+.markdown-body table {
+ display: block;
+ width: 100%;
+ overflow: auto;
+}
+
+.markdown-body table th {
+ font-weight: 600;
+}
+
+.markdown-body table td,
+.markdown-body table th {
+ padding: 6px 13px;
+ border: 1px solid #dfe2e5;
+}
+
+.markdown-body table tr {
+ background-color: #fff;
+ border-top: 1px solid #c6cbd1;
+}
+
+.markdown-body table tr:nth-child(2n) {
+ background-color: #f6f8fa;
+}
+
+.markdown-body img {
+ max-width: 100%;
+ box-sizing: initial;
+ background-color: #fff;
+ border: 1px solid #eee;
+ border: 1px solid var(--art-c-border-2);
+ cursor: zoom-in;
+}
+
+.markdown-body img[align='right'] {
+ padding-left: 20px;
+}
+
+.markdown-body img[align='left'] {
+ padding-right: 20px;
+}
+
+.markdown-body code {
+ padding: 0.2em 0.4em;
+ margin: 0;
+ font-size: 85%;
+ background-color: rgba(27, 31, 35, 0.05);
+ border-radius: 3px;
+}
+
+.markdown-body pre {
+ word-wrap: normal;
+}
+
+.markdown-body pre > code {
+ padding: 0;
+ margin: 0;
+ font-size: 100%;
+ word-break: normal;
+ white-space: pre;
+ background: transparent;
+ border: 0;
+}
+
+.markdown-body .highlight {
+ margin-bottom: 16px;
+}
+
+.markdown-body .highlight pre {
+ margin-bottom: 0;
+ word-break: normal;
+}
+
+.markdown-body .highlight pre,
+.markdown-body pre {
+ padding: 15px 20px 15px 0;
+ overflow: auto;
+ font-size: 92%;
+ line-height: 1.6;
+}
+
+.markdown-body pre code {
+ display: inline;
+ max-width: auto;
+ padding: 0;
+ margin: 0;
+ overflow: visible;
+ line-height: inherit;
+ word-wrap: normal;
+ background-color: initial;
+ border: 0;
+}
+
+.markdown-body .commit-tease-sha {
+ display: inline-block;
+ font-size: 90%;
+ color: #444d56;
+}
+
+.markdown-body .full-commit .btn-outline:not(:disabled):hover {
+ color: #005cc5;
+ border-color: #005cc5;
+}
+
+.markdown-body .blob-wrapper {
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.markdown-body .blob-wrapper-embedded {
+ max-height: 240px;
+ overflow-y: auto;
+}
+
+.markdown-body .blob-num {
+ width: 1%;
+ min-width: 50px;
+ padding-right: 10px;
+ padding-left: 10px;
+ font-size: 12px;
+ line-height: 20px;
+ color: rgba(27, 31, 35, 0.3);
+ text-align: right;
+ white-space: nowrap;
+ vertical-align: top;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.markdown-body .blob-num:hover {
+ color: rgba(27, 31, 35, 0.6);
+}
+
+.markdown-body .blob-num:before {
+ content: attr(data-line-number);
+}
+
+.markdown-body .blob-code {
+ position: relative;
+ padding-right: 10px;
+ padding-left: 10px;
+ line-height: 20px;
+ vertical-align: top;
+}
+
+.markdown-body .blob-code-inner {
+ overflow: visible;
+ font-size: 12px;
+ color: $font-color;
+ word-wrap: normal;
+ white-space: pre;
+}
+
+.markdown-body .pl-token.active,
+.markdown-body .pl-token:hover {
+ cursor: pointer;
+ background: #ffea7f;
+}
+
+.markdown-body .tab-size[data-tab-size='1'] {
+ -moz-tab-size: 1;
+ tab-size: 1;
+}
+
+.markdown-body .tab-size[data-tab-size='2'] {
+ -moz-tab-size: 2;
+ tab-size: 2;
+}
+
+.markdown-body .tab-size[data-tab-size='3'] {
+ -moz-tab-size: 3;
+ tab-size: 3;
+}
+
+.markdown-body .tab-size[data-tab-size='4'] {
+ -moz-tab-size: 4;
+ tab-size: 4;
+}
+
+.markdown-body .tab-size[data-tab-size='5'] {
+ -moz-tab-size: 5;
+ tab-size: 5;
+}
+
+.markdown-body .tab-size[data-tab-size='6'] {
+ -moz-tab-size: 6;
+ tab-size: 6;
+}
+
+.markdown-body .tab-size[data-tab-size='7'] {
+ -moz-tab-size: 7;
+ tab-size: 7;
+}
+
+.markdown-body .tab-size[data-tab-size='8'] {
+ -moz-tab-size: 8;
+ tab-size: 8;
+}
+
+.markdown-body .tab-size[data-tab-size='9'] {
+ -moz-tab-size: 9;
+ tab-size: 9;
+}
+
+.markdown-body .tab-size[data-tab-size='10'] {
+ -moz-tab-size: 10;
+ tab-size: 10;
+}
+
+.markdown-body .tab-size[data-tab-size='11'] {
+ -moz-tab-size: 11;
+ tab-size: 11;
+}
+
+.markdown-body .tab-size[data-tab-size='12'] {
+ -moz-tab-size: 12;
+ tab-size: 12;
+}
+
+.markdown-body .task-list-item {
+ list-style-type: none;
+}
+
+.markdown-body .task-list-item + .task-list-item {
+ margin-top: 3px;
+}
+
+.markdown-body .task-list-item input {
+ margin: 0 0.2em 0.25em -1.6em;
+ vertical-align: middle;
+}
diff --git a/adminSystem/src/assets/styles/core/mixin.scss b/adminSystem/src/assets/styles/core/mixin.scss
new file mode 100644
index 0000000..db36888
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/mixin.scss
@@ -0,0 +1,157 @@
+// sass 混合宏(函数)
+
+/**
+* 溢出省略号
+* @param {Number} 行数
+*/
+@mixin ellipsis($rowCount: 1) {
+ @if $rowCount <=1 {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ } @else {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-line-clamp: $rowCount;
+ -webkit-box-orient: vertical;
+ }
+}
+
+/**
+* 控制用户能否选中文本
+* @param {String} 类型
+*/
+@mixin userSelect($value: none) {
+ user-select: $value;
+ -moz-user-select: $value;
+ -ms-user-select: $value;
+ -webkit-user-select: $value;
+}
+
+// 绝对定位居中
+@mixin absoluteCenter() {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ margin: auto;
+}
+
+/**
+* css3动画
+*
+*/
+@mixin animation(
+ $from: (
+ width: 0px
+ ),
+ $to: (
+ width: 100px
+ ),
+ $name: mymove,
+ $animate: mymove 2s 1 linear infinite
+) {
+ -webkit-animation: $animate;
+ -o-animation: $animate;
+ animation: $animate;
+
+ @keyframes #{$name} {
+ from {
+ @each $key, $value in $from {
+ #{$key}: #{$value};
+ }
+ }
+
+ to {
+ @each $key, $value in $to {
+ #{$key}: #{$value};
+ }
+ }
+ }
+
+ @-webkit-keyframes #{$name} {
+ from {
+ @each $key, $value in $from {
+ $key: $value;
+ }
+ }
+
+ to {
+ @each $key, $value in $to {
+ $key: $value;
+ }
+ }
+ }
+}
+
+// 圆形盒子
+@mixin circle($size: 11px, $bg: #fff) {
+ border-radius: 50%;
+ width: $size;
+ height: $size;
+ line-height: $size;
+ text-align: center;
+ background: $bg;
+}
+
+// placeholder
+@mixin placeholder($color: #bbb) {
+ // Firefox
+ &::-moz-placeholder {
+ color: $color;
+ opacity: 1;
+ }
+
+ // Internet Explorer 10+
+ &:-ms-input-placeholder {
+ color: $color;
+ }
+
+ // Safari and Chrome
+ &::-webkit-input-placeholder {
+ color: $color;
+ }
+
+ &:placeholder-shown {
+ text-overflow: ellipsis;
+ }
+}
+
+//背景透明,文字不透明。兼容IE8
+@mixin betterTransparentize($color, $alpha) {
+ $c: rgba($color, $alpha);
+ $ie_c: ie_hex_str($c);
+ background: rgba($color, 1);
+ background: $c;
+ background: transparent \9;
+ zoom: 1;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#{$ie_c}, endColorstr=#{$ie_c});
+ -ms-filter: 'progid:DXImageTransform.Microsoft.gradient(startColorstr=#{$ie_c}, endColorstr=#{$ie_c})';
+}
+
+//添加浏览器前缀
+@mixin browserPrefix($propertyName, $value) {
+ @each $prefix in -webkit-, -moz-, -ms-, -o-, '' {
+ #{$prefix}#{$propertyName}: $value;
+ }
+}
+
+// 边框
+@mixin border($color: red) {
+ border: 1px solid $color;
+}
+
+// 背景滤镜
+@mixin backdropBlur() {
+ --tw-backdrop-blur: blur(30px);
+ -webkit-backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness)
+ var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate)
+ var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate)
+ var(--tw-backdrop-sepia);
+ backdrop-filter: var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast)
+ var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert)
+ var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);
+}
diff --git a/adminSystem/src/assets/styles/core/reset.scss b/adminSystem/src/assets/styles/core/reset.scss
new file mode 100644
index 0000000..17a3bcf
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/reset.scss
@@ -0,0 +1,41 @@
+@charset "UTF-8";
+
+/*滚动条*/
+/*滚动条整体部分,必须要设置*/
+::-webkit-scrollbar {
+ width: 8px !important;
+ height: 0 !important;
+}
+
+/*滚动条的轨道*/
+::-webkit-scrollbar-track {
+ background-color: var(--art-gray-200);
+}
+
+/*滚动条的滑块按钮*/
+::-webkit-scrollbar-thumb {
+ border-radius: 5px;
+ background-color: #cccccc !important;
+ transition: all 0.2s;
+ -webkit-transition: all 0.2s;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background-color: #b0abab !important;
+}
+
+/*滚动条的上下两端的按钮*/
+::-webkit-scrollbar-button {
+ height: 0px;
+ width: 0;
+}
+
+.dark {
+ ::-webkit-scrollbar-track {
+ background-color: var(--default-bg-color);
+ }
+
+ ::-webkit-scrollbar-thumb {
+ background-color: var(--art-gray-300) !important;
+ }
+}
diff --git a/adminSystem/src/assets/styles/core/router-transition.scss b/adminSystem/src/assets/styles/core/router-transition.scss
new file mode 100644
index 0000000..f47c741
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/router-transition.scss
@@ -0,0 +1,104 @@
+@use 'sass:map';
+
+// === 变量区域 ===
+$transition: (
+ // 动画持续时间
+ duration: 0.25s,
+ // 滑动动画的移动距离
+ distance: 15px,
+ // 默认缓动函数
+ easing: cubic-bezier(0.25, 0.1, 0.25, 1),
+ // 淡入淡出专用的缓动函数
+ fade-easing: cubic-bezier(0.4, 0, 0.6, 1)
+);
+
+// 抽取配置值函数,提高可复用性
+@function transition-config($key) {
+ @return map.get($transition, $key);
+}
+
+// 变量简写
+$duration: transition-config('duration');
+$distance: transition-config('distance');
+$easing: transition-config('easing');
+$fade-easing: transition-config('fade-easing');
+
+// === 动画类 ===
+
+// 淡入淡出动画
+.fade {
+ &-enter-active,
+ &-leave-active {
+ transition: opacity $duration $fade-easing;
+ will-change: opacity;
+ }
+
+ &-enter-from,
+ &-leave-to {
+ opacity: 0;
+ }
+
+ &-enter-to,
+ &-leave-from {
+ opacity: 1;
+ }
+}
+
+// 滑动动画通用样式
+@mixin slide-transition($direction) {
+ $distance-x: 0;
+ $distance-y: 0;
+
+ @if $direction == 'left' {
+ $distance-x: -$distance;
+ } @else if $direction == 'right' {
+ $distance-x: $distance;
+ } @else if $direction == 'top' {
+ $distance-y: -$distance;
+ } @else if $direction == 'bottom' {
+ $distance-y: $distance;
+ }
+
+ &-enter-active {
+ transition:
+ opacity $duration $easing,
+ transform $duration $easing;
+ will-change: opacity, transform;
+ }
+
+ &-leave-active {
+ transition:
+ opacity calc($duration * 0.7) $easing,
+ transform calc($duration * 0.7) $easing;
+ will-change: opacity, transform;
+ }
+
+ &-enter-from {
+ opacity: 0;
+ transform: translate3d($distance-x, $distance-y, 0);
+ }
+
+ &-enter-to {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+ }
+
+ &-leave-to {
+ opacity: 0;
+ transform: translate3d(-$distance-x, -$distance-y, 0);
+ }
+}
+
+// 滑动动画方向类
+.slide-left {
+ @include slide-transition('left');
+}
+.slide-right {
+ @include slide-transition('right');
+}
+.slide-top {
+ @include slide-transition('top');
+}
+.slide-bottom {
+ @include slide-transition('bottom');
+}
diff --git a/adminSystem/src/assets/styles/core/tailwind.css b/adminSystem/src/assets/styles/core/tailwind.css
new file mode 100644
index 0000000..1a9e22c
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/tailwind.css
@@ -0,0 +1,208 @@
+@import 'tailwindcss';
+@custom-variant dark (&:where(.dark, .dark *));
+
+/* ==================== Light Mode Variables ==================== */
+:root {
+ /* Base Colors */
+ --art-color: #ffffff;
+ --theme-color: var(--main-color);
+
+ /* Theme Colors - OKLCH Format */
+ --art-primary: oklch(0.7 0.23 260);
+ --art-secondary: oklch(0.72 0.19 231.6);
+ --art-error: oklch(0.73 0.15 25.3);
+ --art-info: oklch(0.58 0.03 254.1);
+ --art-success: oklch(0.78 0.17 166.1);
+ --art-warning: oklch(0.78 0.14 75.5);
+ --art-danger: oklch(0.68 0.22 25.3);
+
+ /* Gray Scale - Light Mode */
+ --art-gray-100: #f9fafb;
+ --art-gray-200: #f2f4f5;
+ --art-gray-300: #e6eaeb;
+ --art-gray-400: #dbdfe1;
+ --art-gray-500: #949eb7;
+ --art-gray-600: #7987a1;
+ --art-gray-700: #4d5875;
+ --art-gray-800: #383853;
+ --art-gray-900: #323251;
+
+ /* Border Colors */
+ --art-card-border: rgba(0, 0, 0, 0.08);
+
+ --default-border: #e2e8ee;
+ --default-border-dashed: #dbdfe9;
+
+ /* Background Colors */
+ --default-bg-color: #fafbfc;
+ --default-box-color: #ffffff;
+
+ /* Hover Color */
+ --art-hover-color: #edeff0;
+
+ /* Active Color */
+ --art-active-color: #f2f4f5;
+
+ /* Element Component Active Color */
+ --art-el-active-color: #f2f4f5;
+}
+
+/* ==================== Dark Mode Variables ==================== */
+.dark {
+ /* Base Colors */
+ --art-color: #000000;
+
+ /* Gray Scale - Dark Mode */
+ --art-gray-100: #110f0f;
+ --art-gray-200: #17171c;
+ --art-gray-300: #393946;
+ --art-gray-400: #505062;
+ --art-gray-500: #73738c;
+ --art-gray-600: #8f8fa3;
+ --art-gray-700: #ababba;
+ --art-gray-800: #c7c7d1;
+ --art-gray-900: #e3e3e8;
+
+ /* Border Colors */
+ --art-card-border: rgba(255, 255, 255, 0.08);
+
+ --default-border: rgba(255, 255, 255, 0.1);
+ --default-border-dashed: #363843;
+
+ /* Background Colors */
+ --default-bg-color: #070707;
+ --default-box-color: #161618;
+
+ /* Hover Color */
+ --art-hover-color: #252530;
+
+ /* Active Color */
+ --art-active-color: #202226;
+
+ /* Element Component Active Color */
+ --art-el-active-color: #2e2e38;
+}
+
+/* ==================== Tailwind Theme Configuration ==================== */
+@theme {
+ /* Box Color (Light: white / Dark: black) */
+ --color-box: var(--default-box-color);
+
+ /* System Theme Color */
+ --color-theme: var(--theme-color);
+
+ /* Hover Color */
+ --color-hover-color: var(--art-hover-color);
+
+ /* Active Color */
+ --color-active-color: var(--art-active-color);
+
+ /* Active Color */
+ --color-el-active-color: var(--art-active-color);
+
+ /* ElementPlus Theme Colors */
+ --color-primary: var(--art-primary);
+ --color-secondary: var(--art-secondary);
+ --color-error: var(--art-error);
+ --color-info: var(--art-info);
+ --color-success: var(--art-success);
+ --color-warning: var(--art-warning);
+ --color-danger: var(--art-danger);
+
+ /* Gray Scale Colors (Auto-adapts to dark mode) */
+ --color-g-100: var(--art-gray-100);
+ --color-g-200: var(--art-gray-200);
+ --color-g-300: var(--art-gray-300);
+ --color-g-400: var(--art-gray-400);
+ --color-g-500: var(--art-gray-500);
+ --color-g-600: var(--art-gray-600);
+ --color-g-700: var(--art-gray-700);
+ --color-g-800: var(--art-gray-800);
+ --color-g-900: var(--art-gray-900);
+}
+
+/* ==================== Custom Border Radius Utilities ==================== */
+@utility rounded-custom-xs {
+ border-radius: calc(var(--custom-radius) / 2);
+}
+
+@utility rounded-custom-sm {
+ border-radius: calc(var(--custom-radius) / 2 + 2px);
+}
+
+/* ==================== Custom Utility Classes ==================== */
+@layer utilities {
+ /* Flexbox Layout Utilities */
+ .flex-c {
+ @apply flex items-center;
+ }
+
+ .flex-b {
+ @apply flex justify-between;
+ }
+
+ .flex-cc {
+ @apply flex items-center justify-center;
+ }
+
+ .flex-cb {
+ @apply flex items-center justify-between;
+ }
+
+ /* Transition Utilities */
+ .tad-200 {
+ @apply transition-all duration-200;
+ }
+
+ .tad-300 {
+ @apply transition-all duration-300;
+ }
+
+ /* Border Utilities */
+ .border-full-d {
+ @apply border border-[var(--default-border)];
+ }
+
+ .border-b-d {
+ @apply border-b border-[var(--default-border)];
+ }
+
+ .border-t-d {
+ @apply border-t border-[var(--default-border)];
+ }
+
+ .border-l-d {
+ @apply border-l border-[var(--default-border)];
+ }
+
+ .border-r-d {
+ @apply border-r border-[var(--default-border)];
+ }
+
+ /* Cursor Utilities */
+ .c-p {
+ @apply cursor-pointer;
+ }
+}
+
+/* ==================== Custom Component Classes ==================== */
+@layer components {
+ /* Art Card Header Component */
+ .art-card-header {
+ @apply flex justify-between pr-6 pb-1;
+
+ .title {
+ h4 {
+ @apply text-lg font-medium text-g-900;
+ }
+
+ p {
+ @apply mt-1 text-sm text-g-600;
+
+ span {
+ @apply ml-2 font-medium;
+ }
+ }
+ }
+ }
+}
diff --git a/adminSystem/src/assets/styles/core/theme-animation.scss b/adminSystem/src/assets/styles/core/theme-animation.scss
new file mode 100644
index 0000000..377b945
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/theme-animation.scss
@@ -0,0 +1,63 @@
+// 定义基础变量
+$bg-animation-color-light: #000;
+$bg-animation-color-dark: #fff;
+$bg-animation-duration: 0.5s;
+
+html {
+ --bg-animation-color: $bg-animation-color-light;
+
+ &.dark {
+ --bg-animation-color: $bg-animation-color-dark;
+ }
+
+ // View transition styles
+ &::view-transition-old(*) {
+ animation: none;
+ }
+
+ &::view-transition-new(*) {
+ animation: clip $bg-animation-duration ease-in both;
+ }
+
+ &::view-transition-old(root) {
+ z-index: 1;
+ }
+
+ &::view-transition-new(root) {
+ z-index: 9999;
+ }
+
+ &.dark {
+ &::view-transition-old(*) {
+ animation: clip $bg-animation-duration ease-in reverse both;
+ }
+
+ &::view-transition-new(*) {
+ animation: none;
+ }
+
+ &::view-transition-old(root) {
+ z-index: 9999;
+ }
+
+ &::view-transition-new(root) {
+ z-index: 1;
+ }
+ }
+}
+
+// 定义动画
+@keyframes clip {
+ from {
+ clip-path: circle(0% at var(--x) var(--y));
+ }
+
+ to {
+ clip-path: circle(var(--r) at var(--x) var(--y));
+ }
+}
+
+// body 相关样式
+body {
+ background-color: var(--bg-animation-color);
+}
diff --git a/adminSystem/src/assets/styles/core/theme-change.scss b/adminSystem/src/assets/styles/core/theme-change.scss
new file mode 100644
index 0000000..5b640d2
--- /dev/null
+++ b/adminSystem/src/assets/styles/core/theme-change.scss
@@ -0,0 +1,11 @@
+// 主题切换过渡优化,优化除视觉上的不适感
+.theme-change {
+ * {
+ transition: 0s !important;
+ }
+
+ .el-switch__core,
+ .el-switch__action {
+ transition: all 0.3s !important;
+ }
+}
diff --git a/adminSystem/src/assets/styles/custom/one-dark-pro.scss b/adminSystem/src/assets/styles/custom/one-dark-pro.scss
new file mode 100644
index 0000000..36bdf63
--- /dev/null
+++ b/adminSystem/src/assets/styles/custom/one-dark-pro.scss
@@ -0,0 +1,98 @@
+.hljs {
+ display: block;
+ overflow-x: auto;
+ padding: 0.5em;
+
+ color: #a6accd;
+}
+
+.hljs-string,
+.hljs-section,
+.hljs-selector-class,
+.hljs-template-variable,
+.hljs-deletion {
+ color: #aed07e !important;
+}
+
+.hljs-comment,
+.hljs-quote {
+ color: #6f747d;
+}
+
+.hljs-doctag,
+.hljs-keyword,
+.hljs-formula {
+ color: #c792ea;
+}
+
+.hljs-section,
+.hljs-name,
+.hljs-selector-tag,
+.hljs-deletion,
+.hljs-subst {
+ color: #c86068;
+}
+
+.hljs-literal {
+ color: #56b6c2;
+}
+
+.hljs-string,
+.hljs-regexp,
+.hljs-addition,
+.hljs-attribute,
+.hljs-meta-string {
+ color: #abb2bf;
+}
+
+.hljs-attribute {
+ color: #c792ea;
+}
+
+.hljs-function {
+ color: #c792ea;
+}
+
+.hljs-type {
+ color: #f07178;
+}
+
+.hljs-title {
+ color: #82aaff !important;
+}
+
+.hljs-built_in,
+.hljs-class {
+ color: #82aaff;
+}
+
+// 括号
+.hljs-params {
+ color: #a6accd;
+}
+
+.hljs-attr,
+.hljs-variable,
+.hljs-template-variable,
+.hljs-selector-class,
+.hljs-selector-attr,
+.hljs-selector-pseudo,
+.hljs-number {
+ color: #de7e61;
+}
+
+.hljs-symbol,
+.hljs-bullet,
+.hljs-link,
+.hljs-meta,
+.hljs-selector-id {
+ color: #61aeee;
+}
+
+.hljs-strong {
+ font-weight: bold;
+}
+
+.hljs-link {
+ text-decoration: underline;
+}
diff --git a/adminSystem/src/assets/styles/index.scss b/adminSystem/src/assets/styles/index.scss
new file mode 100644
index 0000000..cdc2ddc
--- /dev/null
+++ b/adminSystem/src/assets/styles/index.scss
@@ -0,0 +1,23 @@
+// 重置默认样式
+@use './core/reset.scss';
+
+// 应用全局样式
+@use './core/app.scss';
+
+// Element Plus 样式优化
+@use './core/el-ui.scss';
+
+// Element Plus 暗黑主题
+@use './core/el-dark.scss';
+
+// 暗黑主题样式优化
+@use './core/dark.scss';
+
+// 路由切换动画
+@use './core/router-transition';
+
+// 主题切换过渡优化
+@use './core/theme-change.scss';
+
+// 主题切换圆形扩散动画
+@use './core/theme-animation.scss';
diff --git a/adminSystem/src/assets/svg/loading.ts b/adminSystem/src/assets/svg/loading.ts
new file mode 100644
index 0000000..fdfb078
--- /dev/null
+++ b/adminSystem/src/assets/svg/loading.ts
@@ -0,0 +1,32 @@
+// 自定义四点旋转SVG
+export const fourDotsSpinnerSvg = `
+
+`
diff --git a/adminSystem/src/components/core/banners/art-basic-banner/index.vue b/adminSystem/src/components/core/banners/art-basic-banner/index.vue
new file mode 100644
index 0000000..65b47e4
--- /dev/null
+++ b/adminSystem/src/components/core/banners/art-basic-banner/index.vue
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+ {{
+ subtitle
+ }}
+
+
+
+
+
+ {{ buttonConfig?.text }}
+
+
+
+
+
+
+
+
![背景图片]()
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/banners/art-card-banner/index.vue b/adminSystem/src/components/core/banners/art-card-banner/index.vue
new file mode 100644
index 0000000..8a5f9d4
--- /dev/null
+++ b/adminSystem/src/components/core/banners/art-card-banner/index.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+
![]()
+
+
+
{{ title }}
+
{{ description }}
+
+
+
+ {{ cancelButton?.text }}
+
+
+ {{ button?.text }}
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/base/art-back-to-top/index.vue b/adminSystem/src/components/core/base/art-back-to-top/index.vue
new file mode 100644
index 0000000..6f8da61
--- /dev/null
+++ b/adminSystem/src/components/core/base/art-back-to-top/index.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/base/art-logo/index.vue b/adminSystem/src/components/core/base/art-logo/index.vue
new file mode 100644
index 0000000..8bc8309
--- /dev/null
+++ b/adminSystem/src/components/core/base/art-logo/index.vue
@@ -0,0 +1,21 @@
+
+
+
+

+
+
+
+
diff --git a/adminSystem/src/components/core/base/art-svg-icon/index.vue b/adminSystem/src/components/core/base/art-svg-icon/index.vue
new file mode 100644
index 0000000..0bfcd0c
--- /dev/null
+++ b/adminSystem/src/components/core/base/art-svg-icon/index.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/cards/art-bar-chart-card/index.vue b/adminSystem/src/components/core/cards/art-bar-chart-card/index.vue
new file mode 100644
index 0000000..6815c2b
--- /dev/null
+++ b/adminSystem/src/components/core/cards/art-bar-chart-card/index.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+ {{ value }}
+
+
{{ label }}
+
+
+ {{ percentage > 0 ? '+' : '' }}{{ percentage }}%
+
+
+ {{ date }}
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/cards/art-data-list-card/index.vue b/adminSystem/src/components/core/cards/art-data-list-card/index.vue
new file mode 100644
index 0000000..fc43323
--- /dev/null
+++ b/adminSystem/src/components/core/cards/art-data-list-card/index.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
{{ title }}
+
{{ subtitle }}
+
+
+
+
+
+
{{ item.title }}
+
{{ item.status }}
+
+
{{ item.time }}
+
+
+
查看更多
+
+
+
+
diff --git a/adminSystem/src/components/core/cards/art-donut-chart-card/index.vue b/adminSystem/src/components/core/cards/art-donut-chart-card/index.vue
new file mode 100644
index 0000000..df2dcbb
--- /dev/null
+++ b/adminSystem/src/components/core/cards/art-donut-chart-card/index.vue
@@ -0,0 +1,124 @@
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+ {{ formatNumber(value) }}
+
+
+ {{ percentage > 0 ? '+' : '' }}{{ percentage }}%
+ {{ percentageLabel }}
+
+
+
+
+
+
+ {{ previousValue }}
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/cards/art-image-card/index.vue b/adminSystem/src/components/core/cards/art-image-card/index.vue
new file mode 100644
index 0000000..d27fe00
--- /dev/null
+++ b/adminSystem/src/components/core/cards/art-image-card/index.vue
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ props.readTime }} 阅读
+
+
+
+
+
+ {{ props.category }}
+
+
{{ props.title }}
+
+
+
+ {{ props.views }}
+
+
+
+ {{ props.comments }}
+
+ {{ props.date }}
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/cards/art-line-chart-card/index.vue b/adminSystem/src/components/core/cards/art-line-chart-card/index.vue
new file mode 100644
index 0000000..e58c9b2
--- /dev/null
+++ b/adminSystem/src/components/core/cards/art-line-chart-card/index.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+ {{ value }}
+
+
{{ label }}
+
+
+ {{ percentage > 0 ? '+' : '' }}{{ percentage }}%
+
+
+ {{ date }}
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/cards/art-progress-card/index.vue b/adminSystem/src/components/core/cards/art-progress-card/index.vue
new file mode 100644
index 0000000..048a836
--- /dev/null
+++ b/adminSystem/src/components/core/cards/art-progress-card/index.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/cards/art-stats-card/index.vue b/adminSystem/src/components/core/cards/art-stats-card/index.vue
new file mode 100644
index 0000000..8e0341b
--- /dev/null
+++ b/adminSystem/src/components/core/cards/art-stats-card/index.vue
@@ -0,0 +1,67 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/cards/art-timeline-list-card/index.vue b/adminSystem/src/components/core/cards/art-timeline-list-card/index.vue
new file mode 100644
index 0000000..fbb2c78
--- /dev/null
+++ b/adminSystem/src/components/core/cards/art-timeline-list-card/index.vue
@@ -0,0 +1,69 @@
+
+
+
+
+
{{ title }}
+
{{ subtitle }}
+
+
+
+
+
+
+ {{ item.content }}
+ #{{ item.code }}
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/charts/art-bar-chart/index.vue b/adminSystem/src/components/core/charts/art-bar-chart/index.vue
new file mode 100644
index 0000000..d677196
--- /dev/null
+++ b/adminSystem/src/components/core/charts/art-bar-chart/index.vue
@@ -0,0 +1,203 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/charts/art-dual-bar-compare-chart/index.vue b/adminSystem/src/components/core/charts/art-dual-bar-compare-chart/index.vue
new file mode 100644
index 0000000..32aa60f
--- /dev/null
+++ b/adminSystem/src/components/core/charts/art-dual-bar-compare-chart/index.vue
@@ -0,0 +1,195 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/charts/art-h-bar-chart/index.vue b/adminSystem/src/components/core/charts/art-h-bar-chart/index.vue
new file mode 100644
index 0000000..2e34759
--- /dev/null
+++ b/adminSystem/src/components/core/charts/art-h-bar-chart/index.vue
@@ -0,0 +1,208 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/charts/art-k-line-chart/index.vue b/adminSystem/src/components/core/charts/art-k-line-chart/index.vue
new file mode 100644
index 0000000..0061b51
--- /dev/null
+++ b/adminSystem/src/components/core/charts/art-k-line-chart/index.vue
@@ -0,0 +1,152 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/charts/art-line-chart/index.vue b/adminSystem/src/components/core/charts/art-line-chart/index.vue
new file mode 100644
index 0000000..b70c2c3
--- /dev/null
+++ b/adminSystem/src/components/core/charts/art-line-chart/index.vue
@@ -0,0 +1,371 @@
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/charts/art-radar-chart/index.vue b/adminSystem/src/components/core/charts/art-radar-chart/index.vue
new file mode 100644
index 0000000..e99fff6
--- /dev/null
+++ b/adminSystem/src/components/core/charts/art-radar-chart/index.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/charts/art-ring-chart/index.vue b/adminSystem/src/components/core/charts/art-ring-chart/index.vue
new file mode 100644
index 0000000..79115f7
--- /dev/null
+++ b/adminSystem/src/components/core/charts/art-ring-chart/index.vue
@@ -0,0 +1,133 @@
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/charts/art-scatter-chart/index.vue b/adminSystem/src/components/core/charts/art-scatter-chart/index.vue
new file mode 100644
index 0000000..995b56a
--- /dev/null
+++ b/adminSystem/src/components/core/charts/art-scatter-chart/index.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-button-more/index.vue b/adminSystem/src/components/core/forms/art-button-more/index.vue
new file mode 100644
index 0000000..858d305
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-button-more/index.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-button-table/index.vue b/adminSystem/src/components/core/forms/art-button-table/index.vue
new file mode 100644
index 0000000..5e2fd56
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-button-table/index.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-drag-verify/index.vue b/adminSystem/src/components/core/forms/art-drag-verify/index.vue
new file mode 100644
index 0000000..5306e04
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-drag-verify/index.vue
@@ -0,0 +1,430 @@
+
+
+
+
+
+
+
+
+
+
+ {{ message }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-excel-export/index.vue b/adminSystem/src/components/core/forms/art-excel-export/index.vue
new file mode 100644
index 0000000..08207c2
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-excel-export/index.vue
@@ -0,0 +1,389 @@
+
+
+
+
+
+
+
+ {{ loadingText }}
+
+ {{ buttonText }}
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-excel-import/index.vue b/adminSystem/src/components/core/forms/art-excel-import/index.vue
new file mode 100644
index 0000000..8aa82fe
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-excel-import/index.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+ 导入 Excel
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-form/index.vue b/adminSystem/src/components/core/forms/art-form/index.vue
new file mode 100644
index 0000000..1e76f14
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-form/index.vue
@@ -0,0 +1,311 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('table.form.reset') }}
+
+
+ {{ t('table.form.submit') }}
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-search-bar/index.vue b/adminSystem/src/components/core/forms/art-search-bar/index.vue
new file mode 100644
index 0000000..b25b5bb
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-search-bar/index.vue
@@ -0,0 +1,437 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-wang-editor/index.vue b/adminSystem/src/components/core/forms/art-wang-editor/index.vue
new file mode 100644
index 0000000..cfc457e
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-wang-editor/index.vue
@@ -0,0 +1,219 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/forms/art-wang-editor/style.scss b/adminSystem/src/components/core/forms/art-wang-editor/style.scss
new file mode 100644
index 0000000..fd5dbca
--- /dev/null
+++ b/adminSystem/src/components/core/forms/art-wang-editor/style.scss
@@ -0,0 +1,210 @@
+$box-radius: calc(var(--custom-radius) / 3 + 2px);
+
+// 全屏容器 z-index 调整
+.w-e-full-screen-container {
+ z-index: 100 !important;
+}
+
+/* 编辑器容器 */
+.editor-wrapper {
+ width: 100%;
+ height: 100%;
+ border: 1px solid var(--art-gray-300);
+ border-radius: $box-radius !important;
+
+ .w-e-bar {
+ border-radius: $box-radius $box-radius 0 0 !important;
+ }
+
+ .menu-item {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+
+ i {
+ margin-right: 5px;
+ }
+ }
+
+ /* 工具栏 */
+ .editor-toolbar {
+ border-bottom: 1px solid var(--default-border);
+ }
+
+ /* 下拉选择框配置 */
+ .w-e-select-list {
+ min-width: 140px;
+ padding: 5px 10px 10px;
+ border: none;
+ border-radius: $box-radius;
+ }
+
+ /* 下拉选择框元素配置 */
+ .w-e-select-list ul li {
+ margin-top: 5px;
+ font-size: 15px !important;
+ border-radius: $box-radius;
+ }
+
+ /* 下拉选择框 正文文字大小调整 */
+ .w-e-select-list ul li:last-of-type {
+ font-size: 16px !important;
+ }
+
+ /* 下拉选择框 hover 样式调整 */
+ .w-e-select-list ul li:hover {
+ background-color: var(--art-gray-200);
+ }
+
+ :root {
+ /* 激活颜色 */
+ --w-e-toolbar-active-bg-color: var(--art-gray-200);
+
+ /* toolbar 图标和文字颜色 */
+ --w-e-toolbar-color: #000;
+
+ /* 表格选中时候的边框颜色 */
+ --w-e-textarea-selected-border-color: #ddd;
+
+ /* 表格头背景颜色 */
+ --w-e-textarea-slight-bg-color: var(--art-gray-200);
+ }
+
+ /* 工具栏按钮样式 */
+ .w-e-bar-item svg {
+ fill: var(--art-gray-800);
+ }
+
+ .w-e-bar-item button {
+ color: var(--art-gray-800);
+ border-radius: $box-radius;
+ }
+
+ /* 工具栏 hover 按钮背景颜色 */
+ .w-e-bar-item button:hover {
+ background-color: var(--art-gray-200);
+ }
+
+ /* 工具栏分割线 */
+ .w-e-bar-divider {
+ height: 20px;
+ margin-top: 10px;
+ background-color: #ccc;
+ }
+
+ /* 工具栏菜单 */
+ .w-e-bar-item-group .w-e-bar-item-menus-container {
+ min-width: 120px;
+ padding: 10px 0;
+ border: none;
+ border-radius: $box-radius;
+
+ .w-e-bar-item {
+ button {
+ width: 100%;
+ margin: 0 5px;
+ }
+ }
+ }
+
+ /* 代码块 */
+ .w-e-text-container [data-slate-editor] pre > code {
+ padding: 0.6rem 1rem;
+ background-color: var(--art-gray-50);
+ border-radius: $box-radius;
+ }
+
+ /* 弹出框 */
+ .w-e-drop-panel {
+ border: 0;
+ border-radius: $box-radius;
+ }
+
+ a {
+ color: #318ef4;
+ }
+
+ .w-e-text-container {
+ strong,
+ b {
+ font-weight: 500;
+ }
+
+ i,
+ em {
+ font-style: italic;
+ }
+ }
+
+ /* 表格样式优化 */
+ .w-e-text-container [data-slate-editor] .table-container th {
+ border-right: none;
+ }
+
+ .w-e-text-container [data-slate-editor] .table-container th:last-of-type {
+ border-right: 1px solid #ccc !important;
+ }
+
+ /* 引用 */
+ .w-e-text-container [data-slate-editor] blockquote {
+ background-color: var(--art-gray-200);
+ border-left: 4px solid var(--art-gray-300);
+ }
+
+ /* 输入区域弹出 bar */
+ .w-e-hover-bar {
+ border-radius: $box-radius;
+ }
+
+ /* 超链接弹窗 */
+ .w-e-modal {
+ border: none;
+ border-radius: $box-radius;
+ }
+
+ /* 图片样式调整 */
+ .w-e-text-container [data-slate-editor] .w-e-selected-image-container {
+ overflow: inherit;
+
+ &:hover {
+ border: 0;
+ }
+
+ img {
+ border: 1px solid transparent;
+ transition: border 0.3s;
+
+ &:hover {
+ border: 1px solid #318ef4 !important;
+ }
+ }
+
+ .w-e-image-dragger {
+ width: 12px;
+ height: 12px;
+ background-color: #318ef4;
+ border: 2px solid #fff;
+ border-radius: $box-radius;
+ }
+
+ .left-top {
+ top: -6px;
+ left: -6px;
+ }
+
+ .right-top {
+ top: -6px;
+ right: -6px;
+ }
+
+ .left-bottom {
+ bottom: -6px;
+ left: -6px;
+ }
+
+ .right-bottom {
+ right: -6px;
+ bottom: -6px;
+ }
+ }
+}
diff --git a/adminSystem/src/components/core/layouts/art-breadcrumb/index.vue b/adminSystem/src/components/core/layouts/art-breadcrumb/index.vue
new file mode 100644
index 0000000..4b54859
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-breadcrumb/index.vue
@@ -0,0 +1,142 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-chat-window/index.vue b/adminSystem/src/components/core/layouts/art-chat-window/index.vue
new file mode 100644
index 0000000..f3d9471
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-chat-window/index.vue
@@ -0,0 +1,262 @@
+
+
+
+
+
+
+
Art Bot
+
+
+
{{ isOnline ? '在线' : '离线' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-fast-enter/index.vue b/adminSystem/src/components/core/layouts/art-fast-enter/index.vue
new file mode 100644
index 0000000..fdde222
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-fast-enter/index.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ application.name }}
+
{{ application.description }}
+
+
+
+
+
+
+
快速链接
+
+ -
+ {{ quickLink.name }}
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-fireworks-effect/index.vue b/adminSystem/src/components/core/layouts/art-fireworks-effect/index.vue
new file mode 100644
index 0000000..be85274
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-fireworks-effect/index.vue
@@ -0,0 +1,633 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-global-component/index.vue b/adminSystem/src/components/core/layouts/art-global-component/index.vue
new file mode 100644
index 0000000..6908f94
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-global-component/index.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-global-search/index.vue b/adminSystem/src/components/core/layouts/art-global-search/index.vue
new file mode 100644
index 0000000..a7d88df
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-global-search/index.vue
@@ -0,0 +1,426 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatMenuTitle(item.meta.title) }}
+
+
+
+
+
+
+
{{ $t('search.historyTitle') }}
+
+
+ {{ formatMenuTitle(item.meta.title) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-header-bar/index.vue b/adminSystem/src/components/core/layouts/art-header-bar/index.vue
new file mode 100644
index 0000000..4e3c8f9
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-header-bar/index.vue
@@ -0,0 +1,485 @@
+
+
+
+
+
+
+
+
+
{{ AppConfig.systemInfo.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ $t('topBar.search.title') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('topBar.guide.title')
+ }} {{ $t('topBar.guide.theme') }} 、 {{ $t('topBar.guide.menu') }} {{ $t('topBar.guide.description') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue b/adminSystem/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue
new file mode 100644
index 0000000..c8c5832
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-header-bar/widget/ArtUserMenu.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+

+
+ {{
+ userInfo.userName
+ }}
+ {{ userInfo.email }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue b/adminSystem/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue
new file mode 100644
index 0000000..edd1473
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue b/adminSystem/src/components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue
new file mode 100644
index 0000000..ff32c1e
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-menus/art-horizontal-menu/widget/HorizontalSubmenu.vue
@@ -0,0 +1,95 @@
+
+
+
+
+ {{ formatMenuTitle(item.meta.title) }}
+
+
+ {{ item.meta.showTextBadge }}
+
+
+
+
+
+
+
+
+
+ {{ formatMenuTitle(item.meta.title) }}
+
+
+ {{ item.meta.showTextBadge }}
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-menus/art-mixed-menu/index.vue b/adminSystem/src/components/core/layouts/art-menus/art-mixed-menu/index.vue
new file mode 100644
index 0000000..4e98246
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-menus/art-mixed-menu/index.vue
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue b/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue
new file mode 100644
index 0000000..39387dc
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue
@@ -0,0 +1,355 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss b/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss
new file mode 100644
index 0000000..b98011c
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/style.scss
@@ -0,0 +1,253 @@
+.layout-sidebar {
+ display: flex;
+ height: 100vh;
+ user-select: none;
+ scrollbar-width: none;
+ border-right: 1px solid var(--art-card-border);
+
+ &.no-border {
+ border-right: none !important;
+ }
+
+ // 自定义滚动条宽度
+ :deep(.el-scrollbar__bar.is-vertical) {
+ width: 4px;
+ }
+
+ :deep(.el-scrollbar__thumb) {
+ right: -2px;
+ background-color: #ccc;
+ border-radius: 2px;
+ }
+
+ .dual-menu-left {
+ position: relative;
+ width: 80px;
+ height: 100%;
+ border-right: 1px solid var(--art-card-border) !important;
+ transition: width 0.25s;
+
+ .logo {
+ margin: auto;
+ margin-top: 12px;
+ margin-bottom: 3px;
+ cursor: pointer;
+ }
+
+ ul {
+ li {
+ > div {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ margin: 8px;
+ overflow: hidden;
+ text-align: center;
+ cursor: pointer;
+ border-radius: 5px;
+
+ .art-svg-icon {
+ display: block;
+ margin: 0 auto;
+ font-size: 20px;
+ }
+
+ span {
+ display: -webkit-box;
+ width: 100%;
+ overflow: hidden;
+ font-size: 12px;
+ text-overflow: ellipsis;
+ -webkit-line-clamp: 1;
+ line-clamp: 1;
+ -webkit-box-orient: vertical;
+ }
+
+ &.is-active {
+ background: var(--el-color-primary-light-9);
+
+ .art-svg-icon,
+ span {
+ color: var(--theme-color) !important;
+ }
+ }
+ }
+ }
+ }
+
+ .switch-btn {
+ position: absolute;
+ right: 0;
+ bottom: 15px;
+ left: 0;
+ margin: auto;
+ }
+ }
+
+ .menu-left {
+ position: relative;
+ box-sizing: border-box;
+ height: 100vh;
+
+ @media only screen and (width <= 640px) {
+ height: 100dvh;
+ }
+
+ .el-menu {
+ height: 100%;
+ }
+
+ &:hover {
+ .dual-menu-collapse-btn {
+ opacity: 1 !important;
+ }
+ }
+
+ .dual-menu-collapse-btn {
+ position: absolute;
+ top: 50%;
+ right: -11px;
+ z-index: 10;
+ width: 11px;
+ height: 50px;
+ cursor: pointer;
+ background-color: var(--default-box-color);
+ border: 1px solid var(--art-card-border);
+ border-radius: 0 15px 15px 0;
+ opacity: 0;
+ transition: opacity 0.2s;
+ transform: translateY(-50%);
+
+ &:hover {
+ .art-svg-icon {
+ color: var(--art-gray-800) !important;
+ }
+ }
+
+ .art-svg-icon {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: -4px;
+ margin: auto;
+ transition: all 0.3s;
+ }
+ }
+ }
+
+ .header {
+ position: relative;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ width: 100%;
+ height: 60px;
+ overflow: hidden;
+ line-height: 60px;
+ cursor: pointer;
+
+ .logo {
+ margin-left: 22px;
+ }
+
+ p {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 58px;
+ box-sizing: border-box;
+ margin-left: 10px;
+ font-size: 18px;
+
+ &.is-dual-menu-name {
+ left: 25px;
+ margin: auto;
+ }
+ }
+ }
+
+ .el-menu {
+ box-sizing: border-box;
+ height: calc(100vh - 60px);
+ overflow-y: auto;
+ // 防止菜单内的滚动影响整个页面滚动
+ overscroll-behavior: contain;
+ border-right: 0;
+ scrollbar-width: none;
+ -ms-scroll-chaining: contain;
+
+ &::-webkit-scrollbar {
+ width: 0 !important;
+ }
+ }
+
+ .menu-model {
+ display: none;
+ }
+}
+
+@media only screen and (width <= 800px) {
+ .layout-sidebar {
+ width: 0;
+
+ .header {
+ height: 50px;
+ line-height: 50px;
+ }
+
+ .el-menu {
+ height: calc(100vh - 60px);
+ }
+
+ .el-menu--collapse {
+ width: 0;
+ }
+
+ // 折叠状态下的header样式
+ .menu-left-close .header {
+ .logo {
+ display: none;
+ }
+
+ p {
+ left: 16px;
+ font-size: 0;
+ opacity: 0 !important;
+ }
+ }
+
+ .menu-model {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: -1;
+ display: block;
+ width: 100%;
+ height: 100vh;
+ background: rgba($color: #000, $alpha: 50%);
+ transition: opacity 0.2s ease-in-out;
+ }
+ }
+}
+
+@media only screen and (width <= 640px) {
+ .layout-sidebar {
+ border-right: 0 !important;
+ }
+}
+
+.dark {
+ .layout-sidebar {
+ border-right: 1px solid rgb(255 255 255 / 13%);
+
+ :deep(.el-scrollbar__thumb) {
+ background-color: #777;
+ }
+
+ .dual-menu-left {
+ border-right: 1px solid rgb(255 255 255 / 9%) !important;
+ }
+ }
+}
diff --git a/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/theme.scss b/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/theme.scss
new file mode 100644
index 0000000..7626c42
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/theme.scss
@@ -0,0 +1,258 @@
+@use '@styles/core/mixin.scss' as *;
+
+// 菜单样式变量
+$menu-height: 42px;
+$menu-icon-size: 20px;
+$menu-font-size: 14px;
+$hover-bg-color: var(--art-gray-200);
+$popup-menu-height: 40px;
+$popup-menu-padding: 8px;
+$popup-menu-margin: 5px;
+$popup-menu-radius: 6px;
+
+// 通用菜单项样式
+@mixin menu-item-base {
+ width: calc(100% - 16px);
+ margin-left: 8px;
+ border-radius: 6px;
+
+ .menu-icon {
+ margin-left: -7px;
+ }
+}
+
+// 通用 hover 样式
+@mixin menu-hover($bg-color) {
+ .el-sub-menu__title:hover,
+ .el-menu-item:not(.is-active):hover {
+ background: $bg-color !important;
+ }
+}
+
+// 通用选中样式
+@mixin menu-active($color, $bg-color, $icon-color: var(--theme-color)) {
+ .el-menu-item.is-active {
+ color: $color !important;
+ background-color: $bg-color;
+
+ .menu-icon {
+ .art-svg-icon {
+ color: $icon-color !important;
+ }
+ }
+ }
+}
+
+// 弹窗菜单项样式
+@mixin popup-menu-item {
+ height: $popup-menu-height;
+ margin-bottom: $popup-menu-margin;
+ border-radius: $popup-menu-radius;
+
+ .menu-icon {
+ margin-right: 5px;
+ }
+
+ &:last-of-type {
+ margin-bottom: 0;
+ }
+}
+
+// 主题菜单通用样式(合并 design 和 dark 主题的共同逻辑)
+@mixin theme-menu-base {
+ .el-sub-menu__title,
+ .el-menu-item {
+ @include menu-item-base;
+ }
+}
+
+// 弹窗菜单通用样式
+@mixin popup-menu-base($hover-bg, $active-color, $active-bg) {
+ .el-menu--popup {
+ padding: $popup-menu-padding;
+
+ .el-sub-menu__title:hover,
+ .el-menu-item:hover {
+ background-color: $hover-bg !important;
+ border-radius: $popup-menu-radius;
+ }
+
+ .el-menu-item {
+ @include popup-menu-item;
+
+ &.is-active {
+ color: $active-color !important;
+ background-color: $active-bg !important;
+ }
+ }
+
+ .el-sub-menu {
+ @include popup-menu-item;
+
+ height: $popup-menu-height !important;
+
+ .el-sub-menu__title {
+ height: $popup-menu-height !important;
+ border-radius: $popup-menu-radius;
+ }
+ }
+ }
+}
+
+.layout-sidebar {
+ // ---------------------- Modify default style ----------------------
+
+ // 菜单折叠样式
+ .menu-left-close {
+ .header {
+ .logo {
+ margin: 0 auto;
+ }
+ }
+ }
+
+ // 菜单图标
+ .menu-icon {
+ margin-right: 8px;
+ font-size: $menu-icon-size;
+ }
+
+ // 菜单高度
+ .el-sub-menu__title,
+ .el-menu-item {
+ height: $menu-height !important;
+ margin-bottom: 4px;
+ line-height: $menu-height !important;
+
+ span {
+ font-size: $menu-font-size !important;
+
+ @include ellipsis();
+ }
+ }
+
+ // 右侧箭头
+ .el-sub-menu__icon-arrow {
+ width: 13px !important;
+ font-size: 13px !important;
+ }
+
+ // 菜单折叠
+ .el-menu--collapse {
+ .el-sub-menu.is-active {
+ .el-sub-menu__title {
+ .menu-icon {
+ .art-svg-icon {
+ // 选中菜单图标颜色
+ color: var(--theme-color) !important;
+ }
+ }
+ }
+ }
+ }
+
+ // ---------------------- Design theme menu ----------------------
+ .el-menu-design {
+ @include theme-menu-base;
+ @include menu-active(var(--theme-color), var(--el-color-primary-light-9));
+ @include menu-hover($hover-bg-color);
+
+ .el-sub-menu__icon-arrow {
+ color: var(--art-gray-600);
+ }
+ }
+
+ // ---------------------- Dark theme menu ----------------------
+ .el-menu-dark {
+ @include theme-menu-base;
+ @include menu-active(#fff, #27282d, #fff);
+ @include menu-hover(#0f1015);
+
+ .el-sub-menu__icon-arrow {
+ color: var(--art-gray-400);
+ }
+ }
+
+ // ---------------------- Light theme menu ----------------------
+ .el-menu-light {
+ .el-sub-menu__title,
+ .el-menu-item {
+ .menu-icon {
+ margin-left: 1px;
+ }
+ }
+
+ .el-menu-item.is-active {
+ background-color: var(--el-color-primary-light-9);
+
+ .art-svg-icon {
+ color: var(--theme-color) !important;
+ }
+
+ &::before {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 4px;
+ height: 100%;
+ content: '';
+ background: var(--theme-color);
+ }
+ }
+
+ @include menu-hover($hover-bg-color);
+
+ .el-sub-menu__icon-arrow {
+ color: var(--art-gray-600);
+ }
+ }
+}
+
+@media only screen and (width <= 640px) {
+ .layout-sidebar {
+ .el-menu-design {
+ > .el-sub-menu {
+ margin-left: 0;
+ }
+
+ .el-sub-menu {
+ width: 100% !important;
+ }
+ }
+ }
+}
+
+// 菜单折叠 hover 弹窗样式(浅色主题)
+.el-menu--vertical,
+.el-menu--popup-container {
+ @include popup-menu-base(var(--art-gray-200), var(--art-gray-900), var(--art-gray-200));
+}
+
+// 暗黑模式菜单样式
+.dark {
+ .el-menu--vertical,
+ .el-menu--popup-container {
+ @include popup-menu-base(var(--art-gray-200), var(--art-gray-900), #292a2e);
+ }
+
+ .layout-sidebar {
+ // 图标颜色、文字颜色
+ .menu-icon .art-svg-icon,
+ .menu-name {
+ color: var(--art-gray-800) !important;
+ }
+
+ // 选中的文字颜色跟图标颜色
+ .el-menu-item.is-active {
+ span,
+ .menu-icon .art-svg-icon {
+ color: var(--theme-color) !important;
+ }
+ }
+
+ // 右侧箭头颜色
+ .el-sub-menu__icon-arrow {
+ color: #fff;
+ }
+ }
+}
diff --git a/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue b/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue
new file mode 100644
index 0000000..a7ac6a9
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-menus/art-sidebar-menu/widget/SidebarSubmenu.vue
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.meta.showTextBadge }}
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-notification/index.vue b/adminSystem/src/components/core/layouts/art-notification/index.vue
new file mode 100644
index 0000000..a58853c
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-notification/index.vue
@@ -0,0 +1,456 @@
+
+
+
+
+ {{ $t('notice.title') }}
+
+ {{ $t('notice.btnRead') }}
+
+
+
+
+ -
+ {{ item.name }} ({{ item.num }})
+
+
+
+
+
+
+
+
+ {{ $t('notice.viewAll') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-page-content/index.vue b/adminSystem/src/components/core/layouts/art-page-content/index.vue
new file mode 100644
index 0000000..a862df1
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-page-content/index.vue
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-screen-lock/index.vue b/adminSystem/src/components/core/layouts/art-screen-lock/index.vue
new file mode 100644
index 0000000..f00199b
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-screen-lock/index.vue
@@ -0,0 +1,517 @@
+
+
+
+
+
+
+
🔒
+
系统已锁定
+
+ 检测到开发者工具已打开
+ 为了系统安全,请关闭开发者工具后继续使用
+
+
Security Lock Activated
+
+
+
+
+
+
+
+

+
{{ userInfo.userName }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('lockScreen.lock.btnText') }}
+
+
+
+
+
+
+
+
+
+

+
+ {{ userInfo.userName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('lockScreen.unlock.btnText') }}
+
+
+
+ {{ $t('lockScreen.unlock.backBtnText') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsConfig.ts b/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsConfig.ts
new file mode 100644
index 0000000..35e8066
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsConfig.ts
@@ -0,0 +1,248 @@
+import { computed } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { ContainerWidthEnum } from '@/enums/appEnum'
+import AppConfig from '@/config'
+import { headerBarConfig } from '@/config/modules/headerBar'
+
+/**
+ * 设置项配置选项管理
+ */
+export function useSettingsConfig() {
+ const { t } = useI18n()
+
+ // 标签页风格选项
+ const tabStyleOptions = computed(() => [
+ {
+ value: 'tab-default',
+ label: t('setting.tabStyle.default')
+ },
+ {
+ value: 'tab-card',
+ label: t('setting.tabStyle.card')
+ },
+ {
+ value: 'tab-google',
+ label: t('setting.tabStyle.google')
+ }
+ ])
+
+ // 页面切换动画选项
+ const pageTransitionOptions = computed(() => [
+ {
+ value: '',
+ label: t('setting.transition.list.none')
+ },
+ {
+ value: 'fade',
+ label: t('setting.transition.list.fade')
+ },
+ {
+ value: 'slide-left',
+ label: t('setting.transition.list.slideLeft')
+ },
+ {
+ value: 'slide-bottom',
+ label: t('setting.transition.list.slideBottom')
+ },
+ {
+ value: 'slide-top',
+ label: t('setting.transition.list.slideTop')
+ }
+ ])
+
+ // 圆角大小选项
+ const customRadiusOptions = [
+ { value: '0', label: '0' },
+ { value: '0.25', label: '0.25' },
+ { value: '0.5', label: '0.5' },
+ { value: '0.75', label: '0.75' },
+ { value: '1', label: '1' }
+ ]
+
+ // 容器宽度选项
+ const containerWidthOptions = computed(() => [
+ {
+ value: ContainerWidthEnum.FULL,
+ label: t('setting.container.list[0]'),
+ icon: 'icon-park-outline:auto-width'
+ },
+ {
+ value: ContainerWidthEnum.BOXED,
+ label: t('setting.container.list[1]'),
+ icon: 'ix:width'
+ }
+ ])
+
+ // 盒子样式选项
+ const boxStyleOptions = computed(() => [
+ {
+ value: 'border-mode',
+ label: t('setting.box.list[0]'),
+ type: 'border-mode' as const
+ },
+ {
+ value: 'shadow-mode',
+ label: t('setting.box.list[1]'),
+ type: 'shadow-mode' as const
+ }
+ ])
+
+ // 从配置文件获取的选项
+ const configOptions = {
+ // 主题色彩选项
+ mainColors: AppConfig.systemMainColor,
+
+ // 主题风格选项
+ themeList: AppConfig.settingThemeList,
+
+ // 菜单布局选项
+ menuLayoutList: AppConfig.menuLayoutList
+ }
+
+ // 基础设置项配置
+ const basicSettingsConfig = computed(() => {
+ // 定义所有基础设置项
+ const allSettings = [
+ {
+ key: 'showWorkTab',
+ label: t('setting.basics.list.multiTab'),
+ type: 'switch' as const,
+ handler: 'workTab',
+ headerBarKey: null // 不依赖headerBar配置
+ },
+ {
+ key: 'uniqueOpened',
+ label: t('setting.basics.list.accordion'),
+ type: 'switch' as const,
+ handler: 'uniqueOpened',
+ headerBarKey: null // 不依赖headerBar配置
+ },
+ {
+ key: 'showMenuButton',
+ label: t('setting.basics.list.collapseSidebar'),
+ type: 'switch' as const,
+ handler: 'menuButton',
+ headerBarKey: 'menuButton' as const
+ },
+ {
+ key: 'showFastEnter',
+ label: t('setting.basics.list.fastEnter'),
+ type: 'switch' as const,
+ handler: 'fastEnter',
+ headerBarKey: 'fastEnter' as const
+ },
+ {
+ key: 'showRefreshButton',
+ label: t('setting.basics.list.reloadPage'),
+ type: 'switch' as const,
+ handler: 'refreshButton',
+ headerBarKey: 'refreshButton' as const
+ },
+ {
+ key: 'showCrumbs',
+ label: t('setting.basics.list.breadcrumb'),
+ type: 'switch' as const,
+ handler: 'crumbs',
+ mobileHide: true,
+ headerBarKey: 'breadcrumb' as const
+ },
+ {
+ key: 'showLanguage',
+ label: t('setting.basics.list.language'),
+ type: 'switch' as const,
+ handler: 'language',
+ headerBarKey: 'language' as const
+ },
+ {
+ key: 'showNprogress',
+ label: t('setting.basics.list.progressBar'),
+ type: 'switch' as const,
+ handler: 'nprogress',
+ headerBarKey: null // 不依赖headerBar配置
+ },
+ {
+ key: 'colorWeak',
+ label: t('setting.basics.list.weakMode'),
+ type: 'switch' as const,
+ handler: 'colorWeak',
+ headerBarKey: null // 不依赖headerBar配置
+ },
+ {
+ key: 'watermarkVisible',
+ label: t('setting.basics.list.watermark'),
+ type: 'switch' as const,
+ handler: 'watermark',
+ headerBarKey: null // 不依赖headerBar配置
+ },
+ {
+ key: 'menuOpenWidth',
+ label: t('setting.basics.list.menuWidth'),
+ type: 'input-number' as const,
+ handler: 'menuOpenWidth',
+ min: 180,
+ max: 320,
+ step: 10,
+ style: { width: '120px' },
+ controlsPosition: 'right' as const,
+ headerBarKey: null // 不依赖headerBar配置
+ },
+ {
+ key: 'tabStyle',
+ label: t('setting.basics.list.tabStyle'),
+ type: 'select' as const,
+ handler: 'tabStyle',
+ options: tabStyleOptions.value,
+ style: { width: '120px' },
+ headerBarKey: null // 不依赖headerBar配置
+ },
+ {
+ key: 'pageTransition',
+ label: t('setting.basics.list.pageTransition'),
+ type: 'select' as const,
+ handler: 'pageTransition',
+ options: pageTransitionOptions.value,
+ style: { width: '120px' },
+ headerBarKey: null // 不依赖headerBar配置
+ },
+ {
+ key: 'customRadius',
+ label: t('setting.basics.list.borderRadius'),
+ type: 'select' as const,
+ handler: 'customRadius',
+ options: customRadiusOptions,
+ style: { width: '120px' },
+ headerBarKey: null // 不依赖headerBar配置
+ }
+ ]
+
+ // 根据 headerBarConfig 过滤设置项
+ return (
+ allSettings
+ .filter((setting) => {
+ // 如果设置项不依赖headerBar配置,则始终显示
+ if (setting.headerBarKey === null) {
+ return true
+ }
+
+ // 如果依赖headerBar配置,检查对应的功能是否启用
+ const headerBarFeature = headerBarConfig[setting.headerBarKey]
+ return headerBarFeature?.enabled !== false
+ })
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ .map(({ headerBarKey: _headerBarKey, ...setting }) => setting)
+ )
+ })
+
+ return {
+ // 选项配置
+ tabStyleOptions,
+ pageTransitionOptions,
+ customRadiusOptions,
+ containerWidthOptions,
+ boxStyleOptions,
+ configOptions,
+
+ // 设置项配置
+ basicSettingsConfig
+ }
+}
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsHandlers.ts b/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsHandlers.ts
new file mode 100644
index 0000000..392c690
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsHandlers.ts
@@ -0,0 +1,167 @@
+import { useSettingStore } from '@/store/modules/setting'
+import { storeToRefs } from 'pinia'
+import type { ContainerWidthEnum } from '@/enums/appEnum'
+
+/**
+ * 设置项通用处理逻辑
+ */
+export function useSettingsHandlers() {
+ const settingStore = useSettingStore()
+
+ // DOM 操作相关
+ const domOperations = {
+ // 设置HTML类名
+ setHtmlClass: (className: string, add: boolean) => {
+ const el = document.getElementsByTagName('html')[0]
+ if (add) {
+ el.classList.add(className)
+ } else {
+ el.classList.remove(className)
+ }
+ },
+
+ // 设置根元素属性
+ setRootAttribute: (attribute: string, value: string) => {
+ const el = document.documentElement
+ el.setAttribute(attribute, value)
+ },
+
+ // 设置body类名
+ setBodyClass: (className: string, add: boolean) => {
+ const el = document.getElementsByTagName('body')[0]
+ if (add) {
+ el.classList.add(className)
+ } else {
+ el.classList.remove(className)
+ }
+ }
+ }
+
+ // 通用切换处理器
+ const createToggleHandler = (storeMethod: () => void, callback?: () => void) => {
+ return () => {
+ storeMethod()
+ callback?.()
+ }
+ }
+
+ // 通用值变更处理器
+ const createValueHandler = (
+ storeMethod: (value: T) => void,
+ callback?: (value: T) => void
+ ) => {
+ return (value: T) => {
+ if (value !== undefined && value !== null) {
+ storeMethod(value)
+ callback?.(value)
+ }
+ }
+ }
+
+ // 基础设置处理器
+ const basicHandlers = {
+ // 工作台标签页
+ workTab: createToggleHandler(() => settingStore.setWorkTab(!settingStore.showWorkTab)),
+
+ // 菜单手风琴
+ uniqueOpened: createToggleHandler(() => settingStore.setUniqueOpened()),
+
+ // 显示菜单按钮
+ menuButton: createToggleHandler(() => settingStore.setButton()),
+
+ // 显示快速入口
+ fastEnter: createToggleHandler(() => settingStore.setFastEnter()),
+
+ // 显示刷新按钮
+ refreshButton: createToggleHandler(() => settingStore.setShowRefreshButton()),
+
+ // 显示面包屑
+ crumbs: createToggleHandler(() => settingStore.setCrumbs()),
+
+ // 显示语言切换
+ language: createToggleHandler(() => settingStore.setLanguage()),
+
+ // 显示进度条
+ nprogress: createToggleHandler(() => settingStore.setNprogress()),
+
+ // 色弱模式
+ colorWeak: createToggleHandler(
+ () => settingStore.setColorWeak(),
+ () => {
+ domOperations.setHtmlClass('color-weak', settingStore.colorWeak)
+ }
+ ),
+
+ // 水印显示
+ watermark: createToggleHandler(() =>
+ settingStore.setWatermarkVisible(!settingStore.watermarkVisible)
+ ),
+
+ // 菜单展开宽度
+ menuOpenWidth: createValueHandler((width: number) =>
+ settingStore.setMenuOpenWidth(width)
+ ),
+
+ // 标签页风格
+ tabStyle: createValueHandler((style: string) => settingStore.setTabStyle(style)),
+
+ // 页面切换动画
+ pageTransition: createValueHandler((transition: string) =>
+ settingStore.setPageTransition(transition)
+ ),
+
+ // 圆角大小
+ customRadius: createValueHandler((radius: string) =>
+ settingStore.setCustomRadius(radius)
+ )
+ }
+
+ // 盒子样式处理器
+ const boxStyleHandlers = {
+ // 设置盒子模式
+ setBoxMode: (type: 'border-mode' | 'shadow-mode') => {
+ const { boxBorderMode } = storeToRefs(settingStore)
+
+ // 防止重复设置
+ if (
+ (type === 'shadow-mode' && boxBorderMode.value === false) ||
+ (type === 'border-mode' && boxBorderMode.value === true)
+ ) {
+ return
+ }
+
+ setTimeout(() => {
+ domOperations.setRootAttribute('data-box-mode', type)
+ settingStore.setBorderMode()
+ }, 50)
+ }
+ }
+
+ // 颜色设置处理器
+ const colorHandlers = {
+ // 选择主题色
+ selectColor: (theme: string) => {
+ settingStore.setElementTheme(theme)
+ settingStore.reload()
+ }
+ }
+
+ // 容器设置处理器
+ const containerHandlers = {
+ // 设置容器宽度
+ setWidth: (type: ContainerWidthEnum) => {
+ settingStore.setContainerWidth(type)
+ settingStore.reload()
+ }
+ }
+
+ return {
+ domOperations,
+ basicHandlers,
+ boxStyleHandlers,
+ colorHandlers,
+ containerHandlers,
+ createToggleHandler,
+ createValueHandler
+ }
+}
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsPanel.ts b/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsPanel.ts
new file mode 100644
index 0000000..66877a8
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsPanel.ts
@@ -0,0 +1,192 @@
+import { ref, computed, watch } from 'vue'
+import { useSettingStore } from '@/store/modules/setting'
+import { storeToRefs } from 'pinia'
+import { useBreakpoints } from '@vueuse/core'
+import AppConfig from '@/config'
+import { SystemThemeEnum, MenuTypeEnum } from '@/enums/appEnum'
+import { mittBus } from '@/utils/sys'
+import { useTheme } from '@/hooks/core/useTheme'
+import { useCeremony } from '@/hooks/core/useCeremony'
+import { useSettingsState } from './useSettingsState'
+import { useSettingsHandlers } from './useSettingsHandlers'
+
+/**
+ * 设置面板核心逻辑管理
+ */
+export function useSettingsPanel() {
+ const settingStore = useSettingStore()
+ const { systemThemeType, systemThemeMode, menuType } = storeToRefs(settingStore)
+
+ // Composables
+ const { openFestival, cleanup } = useCeremony()
+ const { setSystemTheme, setSystemAutoTheme } = useTheme()
+ const { initColorWeak } = useSettingsState()
+ const { domOperations } = useSettingsHandlers()
+
+ // 响应式状态
+ const showDrawer = ref(false)
+
+ // 使用 VueUse breakpoints 优化性能
+ const breakpoints = useBreakpoints({ tablet: 1000 })
+ const isMobile = breakpoints.smaller('tablet')
+
+ // 记录窗口宽度变化前的菜单类型
+ const beforeMenuType = ref()
+ const hasChangedMenu = ref(false)
+
+ // 计算属性
+ const systemThemeColor = computed(() => settingStore.systemThemeColor as string)
+
+ // 主题相关处理
+ const useThemeHandlers = () => {
+ // 初始化系统颜色
+ const initSystemColor = () => {
+ if (!AppConfig.systemMainColor.includes(systemThemeColor.value)) {
+ settingStore.setElementTheme(AppConfig.systemMainColor[0])
+ settingStore.reload()
+ }
+ }
+
+ // 初始化系统主题
+ const initSystemTheme = () => {
+ if (systemThemeMode.value === SystemThemeEnum.AUTO) {
+ setSystemAutoTheme()
+ } else {
+ setSystemTheme(systemThemeType.value)
+ }
+ }
+
+ // 监听系统主题变化
+ const listenerSystemTheme = () => {
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
+ mediaQuery.addEventListener('change', initSystemTheme)
+ return () => {
+ mediaQuery.removeEventListener('change', initSystemTheme)
+ }
+ }
+
+ return {
+ initSystemColor,
+ initSystemTheme,
+ listenerSystemTheme
+ }
+ }
+
+ // 响应式布局处理
+ const useResponsiveLayout = () => {
+ // 使用 watch 监听断点变化,性能更优
+ const stopWatch = watch(
+ isMobile,
+ (mobile: boolean) => {
+ if (mobile) {
+ // 切换到移动端布局
+ if (!hasChangedMenu.value) {
+ beforeMenuType.value = menuType.value
+ useSettingsState().switchMenuLayouts(MenuTypeEnum.LEFT)
+ settingStore.setMenuOpen(false)
+ hasChangedMenu.value = true
+ }
+ } else {
+ // 恢复桌面端布局
+ if (hasChangedMenu.value && beforeMenuType.value) {
+ useSettingsState().switchMenuLayouts(beforeMenuType.value)
+ settingStore.setMenuOpen(true)
+ hasChangedMenu.value = false
+ }
+ }
+ },
+ { immediate: true }
+ )
+
+ return { stopWatch }
+ }
+
+ // 抽屉控制
+ const useDrawerControl = () => {
+ // 打开抽屉
+ const handleOpen = () => {
+ setTimeout(() => {
+ domOperations.setBodyClass('theme-change', true)
+ }, 500)
+ }
+
+ // 关闭抽屉
+ const handleClose = () => {
+ domOperations.setBodyClass('theme-change', false)
+ }
+
+ // 打开设置
+ const openSetting = () => {
+ showDrawer.value = true
+ }
+
+ // 关闭设置
+ const closeDrawer = () => {
+ showDrawer.value = false
+ }
+
+ return {
+ handleOpen,
+ handleClose,
+ openSetting,
+ closeDrawer
+ }
+ }
+
+ // Props 变化监听
+ const usePropsWatcher = (props: { open?: boolean }) => {
+ watch(
+ () => props.open,
+ (val: boolean | undefined) => {
+ if (val !== undefined) {
+ showDrawer.value = val
+ }
+ }
+ )
+ }
+
+ // 初始化设置
+ const useSettingsInitializer = () => {
+ const themeHandlers = useThemeHandlers()
+ const { openSetting } = useDrawerControl()
+ const { stopWatch } = useResponsiveLayout()
+ let themeCleanup: (() => void) | null = null
+
+ const initializeSettings = () => {
+ mittBus.on('openSetting', openSetting)
+ themeHandlers.initSystemColor()
+ themeCleanup = themeHandlers.listenerSystemTheme()
+ initColorWeak()
+
+ // 设置盒子模式
+ const boxMode = settingStore.boxBorderMode ? 'border-mode' : 'shadow-mode'
+ domOperations.setRootAttribute('data-box-mode', boxMode)
+
+ themeHandlers.initSystemTheme()
+ openFestival()
+ }
+
+ const cleanupSettings = () => {
+ stopWatch()
+ themeCleanup?.()
+ cleanup()
+ }
+
+ return {
+ initializeSettings,
+ cleanupSettings
+ }
+ }
+
+ return {
+ // 状态
+ showDrawer,
+
+ // 方法组合
+ useThemeHandlers,
+ useResponsiveLayout,
+ useDrawerControl,
+ usePropsWatcher,
+ useSettingsInitializer
+ }
+}
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsState.ts b/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsState.ts
new file mode 100644
index 0000000..65352d2
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/composables/useSettingsState.ts
@@ -0,0 +1,37 @@
+import { useSettingStore } from '@/store/modules/setting'
+import { MenuThemeEnum, MenuTypeEnum } from '@/enums/appEnum'
+
+/**
+ * 设置状态管理
+ */
+export function useSettingsState() {
+ const settingStore = useSettingStore()
+
+ // 色弱模式初始化
+ const initColorWeak = () => {
+ if (settingStore.colorWeak) {
+ const el = document.getElementsByTagName('html')[0]
+ setTimeout(() => {
+ el.classList.add('color-weak')
+ }, 100)
+ }
+ }
+
+ // 菜单布局切换
+ const switchMenuLayouts = (type: MenuTypeEnum) => {
+ if (type === MenuTypeEnum.LEFT || type === MenuTypeEnum.TOP_LEFT) {
+ settingStore.setMenuOpen(true)
+ }
+ settingStore.switchMenuLayouts(type)
+ if (type === MenuTypeEnum.DUAL_MENU) {
+ settingStore.switchMenuStyles(MenuThemeEnum.DESIGN)
+ settingStore.setMenuOpen(true)
+ }
+ }
+
+ return {
+ // 方法
+ initColorWeak,
+ switchMenuLayouts
+ }
+}
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/index.vue b/adminSystem/src/components/core/layouts/art-settings-panel/index.vue
new file mode 100644
index 0000000..0cbf344
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/index.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/style.scss b/adminSystem/src/components/core/layouts/art-settings-panel/style.scss
new file mode 100644
index 0000000..e863074
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/style.scss
@@ -0,0 +1,92 @@
+@use '@styles/core/mixin.scss' as *;
+
+// 设置抽屉模态框样式
+.setting-modal {
+ background: transparent !important;
+
+ .el-drawer {
+ // 背景滤镜效果
+ background: rgba($color: #fff, $alpha: 50%) !important;
+ box-shadow: 0 0 30px rgb(0 0 0 / 10%) !important;
+
+ @include backdropBlur();
+
+ .setting-box-wrap {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ width: calc(100% + 15px);
+ margin-bottom: 10px;
+
+ .setting-item {
+ box-sizing: border-box;
+ width: calc(33.333% - 15px);
+ margin-right: 15px;
+ text-align: center;
+
+ .box {
+ position: relative;
+ box-sizing: border-box;
+ display: flex;
+ height: 52px;
+ overflow: hidden;
+ cursor: pointer;
+ border: 2px solid var(--default-border);
+ border-radius: 8px;
+ box-shadow: 0 0 8px 0 rgb(0 0 0 / 10%);
+ transition: box-shadow 0.1s;
+
+ &.mt-16 {
+ margin-top: 16px;
+ }
+
+ &.is-active {
+ border: 2px solid var(--theme-color);
+ }
+
+ img {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ .name {
+ margin-top: 6px;
+ font-size: 14px;
+ text-align: center;
+ }
+ }
+ }
+ }
+
+ // 去除滚动条
+ .el-drawer__body::-webkit-scrollbar {
+ width: 0 !important;
+ }
+}
+
+.dark {
+ .setting-modal {
+ .el-drawer {
+ background: rgba($color: #000, $alpha: 50%) !important;
+
+ .setting-item {
+ .box {
+ border: 2px solid transparent;
+ }
+ }
+ }
+ }
+}
+
+// 去除火狐浏览器滚动条
+:deep(.el-drawer__body) {
+ scrollbar-width: none;
+}
+
+// 移动端隐藏
+@media screen and (width <= 800px) {
+ .mobile-hide {
+ display: none !important;
+ }
+}
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/BasicSettings.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/BasicSettings.vue
new file mode 100644
index 0000000..b6dc9d3
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/BasicSettings.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/BoxStyleSettings.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/BoxStyleSettings.vue
new file mode 100644
index 0000000..86c7a9e
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/BoxStyleSettings.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+ {{ option.label }}
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/ColorSettings.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/ColorSettings.vue
new file mode 100644
index 0000000..05a4b41
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/ColorSettings.vue
@@ -0,0 +1,35 @@
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/ContainerSettings.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/ContainerSettings.vue
new file mode 100644
index 0000000..1f5be72
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/ContainerSettings.vue
@@ -0,0 +1,33 @@
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/MenuLayoutSettings.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/MenuLayoutSettings.vue
new file mode 100644
index 0000000..dbcae46
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/MenuLayoutSettings.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
![]()
+
+
{{ $t(`setting.menuType.list[${index}]`) }}
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/MenuStyleSettings.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/MenuStyleSettings.vue
new file mode 100644
index 0000000..61237eb
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/MenuStyleSettings.vue
@@ -0,0 +1,44 @@
+
+
+
+
+
+
![]()
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/SectionTitle.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SectionTitle.vue
new file mode 100644
index 0000000..31ef00c
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SectionTitle.vue
@@ -0,0 +1,17 @@
+
+
+ {{ title }}
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingActions.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingActions.vue
new file mode 100644
index 0000000..7b47d1a
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingActions.vue
@@ -0,0 +1,235 @@
+
+
+
+
+ {{ $t('setting.actions.copyConfig') }}
+
+
+ {{ $t('setting.actions.resetConfig') }}
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingDrawer.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingDrawer.vue
new file mode 100644
index 0000000..85372be
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingDrawer.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingHeader.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingHeader.vue
new file mode 100644
index 0000000..e3ead9e
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingHeader.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingItem.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingItem.vue
new file mode 100644
index 0000000..5721027
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/SettingItem.vue
@@ -0,0 +1,101 @@
+
+
+ {{ config.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-settings-panel/widget/ThemeSettings.vue b/adminSystem/src/components/core/layouts/art-settings-panel/widget/ThemeSettings.vue
new file mode 100644
index 0000000..4b46fcd
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-settings-panel/widget/ThemeSettings.vue
@@ -0,0 +1,28 @@
+
+
+
+
+
+
![]()
+
+
{{ $t(`setting.theme.list[${index}]`) }}
+
+
+
+
+
diff --git a/adminSystem/src/components/core/layouts/art-work-tab/index.vue b/adminSystem/src/components/core/layouts/art-work-tab/index.vue
new file mode 100644
index 0000000..152ff63
--- /dev/null
+++ b/adminSystem/src/components/core/layouts/art-work-tab/index.vue
@@ -0,0 +1,584 @@
+
+
+
+
+
+ - showMenu(e, item.path)"
+ >
+
+ {{ item.customTitle || formatMenuTitle(item.title) }}
+
+
+
+
+
+
+
+
+
+
showMenu(e, activeTab)"
+ >
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/media/art-cutter-img/index.vue b/adminSystem/src/components/core/media/art-cutter-img/index.vue
new file mode 100644
index 0000000..191ceed
--- /dev/null
+++ b/adminSystem/src/components/core/media/art-cutter-img/index.vue
@@ -0,0 +1,350 @@
+
+
+
+
+
{{ title }}
+
+
+ 选择图片
+
+
+ 清除
+
+
+
+
+
+
+
+
+
+
{{ previewTitle }}
+
+
![预览图]()
+
+
下载图片
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/media/art-video-player/index.vue b/adminSystem/src/components/core/media/art-video-player/index.vue
new file mode 100644
index 0000000..4f681ea
--- /dev/null
+++ b/adminSystem/src/components/core/media/art-video-player/index.vue
@@ -0,0 +1,111 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/others/art-menu-right/index.vue b/adminSystem/src/components/core/others/art-menu-right/index.vue
new file mode 100644
index 0000000..1cc92ab
--- /dev/null
+++ b/adminSystem/src/components/core/others/art-menu-right/index.vue
@@ -0,0 +1,415 @@
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/others/art-watermark/index.vue b/adminSystem/src/components/core/others/art-watermark/index.vue
new file mode 100644
index 0000000..1d7f06b
--- /dev/null
+++ b/adminSystem/src/components/core/others/art-watermark/index.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/tables/art-table-header/index.vue b/adminSystem/src/components/core/tables/art-table-header/index.vue
new file mode 100644
index 0000000..788c2b7
--- /dev/null
+++ b/adminSystem/src/components/core/tables/art-table-header/index.vue
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/tables/art-table/index.vue b/adminSystem/src/components/core/tables/art-table/index.vue
new file mode 100644
index 0000000..2392d96
--- /dev/null
+++ b/adminSystem/src/components/core/tables/art-table/index.vue
@@ -0,0 +1,342 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ getGlobalIndex($index) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ col.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/tables/art-table/style.scss b/adminSystem/src/components/core/tables/art-table/style.scss
new file mode 100644
index 0000000..67459e8
--- /dev/null
+++ b/adminSystem/src/components/core/tables/art-table/style.scss
@@ -0,0 +1,99 @@
+.art-table {
+ position: relative;
+ height: 100%;
+
+ .el-table {
+ height: 100%;
+ margin-top: 10px;
+ }
+
+ :deep(.el-loading-mask) {
+ z-index: 100;
+ background-color: var(--default-box-color) !important;
+ }
+
+ // Loading 过渡动画 - 消失时淡出
+ .loading-fade-leave-active {
+ transition: opacity 0.3s ease-out;
+ }
+
+ .loading-fade-leave-to {
+ opacity: 0;
+ }
+
+ // 空状态垂直居中
+ &.is-empty {
+ :deep(.el-scrollbar__wrap) {
+ display: flex;
+ }
+ }
+
+ .pagination {
+ display: flex;
+ margin-top: 13px;
+
+ :deep(.el-select) {
+ width: 102px !important;
+ }
+
+ // 分页对齐方式
+ &.left {
+ justify-content: flex-start;
+ }
+
+ &.center {
+ justify-content: center;
+ }
+
+ &.right {
+ justify-content: flex-end;
+ }
+
+ // 自定义分页组件样式
+ &.custom-pagination {
+ :deep(.el-pagination) {
+ .btn-prev,
+ .btn-next {
+ background-color: transparent;
+ border: 1px solid var(--art-gray-300);
+ transition: border-color 0.15s;
+
+ &:hover:not(.is-disabled) {
+ color: var(--theme-color);
+ border-color: var(--theme-color);
+ }
+ }
+
+ li {
+ box-sizing: border-box;
+ font-weight: 400 !important;
+ background-color: transparent;
+ border: 1px solid var(--art-gray-300);
+ transition: border-color 0.15s;
+
+ &.is-active {
+ font-weight: 400;
+ color: #fff;
+ background-color: var(--theme-color);
+ border: 1px solid var(--theme-color);
+ }
+
+ &:hover:not(.is-disabled) {
+ border-color: var(--theme-color);
+ }
+ }
+ }
+ }
+ }
+}
+
+// 移动端分页
+@media (width <= 640px) {
+ :deep(.el-pagination) {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 15px 0;
+ align-items: center;
+ justify-content: center;
+ }
+}
diff --git a/adminSystem/src/components/core/text-effect/art-count-to/index.vue b/adminSystem/src/components/core/text-effect/art-count-to/index.vue
new file mode 100644
index 0000000..7fb104b
--- /dev/null
+++ b/adminSystem/src/components/core/text-effect/art-count-to/index.vue
@@ -0,0 +1,310 @@
+
+
+
+ {{ formattedValue }}
+
+
+
+
diff --git a/adminSystem/src/components/core/text-effect/art-festival-text-scroll/index.vue b/adminSystem/src/components/core/text-effect/art-festival-text-scroll/index.vue
new file mode 100644
index 0000000..770b457
--- /dev/null
+++ b/adminSystem/src/components/core/text-effect/art-festival-text-scroll/index.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/text-effect/art-text-scroll/index.vue b/adminSystem/src/components/core/text-effect/art-text-scroll/index.vue
new file mode 100644
index 0000000..90be30f
--- /dev/null
+++ b/adminSystem/src/components/core/text-effect/art-text-scroll/index.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/theme/theme-svg/index.vue b/adminSystem/src/components/core/theme/theme-svg/index.vue
new file mode 100644
index 0000000..0b565a9
--- /dev/null
+++ b/adminSystem/src/components/core/theme/theme-svg/index.vue
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/views/exception/ArtException.vue b/adminSystem/src/components/core/views/exception/ArtException.vue
new file mode 100644
index 0000000..699228f
--- /dev/null
+++ b/adminSystem/src/components/core/views/exception/ArtException.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
{{ data.desc }}
+
{{
+ data.btnText
+ }}
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/views/login/AuthTopBar.vue b/adminSystem/src/components/core/views/login/AuthTopBar.vue
new file mode 100644
index 0000000..9455253
--- /dev/null
+++ b/adminSystem/src/components/core/views/login/AuthTopBar.vue
@@ -0,0 +1,149 @@
+
+
+
+
+
+
{{ AppConfig.systemInfo.name }}
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/views/login/LoginLeftView.vue b/adminSystem/src/components/core/views/login/LoginLeftView.vue
new file mode 100644
index 0000000..af6a904
--- /dev/null
+++ b/adminSystem/src/components/core/views/login/LoginLeftView.vue
@@ -0,0 +1,602 @@
+
+
+
+
+
+
{{ AppConfig.systemInfo.name }}
+
+
+
+
+
+
+
+
{{ $t('login.leftView.title') }}
+
{{ $t('login.leftView.subTitle') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/views/result/ArtResultPage.vue b/adminSystem/src/components/core/views/result/ArtResultPage.vue
new file mode 100644
index 0000000..b2eca48
--- /dev/null
+++ b/adminSystem/src/components/core/views/result/ArtResultPage.vue
@@ -0,0 +1,43 @@
+
+
+
+
{{
+ title
+ }}
+
{{ message }}
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/components/core/widget/art-icon-button/index.vue b/adminSystem/src/components/core/widget/art-icon-button/index.vue
new file mode 100644
index 0000000..760888b
--- /dev/null
+++ b/adminSystem/src/components/core/widget/art-icon-button/index.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/config/assets/images.ts b/adminSystem/src/config/assets/images.ts
new file mode 100644
index 0000000..f3e89dd
--- /dev/null
+++ b/adminSystem/src/config/assets/images.ts
@@ -0,0 +1,61 @@
+/**
+ * 配置图片资源
+ *
+ * 统一管理设置中心使用的预览图片资源。
+ * 包含主题样式、菜单布局、菜单风格的预览图。
+ *
+ * ## 图片分类
+ *
+ * - themeStyles: 系统主题预览图(亮色/暗色/自动)
+ * - menuLayouts: 菜单布局预览图(左侧/顶部/混合/双栏)
+ * - menuStyles: 菜单风格预览图(设计/暗色/亮色)
+ *
+ * @module config/assets/images
+ * @author Art Design Pro Team
+ */
+
+import lightTheme from '@imgs/settings/theme_styles/light.png'
+import darkTheme from '@imgs/settings/theme_styles/dark.png'
+import systemTheme from '@imgs/settings/theme_styles/system.png'
+import verticalLayout from '@imgs/settings/menu_layouts/vertical.png'
+import horizontalLayout from '@imgs/settings/menu_layouts/horizontal.png'
+import mixedLayout from '@imgs/settings/menu_layouts/mixed.png'
+import dualColumnLayout from '@imgs/settings/menu_layouts/dual_column.png'
+import designStyle from '@imgs/settings/menu_styles/design.png'
+import darkStyle from '@imgs/settings/menu_styles/dark.png'
+import lightStyle from '@imgs/settings/menu_styles/light.png'
+
+/**
+ * 配置中心图片资源对象
+ */
+export const configImages = {
+ /** 系统主题预览图 */
+ themeStyles: {
+ /** 亮色主题 */
+ light: lightTheme,
+ /** 暗色主题 */
+ dark: darkTheme,
+ /** 自动主题(跟随系统) */
+ system: systemTheme
+ },
+ /** 菜单布局预览图 */
+ menuLayouts: {
+ /** 左侧菜单 */
+ vertical: verticalLayout,
+ /** 顶部菜单 */
+ horizontal: horizontalLayout,
+ /** 混合菜单 */
+ mixed: mixedLayout,
+ /** 双栏菜单 */
+ dualColumn: dualColumnLayout
+ },
+ /** 菜单风格预览图 */
+ menuStyles: {
+ /** 设计风格 */
+ design: designStyle,
+ /** 暗色风格 */
+ dark: darkStyle,
+ /** 亮色风格 */
+ light: lightStyle
+ }
+}
diff --git a/adminSystem/src/config/fastEnter.ts b/adminSystem/src/config/fastEnter.ts
new file mode 100644
index 0000000..ccade16
--- /dev/null
+++ b/adminSystem/src/config/fastEnter.ts
@@ -0,0 +1,79 @@
+/**
+ * 快速入口配置
+ * 包含:应用列表、快速链接等配置
+ */
+import { WEB_LINKS } from '@/utils/constants'
+import type { FastEnterConfig } from '@/types/config'
+
+const fastEnterConfig: FastEnterConfig = {
+ // 显示条件(屏幕宽度)
+ minWidth: 1200,
+ // 应用列表
+ applications: [
+ {
+ name: '工作台',
+ description: '系统概览与数据统计',
+ icon: 'ri:pie-chart-line',
+ iconColor: '#377dff',
+ enabled: true,
+ order: 1,
+ routeName: 'Console'
+ },
+ {
+ name: '官方文档',
+ description: '使用指南与开发文档',
+ icon: 'ri:bill-line',
+ iconColor: '#ffb100',
+ enabled: true,
+ order: 2,
+ link: WEB_LINKS.DOCS
+ },
+ {
+ name: '技术支持',
+ description: '技术支持与问题反馈',
+ icon: 'ri:user-location-line',
+ iconColor: '#ff6b6b',
+ enabled: true,
+ order: 3,
+ link: WEB_LINKS.COMMUNITY
+ },
+ {
+ name: '哔哩哔哩',
+ description: '技术分享与交流',
+ icon: 'ri:bilibili-line',
+ iconColor: '#FB7299',
+ enabled: true,
+ order: 4,
+ link: WEB_LINKS.BILIBILI
+ }
+ ],
+ // 快速链接
+ quickLinks: [
+ {
+ name: '登录',
+ enabled: true,
+ order: 1,
+ routeName: 'Login'
+ },
+ {
+ name: '注册',
+ enabled: true,
+ order: 2,
+ routeName: 'Register'
+ },
+ {
+ name: '忘记密码',
+ enabled: true,
+ order: 3,
+ routeName: 'ForgetPassword'
+ },
+ {
+ name: '个人中心',
+ enabled: true,
+ order: 4,
+ routeName: 'UserCenter'
+ }
+ ]
+}
+
+export default Object.freeze(fastEnterConfig)
diff --git a/adminSystem/src/config/index.ts b/adminSystem/src/config/index.ts
new file mode 100644
index 0000000..daa623a
--- /dev/null
+++ b/adminSystem/src/config/index.ts
@@ -0,0 +1,135 @@
+/**
+ * 系统全局配置
+ *
+ * 这是系统的核心配置文件,集中管理所有全局配置项。
+ * 包含系统信息、主题样式、菜单布局、颜色方案等所有可配置项。
+ *
+ * ## 主要功能
+ *
+ * - 系统信息 - 系统名称等基础信息
+ * - 主题配置 - 亮色/暗色/自动主题的样式配置
+ * - 菜单配置 - 菜单布局、主题、宽度等配置
+ * - 颜色方案 - 系统主色和预设颜色列表
+ * - 快速入口 - 快速入口应用和链接配置
+ * - 顶部栏配置 - 顶部栏功能模块配置
+ *
+ * ## 配置项说明
+ *
+ * - systemInfo: 系统基础信息(名称等)
+ * - systemThemeStyles: 系统主题样式映射
+ * - settingThemeList: 可选的系统主题列表
+ * - menuLayoutList: 可选的菜单布局列表
+ * - themeList: 菜单主题样式列表
+ * - darkMenuStyles: 暗黑模式下的菜单样式
+ * - systemMainColor: 预设的系统主色列表
+ * - fastEnter: 快速入口配置
+ * - headerBar: 顶部栏功能配置
+ *
+ * @module config
+ * @author Art Design Pro Team
+ */
+
+import { MenuThemeEnum, MenuTypeEnum, SystemThemeEnum } from '@/enums/appEnum'
+import { SystemConfig } from '@/types/config'
+import { configImages } from './assets/images'
+import fastEnterConfig from './modules/fastEnter'
+import { headerBarConfig } from './modules/headerBar'
+
+const appConfig: SystemConfig = {
+ // 系统信息
+ systemInfo: {
+ name: 'Art Design Pro' // 系统名称
+ },
+ // 系统主题
+ systemThemeStyles: {
+ [SystemThemeEnum.LIGHT]: { className: '' },
+ [SystemThemeEnum.DARK]: { className: SystemThemeEnum.DARK }
+ },
+ // 系统主题列表
+ settingThemeList: [
+ {
+ name: 'Light',
+ theme: SystemThemeEnum.LIGHT,
+ color: ['#fff', '#fff'],
+ leftLineColor: '#EDEEF0',
+ rightLineColor: '#EDEEF0',
+ img: configImages.themeStyles.light
+ },
+ {
+ name: 'Dark',
+ theme: SystemThemeEnum.DARK,
+ color: ['#22252A'],
+ leftLineColor: '#3F4257',
+ rightLineColor: '#3F4257',
+ img: configImages.themeStyles.dark
+ },
+ {
+ name: 'System',
+ theme: SystemThemeEnum.AUTO,
+ color: ['#fff', '#22252A'],
+ leftLineColor: '#EDEEF0',
+ rightLineColor: '#3F4257',
+ img: configImages.themeStyles.system
+ }
+ ],
+ // 菜单布局列表
+ menuLayoutList: [
+ { name: 'Left', value: MenuTypeEnum.LEFT, img: configImages.menuLayouts.vertical },
+ { name: 'Top', value: MenuTypeEnum.TOP, img: configImages.menuLayouts.horizontal },
+ { name: 'Mixed', value: MenuTypeEnum.TOP_LEFT, img: configImages.menuLayouts.mixed },
+ { name: 'Dual Column', value: MenuTypeEnum.DUAL_MENU, img: configImages.menuLayouts.dualColumn }
+ ],
+ // 菜单主题列表
+ themeList: [
+ {
+ theme: MenuThemeEnum.DESIGN,
+ background: '#FFFFFF',
+ systemNameColor: 'var(--art-gray-800)',
+ iconColor: '#6B6B6B',
+ textColor: '#29343D',
+ img: configImages.menuStyles.design
+ },
+ {
+ theme: MenuThemeEnum.DARK,
+ background: '#191A23',
+ systemNameColor: '#D9DADB',
+ iconColor: '#BABBBD',
+ textColor: '#BABBBD',
+ img: configImages.menuStyles.dark
+ },
+ {
+ theme: MenuThemeEnum.LIGHT,
+ background: '#ffffff',
+ systemNameColor: 'var(--art-gray-800)',
+ iconColor: '#6B6B6B',
+ textColor: '#29343D',
+ img: configImages.menuStyles.light
+ }
+ ],
+ // 暗黑模式菜单样式
+ darkMenuStyles: [
+ {
+ theme: MenuThemeEnum.DARK,
+ background: 'var(--default-box-color)',
+ systemNameColor: '#DDDDDD',
+ iconColor: '#BABBBD',
+ textColor: 'rgba(#FFFFFF, 0.7)'
+ }
+ ],
+ // 系统主色
+ systemMainColor: [
+ '#5D87FF',
+ '#B48DF3',
+ '#1D84FF',
+ '#60C041',
+ '#38C0FC',
+ '#F9901F',
+ '#FF80C8'
+ ] as const,
+ // 快速入口配置
+ fastEnter: fastEnterConfig,
+ // 顶部栏功能配置
+ headerBar: headerBarConfig
+}
+
+export default Object.freeze(appConfig)
diff --git a/adminSystem/src/config/modules/component.ts b/adminSystem/src/config/modules/component.ts
new file mode 100644
index 0000000..bc709e0
--- /dev/null
+++ b/adminSystem/src/config/modules/component.ts
@@ -0,0 +1,105 @@
+/**
+ * 全局组件配置
+ *
+ * 统一管理系统级全局组件的注册。
+ * 这些组件会在应用启动时全局注册,可在任何地方使用。
+ *
+ * ## 主要功能
+ *
+ * - 组件配置 - 集中管理全局组件的配置信息
+ * - 异步加载 - 使用 defineAsyncComponent 实现按需加载
+ * - 开关控制 - 支持通过 enabled 字段启用/禁用组件
+ * - 配置查询 - 提供工具函数快速查询组件配置
+ *
+ * @module config/component
+ * @author Art Design Pro Team
+ */
+
+import { defineAsyncComponent } from 'vue'
+
+/**
+ * 全局组件配置列表
+ */
+export const globalComponentsConfig: GlobalComponentConfig[] = [
+ {
+ name: '设置面板',
+ key: 'settings-panel',
+ component: defineAsyncComponent(
+ () => import('@/components/core/layouts/art-settings-panel/index.vue')
+ ),
+ enabled: true
+ },
+ {
+ name: '全局搜索',
+ key: 'global-search',
+ component: defineAsyncComponent(
+ () => import('@/components/core/layouts/art-global-search/index.vue')
+ ),
+ enabled: true
+ },
+ {
+ name: '锁屏',
+ key: 'screen-lock',
+ component: defineAsyncComponent(
+ () => import('@/components/core/layouts/art-screen-lock/index.vue')
+ ),
+ enabled: true
+ },
+ {
+ name: '聊天窗口',
+ key: 'chat-window',
+ component: defineAsyncComponent(
+ () => import('@/components/core/layouts/art-chat-window/index.vue')
+ ),
+ enabled: true
+ },
+ {
+ name: '礼花效果',
+ key: 'fireworks-effect',
+ component: defineAsyncComponent(
+ () => import('@/components/core/layouts/art-fireworks-effect/index.vue')
+ ),
+ enabled: true
+ },
+ {
+ name: '水印效果',
+ key: 'watermark',
+ component: defineAsyncComponent(
+ () => import('@/components/core/others/art-watermark/index.vue')
+ ),
+ enabled: true
+ }
+]
+
+/**
+ * 全局组件配置接口
+ */
+export interface GlobalComponentConfig {
+ /** 组件名称 */
+ name: string
+ /** 组件标识 */
+ key: string
+ /** 组件 */
+ component: any
+ /** 是否启用 */
+ enabled?: boolean
+ /** 组件描述 */
+ description?: string
+}
+
+/**
+ * 获取启用的全局组件
+ * @returns 已启用的组件配置列表
+ */
+export const getEnabledGlobalComponents = () => {
+ return globalComponentsConfig.filter((config) => config.enabled !== false)
+}
+
+/**
+ * 根据 key 获取组件配置
+ * @param key 组件标识
+ * @returns 组件配置对象
+ */
+export const getGlobalComponentByKey = (key: string) => {
+ return globalComponentsConfig.find((config) => config.key === key)
+}
diff --git a/adminSystem/src/config/modules/fastEnter.ts b/adminSystem/src/config/modules/fastEnter.ts
new file mode 100644
index 0000000..6b9740c
--- /dev/null
+++ b/adminSystem/src/config/modules/fastEnter.ts
@@ -0,0 +1,127 @@
+/**
+ * 快速入口配置
+ * 包含:应用列表、快速链接等配置
+ */
+import { WEB_LINKS } from '@/utils/constants'
+import type { FastEnterConfig } from '@/types/config'
+
+const fastEnterConfig: FastEnterConfig = {
+ // 显示条件(屏幕宽度)
+ minWidth: 1200,
+ // 应用列表
+ applications: [
+ {
+ name: '工作台',
+ description: '系统概览与数据统计',
+ icon: 'ri:pie-chart-line',
+ iconColor: '#377dff',
+ enabled: true,
+ order: 1,
+ routeName: 'Console'
+ },
+ {
+ name: '分析页',
+ description: '数据分析与可视化',
+ icon: 'ri:game-line',
+ iconColor: '#ff3b30',
+ enabled: true,
+ order: 2,
+ routeName: 'Analysis'
+ },
+ {
+ name: '礼花效果',
+ description: '动画特效展示',
+ icon: 'ri:loader-line',
+ iconColor: '#7A7FFF',
+ enabled: true,
+ order: 3,
+ routeName: 'Fireworks'
+ },
+ {
+ name: '聊天',
+ description: '即时通讯功能',
+ icon: 'ri:user-line',
+ iconColor: '#13DEB9',
+ enabled: true,
+ order: 4,
+ routeName: 'Chat'
+ },
+ {
+ name: '官方文档',
+ description: '使用指南与开发文档',
+ icon: 'ri:bill-line',
+ iconColor: '#ffb100',
+ enabled: true,
+ order: 5,
+ link: WEB_LINKS.DOCS
+ },
+ {
+ name: '技术支持',
+ description: '技术支持与问题反馈',
+ icon: 'ri:user-location-line',
+ iconColor: '#ff6b6b',
+ enabled: true,
+ order: 6,
+ link: WEB_LINKS.COMMUNITY
+ },
+ {
+ name: '更新日志',
+ description: '版本更新与变更记录',
+ icon: 'ri:gamepad-line',
+ iconColor: '#38C0FC',
+ enabled: true,
+ order: 7,
+ routeName: 'ChangeLog'
+ },
+ {
+ name: '哔哩哔哩',
+ description: '技术分享与交流',
+ icon: 'ri:bilibili-line',
+ iconColor: '#FB7299',
+ enabled: true,
+ order: 8,
+ link: WEB_LINKS.BILIBILI
+ }
+ ],
+ // 快速链接
+ quickLinks: [
+ {
+ name: '登录',
+ enabled: true,
+ order: 1,
+ routeName: 'Login'
+ },
+ {
+ name: '注册',
+ enabled: true,
+ order: 2,
+ routeName: 'Register'
+ },
+ {
+ name: '忘记密码',
+ enabled: true,
+ order: 3,
+ routeName: 'ForgetPassword'
+ },
+ {
+ name: '定价',
+ enabled: true,
+ order: 4,
+ routeName: 'Pricing'
+ },
+ {
+ name: '个人中心',
+ enabled: true,
+ order: 5,
+ routeName: 'UserCenter'
+ },
+ {
+ name: '留言管理',
+ enabled: true,
+ order: 6,
+ routeName: 'ArticleComment'
+ }
+ ]
+}
+
+export default Object.freeze(fastEnterConfig)
diff --git a/adminSystem/src/config/modules/festival.ts b/adminSystem/src/config/modules/festival.ts
new file mode 100644
index 0000000..39cd790
--- /dev/null
+++ b/adminSystem/src/config/modules/festival.ts
@@ -0,0 +1,51 @@
+/**
+ * 节日庆祝配置
+ *
+ * 配置系统的节日烟花效果和祝福文本。
+ * 支持单日节日和跨日期节日,可自定义烟花播放次数。
+ *
+ * ## 配置说明
+ *
+ * - name: 节日名称
+ * - date: 节日开始日期(格式:YYYY-MM-DD)
+ * - endDate: 节日结束日期(可选,用于跨日期节日)
+ * - image: 烟花图片(需要预先导入)
+ * - scrollText: 滚动显示的祝福文本
+ * - count: 烟花播放次数(可选,默认为 3 次)
+ *
+ * ## 注意事项
+ *
+ * - 图片需要预先导入并在配置中引用
+ * - 跨日期节日会在整个日期范围内生效
+ * - 每个用户每天只会播放一次烟花效果
+ *
+ * @module config/modules/festival
+ * @author Art Design Pro Team
+ */
+
+import { FestivalConfig } from '@/types/config'
+
+// 导入烟花图片(根据需要取消注释)
+// import sd from '@imgs/ceremony/sd.png'
+// import yd from '@imgs/ceremony/yd.png'
+
+export const festivalConfigList: FestivalConfig[] = [
+ // 跨日期示例
+ // {
+ // name: 'v3.0 Sass 升级至 TailwindCSS',
+ // date: '2025-11-03',
+ // endDate: '2025-11-09',
+ // image: '',
+ // count: 3,
+ // scrollText:
+ // '🚀 系统 v3.0 测试阶段正式开启!测试周期为 11 月 3 日 - 11 月 16 日,通过 TailwindCSS 重构样式体系、统一 Iconify 图标方案,带来更高效现代的开发体验,正式发布敬请期待~'
+ // }
+ // 单日示例:圣诞节
+ // {
+ // name: '圣诞节',
+ // date: '2024-12-25',
+ // image: sd,
+ // count: 3 // 可选,不设置则使用默认值 3 次
+ // scrollText: 'Merry Christmas!Art Design Pro 祝您圣诞快乐,愿节日的欢乐与祝福如雪花般纷至沓来!',
+ // }
+]
diff --git a/adminSystem/src/config/modules/headerBar.ts b/adminSystem/src/config/modules/headerBar.ts
new file mode 100644
index 0000000..a420e82
--- /dev/null
+++ b/adminSystem/src/config/modules/headerBar.ts
@@ -0,0 +1,63 @@
+/**
+ * 顶部栏功能配置
+ *
+ * 统一管理顶部栏各个功能模块的启用状态。
+ * 通过修改此配置文件可以快速启用或禁用顶部栏的功能按钮。
+ *
+ * @module config/headerBar
+ * @author Art Design Pro Team
+ */
+
+import { HeaderBarFeatureConfig } from '@/types'
+
+/**
+ * 顶部栏功能配置对象
+ */
+export const headerBarConfig: HeaderBarFeatureConfig = {
+ menuButton: {
+ enabled: true,
+ description: '控制左侧菜单的展开/收起按钮'
+ },
+ refreshButton: {
+ enabled: true,
+ description: '页面刷新按钮'
+ },
+ fastEnter: {
+ enabled: true,
+ description: '快速入口功能,提供常用应用和链接的快速访问'
+ },
+ breadcrumb: {
+ enabled: true,
+ description: '面包屑导航,显示当前页面路径'
+ },
+ globalSearch: {
+ enabled: true,
+ description: '全局搜索功能,支持快捷键 Ctrl+K 或 Cmd+K'
+ },
+ fullscreen: {
+ enabled: true,
+ description: '全屏切换功能'
+ },
+ notification: {
+ enabled: true,
+ description: '通知中心,显示系统通知和消息'
+ },
+ chat: {
+ enabled: true,
+ description: '聊天功能,提供实时沟通'
+ },
+ language: {
+ enabled: true,
+ description: '多语言切换功能'
+ },
+ settings: {
+ enabled: true,
+ description: '系统设置面板'
+ },
+ themeToggle: {
+ enabled: true,
+ description: '主题切换功能(明暗主题)'
+ }
+}
+
+export default headerBarConfig
diff --git a/adminSystem/src/config/setting.ts b/adminSystem/src/config/setting.ts
new file mode 100644
index 0000000..94f2d2c
--- /dev/null
+++ b/adminSystem/src/config/setting.ts
@@ -0,0 +1,109 @@
+/**
+ * 系统设置默认值配置
+ *
+ * 统一管理系统设置的所有默认值
+ *
+ * ## 主要功能
+ *
+ * - 菜单相关默认配置
+ * - 主题相关默认配置
+ * - 界面显示默认配置
+ * - 功能开关默认配置
+ * - 样式相关默认配置
+ *
+ * ## 注意事项
+ *
+ * 1. 修改此文件的配置项时,需要同步更新以下文件:
+ * - src/components/core/layouts/art-settings-panel/widget/SettingActions.vue(复制配置和重置配置逻辑)
+ * - src/store/modules/setting.ts(Store 状态定义)
+ * 2. 可以通过设置面板的"复制配置"按钮快速生成配置代码
+ * 3. 枚举类型的值需要与 src/enums/appEnum.ts 中的定义保持一致
+ */
+
+import AppConfig from '@/config'
+import { SystemThemeEnum, MenuThemeEnum, MenuTypeEnum, ContainerWidthEnum } from '@/enums/appEnum'
+
+/**
+ * 系统设置默认值配置
+ */
+export const SETTING_DEFAULT_CONFIG = {
+ /** 菜单类型 */
+ menuType: MenuTypeEnum.LEFT,
+ /** 菜单展开宽度 */
+ menuOpenWidth: 230,
+ /** 菜单是否展开 */
+ menuOpen: true,
+ /** 双菜单是否显示文本 */
+ dualMenuShowText: false,
+ /** 系统主题类型 */
+ systemThemeType: SystemThemeEnum.AUTO,
+ /** 系统主题模式 */
+ systemThemeMode: SystemThemeEnum.AUTO,
+ /** 菜单风格 */
+ menuThemeType: MenuThemeEnum.DESIGN,
+ /** 系统主题颜色 */
+ systemThemeColor: AppConfig.systemMainColor[0],
+ /** 是否显示菜单按钮 */
+ showMenuButton: true,
+ /** 是否显示快速入口 */
+ showFastEnter: true,
+ /** 是否显示刷新按钮 */
+ showRefreshButton: true,
+ /** 是否显示面包屑 */
+ showCrumbs: true,
+ /** 是否显示工作台标签 */
+ showWorkTab: true,
+ /** 是否显示语言切换 */
+ showLanguage: true,
+ /** 是否显示进度条 */
+ showNprogress: false,
+ /** 是否显示设置引导 */
+ showSettingGuide: true,
+ /** 是否显示节日文本 */
+ showFestivalText: false,
+ /** 是否显示水印 */
+ watermarkVisible: false,
+ /** 是否自动关闭 */
+ autoClose: false,
+ /** 是否唯一展开 */
+ uniqueOpened: true,
+ /** 是否色弱模式 */
+ colorWeak: false,
+ /** 是否刷新 */
+ refresh: false,
+ /** 是否加载节日烟花 */
+ holidayFireworksLoaded: false,
+ /** 边框模式 */
+ boxBorderMode: true,
+ /** 页面过渡效果 */
+ pageTransition: 'slide-left',
+ /** 标签页样式 */
+ tabStyle: 'tab-default',
+ /** 自定义圆角 */
+ customRadius: '0.75',
+ /** 容器宽度 */
+ containerWidth: ContainerWidthEnum.FULL,
+ /** 节日日期 */
+ festivalDate: ''
+}
+
+/**
+ * 获取设置默认值
+ * @returns 设置默认值对象
+ */
+export function getSettingDefaults() {
+ return { ...SETTING_DEFAULT_CONFIG }
+}
+
+/**
+ * 重置为默认设置
+ * @param currentSettings 当前设置对象
+ */
+export function resetToDefaults(currentSettings: Record) {
+ const defaults = getSettingDefaults()
+ Object.keys(defaults).forEach((key) => {
+ if (key in currentSettings) {
+ currentSettings[key] = defaults[key as keyof typeof defaults]
+ }
+ })
+}
diff --git a/adminSystem/src/directives/business/highlight.ts b/adminSystem/src/directives/business/highlight.ts
new file mode 100644
index 0000000..13af225
--- /dev/null
+++ b/adminSystem/src/directives/business/highlight.ts
@@ -0,0 +1,248 @@
+/**
+ * v-highlight 代码高亮指令
+ *
+ * 为代码块提供语法高亮、行号显示和一键复制功能。
+ * 基于 highlight.js 实现,支持多种编程语言的语法高亮。
+ *
+ * ## 主要功能
+ *
+ * - 语法高亮 - 使用 highlight.js 自动识别并高亮代码
+ * - 行号显示 - 自动为每行代码添加行号
+ * - 一键复制 - 提供复制按钮,点击即可复制代码(自动过滤行号)
+ * - 性能优化 - 批量处理代码块,避免阻塞渲染
+ * - 动态监听 - 使用 MutationObserver 监听新增代码块
+ * - 防重复处理 - 自动标记已处理的代码块,避免重复处理
+ *
+ * ## 使用示例
+ *
+ * ```vue
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * const hello = 'world';
+ * console.log(hello);
+ *
+ *
+ *
+ * ```
+ *
+ * ## 性能优化
+ *
+ * - 批量处理:每次处理 10 个代码块,避免长时间阻塞
+ * - 延迟处理:使用 requestAnimationFrame 分批处理
+ * - 重试机制:自动重试处理失败的代码块
+ * - 智能监听:只在有新代码块时才触发处理
+ *
+ * @module directives/highlight
+ * @author Art Design Pro Team
+ */
+
+import { App, Directive } from 'vue'
+import hljs from 'highlight.js'
+
+// 高亮代码
+function highlightCode(block: HTMLElement) {
+ hljs.highlightElement(block)
+}
+
+// 插入行号
+function insertLineNumbers(block: HTMLElement) {
+ const lines = block.innerHTML.split('\n')
+ const numberedLines = lines
+ .map((line, index) => {
+ return `${index + 1} ${line}`
+ })
+ .join('\n')
+ block.innerHTML = numberedLines
+}
+
+// 添加复制按钮:调整 DOM 结构,将代码部分包裹在 .code-wrapper 内
+function addCopyButton(block: HTMLElement) {
+ const copyButton = document.createElement('i')
+ copyButton.className = 'copy-button'
+ copyButton.innerHTML =
+ ''
+ copyButton.onclick = () => {
+ // 过滤掉行号,只复制代码内容
+ const codeContent = block.innerText.replace(/^\d+\s+/gm, '')
+ navigator.clipboard.writeText(codeContent).then(() => {
+ ElMessage.success('复制成功')
+ })
+ }
+
+ const preElement = block.parentElement
+ if (preElement) {
+ let codeWrapper: HTMLElement
+ // 如果代码块还没有被包裹,则创建包裹容器
+ if (!block.parentElement.classList.contains('code-wrapper')) {
+ codeWrapper = document.createElement('div')
+ codeWrapper.className = 'code-wrapper'
+ preElement.replaceChild(codeWrapper, block)
+ codeWrapper.appendChild(block)
+ } else {
+ codeWrapper = block.parentElement
+ }
+ // 将复制按钮添加到 pre 元素(而非 codeWrapper 内),这样它不会随滚动条滚动
+ preElement.appendChild(copyButton)
+ }
+}
+
+// 检查代码块是否已经被处理过
+function isBlockProcessed(block: HTMLElement): boolean {
+ return (
+ block.hasAttribute('data-highlighted') ||
+ !!block.querySelector('.line-number') ||
+ !!block.parentElement?.querySelector('.copy-button')
+ )
+}
+
+// 标记代码块为已处理
+function markBlockAsProcessed(block: HTMLElement) {
+ block.setAttribute('data-highlighted', 'true')
+}
+
+// 处理单个代码块
+function processBlock(block: HTMLElement) {
+ if (isBlockProcessed(block)) {
+ return
+ }
+
+ try {
+ highlightCode(block)
+ insertLineNumbers(block)
+ addCopyButton(block)
+ markBlockAsProcessed(block)
+ } catch (error) {
+ console.warn('处理代码块时出错:', error)
+ }
+}
+
+// 查找并处理所有代码块
+function processAllCodeBlocks(el: HTMLElement) {
+ const blocks = Array.from(el.querySelectorAll('pre code'))
+ const unprocessedBlocks = blocks.filter((block) => !isBlockProcessed(block))
+
+ if (unprocessedBlocks.length === 0) {
+ return
+ }
+
+ if (unprocessedBlocks.length <= 10) {
+ // 如果代码块数量少于等于10,直接处理所有代码块
+ unprocessedBlocks.forEach((block) => processBlock(block))
+ } else {
+ // 定义每次处理的代码块数
+ const batchSize = 10
+ let currentIndex = 0
+
+ const processBatch = () => {
+ const batch = unprocessedBlocks.slice(currentIndex, currentIndex + batchSize)
+
+ batch.forEach((block) => {
+ processBlock(block)
+ })
+
+ // 更新索引并继续处理下一批
+ currentIndex += batchSize
+ if (currentIndex < unprocessedBlocks.length) {
+ // 使用 requestAnimationFrame 确保下一帧再处理
+ requestAnimationFrame(processBatch)
+ }
+ }
+
+ // 开始处理第一批代码块
+ processBatch()
+ }
+}
+
+// 重试处理函数
+function retryProcessing(el: HTMLElement, maxRetries: number = 3, delay: number = 200) {
+ let retryCount = 0
+
+ const tryProcess = () => {
+ processAllCodeBlocks(el)
+
+ // 检查是否还有未处理的代码块
+ const remainingBlocks = Array.from(el.querySelectorAll('pre code')).filter(
+ (block) => !isBlockProcessed(block)
+ )
+
+ if (remainingBlocks.length > 0 && retryCount < maxRetries) {
+ retryCount++
+ setTimeout(tryProcess, delay * retryCount) // 递增延迟
+ }
+ }
+
+ tryProcess()
+}
+
+// 代码高亮、插入行号、复制按钮
+const highlightDirective: Directive = {
+ mounted(el: HTMLElement) {
+ // 立即尝试处理一次
+ processAllCodeBlocks(el)
+
+ // 延迟处理,确保 v-html 内容已经渲染
+ setTimeout(() => {
+ retryProcessing(el)
+ }, 100)
+
+ // 使用 MutationObserver 监听 DOM 变化
+ const observer = new MutationObserver((mutations) => {
+ let hasNewCodeBlocks = false
+
+ mutations.forEach((mutation) => {
+ if (mutation.type === 'childList') {
+ mutation.addedNodes.forEach((node) => {
+ if (node.nodeType === Node.ELEMENT_NODE) {
+ const element = node as HTMLElement
+ // 检查新添加的节点是否包含代码块
+ if (element.tagName === 'PRE' || element.querySelector('pre code')) {
+ hasNewCodeBlocks = true
+ }
+ }
+ })
+ }
+ })
+
+ if (hasNewCodeBlocks) {
+ // 延迟处理新添加的代码块
+ setTimeout(() => {
+ processAllCodeBlocks(el)
+ }, 50)
+ }
+ })
+
+ // 开始观察
+ observer.observe(el, {
+ childList: true,
+ subtree: true
+ })
+
+ // 将 observer 存储到元素上,以便在 unmounted 时清理
+ ;(el as any)._highlightObserver = observer
+ },
+
+ updated(el: HTMLElement) {
+ // 当组件更新时,重新处理代码块
+ setTimeout(() => {
+ processAllCodeBlocks(el)
+ }, 50)
+ },
+
+ unmounted(el: HTMLElement) {
+ // 清理 MutationObserver
+ const observer = (el as any)._highlightObserver
+ if (observer) {
+ observer.disconnect()
+ delete (el as any)._highlightObserver
+ }
+ }
+}
+
+export function setupHighlightDirective(app: App) {
+ app.directive('highlight', highlightDirective)
+}
diff --git a/adminSystem/src/directives/business/ripple.ts b/adminSystem/src/directives/business/ripple.ts
new file mode 100644
index 0000000..8d7d8f9
--- /dev/null
+++ b/adminSystem/src/directives/business/ripple.ts
@@ -0,0 +1,114 @@
+/**
+ * v-ripple 水波纹效果指令
+ *
+ * 为元素添加 Material Design 风格的水波纹点击效果。
+ * 点击时从点击位置扩散出圆形水波纹动画,提升交互体验。
+ *
+ * ## 主要功能
+ *
+ * - 水波纹动画 - 点击时从点击位置扩散圆形波纹
+ * - 自适应大小 - 根据元素尺寸自动调整波纹大小和动画时长
+ * - 智能配色 - 自动识别按钮类型,使用合适的波纹颜色
+ * - 自定义颜色 - 支持通过参数自定义波纹颜色
+ * - 性能优化 - 使用 requestAnimationFrame 和自动清理机制
+ *
+ * ## 使用示例
+ *
+ * ```vue
+ *
+ *
+ * 点击我
+ *
+ *
+ * 自定义颜色
+ *
+ *
+ * 卡片内容
+ *
+ * ```
+ *
+ * ## 颜色规则
+ *
+ * - 有色按钮(primary、success、warning 等):使用白色半透明波纹
+ * - 默认按钮:使用主题色半透明波纹
+ * - 自定义:通过 color 参数指定任意颜色
+ *
+ * @module directives/ripple
+ * @author Art Design Pro Team
+ */
+
+import type { App, Directive, DirectiveBinding } from 'vue'
+
+export interface RippleOptions {
+ /** 水波纹颜色 */
+ color?: string
+}
+
+export const vRipple: Directive = {
+ mounted(el: HTMLElement, binding: DirectiveBinding) {
+ // 获取指令的配置参数
+ const options: RippleOptions = binding.value || {}
+
+ // 设置元素为相对定位,并隐藏溢出部分
+ el.style.position = 'relative'
+ el.style.overflow = 'hidden'
+
+ // 点击事件处理
+ el.addEventListener('mousedown', (e: MouseEvent) => {
+ const rect = el.getBoundingClientRect()
+ const left = e.clientX - rect.left
+ const top = e.clientY - rect.top
+
+ // 创建水波纹元素
+ const ripple = document.createElement('div')
+ const diameter = Math.max(el.clientWidth, el.clientHeight)
+ const radius = diameter / 2
+
+ // 根据直径计算动画时间(直径越大,动画时间越长)
+ const baseTime = 600 // 基础动画时间(毫秒)
+ const scaleFactor = 0.5 // 缩放因子
+ const animationDuration = baseTime + diameter * scaleFactor
+
+ // 设置水波纹的尺寸和位置
+ ripple.style.width = ripple.style.height = `${diameter}px`
+ ripple.style.left = `${left - radius}px`
+ ripple.style.top = `${top - radius}px`
+ ripple.style.position = 'absolute'
+ ripple.style.borderRadius = '50%'
+ ripple.style.pointerEvents = 'none'
+
+ // 判断是否为有色按钮(Element Plus 按钮类型)
+ const buttonTypes = ['primary', 'info', 'warning', 'danger', 'success'].map(
+ (type) => `el-button--${type}`
+ )
+ const isColoredButton = buttonTypes.some((type) => el.classList.contains(type))
+ const defaultColor = isColoredButton
+ ? 'rgba(255, 255, 255, 0.25)' // 有色按钮使用白色水波纹
+ : 'var(--el-color-primary-light-7)' // 默认按钮使用主题色水波纹
+
+ // 设置水波纹颜色、初始状态和过渡效果
+ ripple.style.backgroundColor = options.color || defaultColor
+ ripple.style.transform = 'scale(0)'
+ ripple.style.transition = `transform ${animationDuration}ms cubic-bezier(0.3, 0, 0.2, 1), opacity ${animationDuration}ms cubic-bezier(0.3, 0, 0.5, 1)`
+ ripple.style.zIndex = '1'
+
+ // 添加水波纹元素到DOM中
+ el.appendChild(ripple)
+
+ // 触发动画
+ requestAnimationFrame(() => {
+ ripple.style.transform = 'scale(2)'
+ ripple.style.opacity = '0'
+ })
+
+ // 动画结束后移除水波纹元素
+ setTimeout(() => {
+ ripple.remove()
+ }, animationDuration + 500) // 增加500ms缓冲时间
+ })
+ }
+}
+
+export function setupRippleDirective(app: App) {
+ app.directive('ripple', vRipple)
+}
diff --git a/adminSystem/src/directives/core/auth.ts b/adminSystem/src/directives/core/auth.ts
new file mode 100644
index 0000000..b9e85d8
--- /dev/null
+++ b/adminSystem/src/directives/core/auth.ts
@@ -0,0 +1,68 @@
+/**
+ * v-auth 权限指令
+ *
+ * 适用于后端权限控制模式,基于权限标识控制 DOM 元素的显示和隐藏。
+ * 如果用户没有对应权限,元素将从 DOM 中移除。
+ *
+ * ## 主要功能
+ *
+ * - 权限验证 - 根据路由 meta 中的权限列表验证用户权限
+ * - DOM 控制 - 无权限时自动移除元素,而非隐藏
+ * - 响应式更新 - 权限变化时自动更新元素状态
+ *
+ * ## 使用示例
+ *
+ * ```vue
+ *
+ * 新增
+ *
+ *
+ * 编辑
+ *
+ *
+ * 删除
+ * ```
+ *
+ * ## 注意事项
+ *
+ * - 该指令会直接移除 DOM 元素,而不是使用 v-if 隐藏
+ * - 权限列表从当前路由的 meta.authList 中获取
+ *
+ * @module directives/auth
+ * @author Art Design Pro Team
+ */
+
+import { router } from '@/router'
+import { App, Directive, DirectiveBinding } from 'vue'
+
+interface AuthBinding extends DirectiveBinding {
+ value: string
+}
+
+function checkAuthPermission(el: HTMLElement, binding: AuthBinding): void {
+ // 获取当前路由的权限列表
+ const authList = (router.currentRoute.value.meta.authList as Array<{ authMark: string }>) || []
+
+ // 检查是否有对应的权限标识
+ const hasPermission = authList.some((item) => item.authMark === binding.value)
+
+ // 如果没有权限,移除元素
+ if (!hasPermission) {
+ removeElement(el)
+ }
+}
+
+function removeElement(el: HTMLElement): void {
+ if (el.parentNode) {
+ el.parentNode.removeChild(el)
+ }
+}
+
+const authDirective: Directive = {
+ mounted: checkAuthPermission,
+ updated: checkAuthPermission
+}
+
+export function setupAuthDirective(app: App): void {
+ app.directive('auth', authDirective)
+}
diff --git a/adminSystem/src/directives/core/roles.ts b/adminSystem/src/directives/core/roles.ts
new file mode 100644
index 0000000..2ab1029
--- /dev/null
+++ b/adminSystem/src/directives/core/roles.ts
@@ -0,0 +1,89 @@
+/**
+ * v-roles 角色权限指令
+ *
+ * 基于用户角色控制 DOM 元素的显示和隐藏。
+ * 只要用户拥有指定角色中的任意一个,元素就会显示,否则从 DOM 中移除。
+ *
+ * ## 主要功能
+ *
+ * - 角色验证 - 检查用户是否拥有指定角色
+ * - 多角色支持 - 支持单个角色或多个角色(满足其一即可)
+ * - DOM 控制 - 无权限时自动移除元素,而非隐藏
+ * - 响应式更新 - 角色变化时自动更新元素状态
+ *
+ * ## 使用示例
+ *
+ * ```vue
+ *
+ *
+ * 超级管理员功能
+ *
+ *
+ * 管理员功能
+ *
+ *
+ *
+ * 所有登录用户可见的内容
+ *
+ *
+ * ```
+ *
+ * ## 权限逻辑
+ *
+ * - 用户角色从 userStore.getUserInfo.roles 获取
+ * - 只要用户拥有指定角色中的任意一个,元素就会显示
+ * - 如果用户没有任何角色或不满足条件,元素将被移除
+ *
+ * ## 注意事项
+ *
+ * - 该指令会直接移除 DOM 元素,而不是使用 v-if 隐藏
+ * - 适用于基于角色的粗粒度权限控制
+ * - 如需基于具体操作的细粒度权限控制,请使用 v-auth 指令
+ *
+ * @module directives/roles
+ * @author Art Design Pro Team
+ */
+
+import { useUserStore } from '@/store/modules/user'
+import { App, Directive, DirectiveBinding } from 'vue'
+
+interface RolesBinding extends DirectiveBinding {
+ value: string | string[]
+}
+
+function checkRolePermission(el: HTMLElement, binding: RolesBinding): void {
+ const userStore = useUserStore()
+ const userRoles = userStore.getUserInfo.roles
+
+ // 如果用户角色为空或未定义,移除元素
+ if (!userRoles?.length) {
+ removeElement(el)
+ return
+ }
+
+ // 确保指令值为数组格式
+ const requiredRoles = Array.isArray(binding.value) ? binding.value : [binding.value]
+
+ // 检查用户是否具有所需角色之一
+ const hasPermission = requiredRoles.some((role: string) => userRoles.includes(role))
+
+ // 如果没有权限,安全地移除元素
+ if (!hasPermission) {
+ removeElement(el)
+ }
+}
+
+function removeElement(el: HTMLElement): void {
+ if (el.parentNode) {
+ el.parentNode.removeChild(el)
+ }
+}
+
+const rolesDirective: Directive = {
+ mounted: checkRolePermission,
+ updated: checkRolePermission
+}
+
+export function setupRolesDirective(app: App): void {
+ app.directive('roles', rolesDirective)
+}
diff --git a/adminSystem/src/directives/index.ts b/adminSystem/src/directives/index.ts
new file mode 100644
index 0000000..780464b
--- /dev/null
+++ b/adminSystem/src/directives/index.ts
@@ -0,0 +1,12 @@
+import type { App } from 'vue'
+import { setupAuthDirective } from './core/auth'
+import { setupHighlightDirective } from './business/highlight'
+import { setupRippleDirective } from './business/ripple'
+import { setupRolesDirective } from './core/roles'
+
+export function setupGlobDirectives(app: App) {
+ setupAuthDirective(app) // 权限指令
+ setupRolesDirective(app) // 角色权限指令
+ setupHighlightDirective(app) // 高亮指令
+ setupRippleDirective(app) // 水波纹指令
+}
diff --git a/adminSystem/src/enums/appEnum.ts b/adminSystem/src/enums/appEnum.ts
new file mode 100644
index 0000000..a39c278
--- /dev/null
+++ b/adminSystem/src/enums/appEnum.ts
@@ -0,0 +1,81 @@
+/**
+ * 系统级别枚举定义模块
+ *
+ * ## 主要功能
+ *
+ * - 菜单类型枚举(左侧、顶部、混合、双栏)
+ * - 主题类型枚举(亮色、暗色、自动)
+ * - 菜单主题枚举(设计、亮色、暗色)
+ * - 语言类型枚举(中文、英文)
+ * - 容器宽度枚举(全屏、固定)
+ * - 菜单宽度枚举(收起宽度)
+ *
+ * @module enums/appEnum
+ * @author Art Design Pro Team
+ */
+
+/**
+ * 菜单类型
+ */
+export enum MenuTypeEnum {
+ /** 左侧菜单 */
+ LEFT = 'left',
+ /** 顶部菜单 */
+ TOP = 'top',
+ /** 顶部+左侧菜单 */
+ TOP_LEFT = 'top-left',
+ /** 双栏菜单 */
+ DUAL_MENU = 'dual-menu'
+}
+
+/**
+ * 系统主题
+ */
+export enum SystemThemeEnum {
+ /** 暗色主题 */
+ DARK = 'dark',
+ /** 亮色主题 */
+ LIGHT = 'light',
+ /** 自动主题(跟随系统) */
+ AUTO = 'auto'
+}
+
+/**
+ * 菜单主题
+ */
+export enum MenuThemeEnum {
+ /** 暗色主题 */
+ DARK = 'dark',
+ /** 亮色主题 */
+ LIGHT = 'light',
+ /** 设计主题 */
+ DESIGN = 'design'
+}
+
+/**
+ * 菜单宽度
+ */
+export enum MenuWidth {
+ /** 收起宽度 */
+ CLOSE = '64px'
+}
+
+/**
+ * 语言类型
+ */
+export enum LanguageEnum {
+ /** 中文 */
+ ZH = 'zh',
+ /** 英文 */
+ EN = 'en'
+}
+
+/**
+ * 容器宽度
+ */
+export enum ContainerWidthEnum {
+ /** 全屏宽度 */
+ FULL = '100%',
+ /** 固定宽度 */
+ BOXED = '1200px'
+}
diff --git a/adminSystem/src/enums/formEnum.ts b/adminSystem/src/enums/formEnum.ts
new file mode 100644
index 0000000..8e9b3b4
--- /dev/null
+++ b/adminSystem/src/enums/formEnum.ts
@@ -0,0 +1,24 @@
+/**
+ * 表单相关枚举定义模块
+ *
+ * ## 主要功能
+ *
+ * - 页面模式枚举(新增、编辑)
+ * - 表格尺寸枚举(默认、紧凑、宽松)
+ *
+ * @module enums/formEnum
+ * @author Art Design Pro Team
+ */
+
+// 页面类型
+export enum PageModeEnum {
+ Add, // 新增
+ Edit // 编辑
+}
+
+// 表格大小
+export enum TableSizeEnum {
+ DEFAULT = 'default',
+ SMALL = 'small',
+ LARGE = 'large'
+}
diff --git a/adminSystem/src/env.d.ts b/adminSystem/src/env.d.ts
new file mode 100644
index 0000000..4401f21
--- /dev/null
+++ b/adminSystem/src/env.d.ts
@@ -0,0 +1,34 @@
+///
+
+declare module 'nprogress'
+
+declare module 'crypto-js'
+
+declare module 'vue-img-cutter'
+
+declare module 'file-saver'
+
+declare module 'qrcode.vue' {
+ export type Level = 'L' | 'M' | 'Q' | 'H'
+ export type RenderAs = 'canvas' | 'svg'
+ export type GradientType = 'linear' | 'radial'
+ export interface ImageSettings {
+ src: string
+ height: number
+ width: number
+ excavate: boolean
+ }
+ export interface QRCodeProps {
+ value: string
+ size?: number
+ level?: Level
+ background?: string
+ foreground?: string
+ renderAs?: RenderAs
+ }
+ const QrcodeVue: any
+ export default QrcodeVue
+}
+
+// 全局变量声明
+declare const __APP_VERSION__: string // 版本号
diff --git a/adminSystem/src/hooks/core/useAppMode.ts b/adminSystem/src/hooks/core/useAppMode.ts
new file mode 100644
index 0000000..c39cd9e
--- /dev/null
+++ b/adminSystem/src/hooks/core/useAppMode.ts
@@ -0,0 +1,45 @@
+/**
+ * useAppMode - 应用模式管理
+ *
+ * 提供应用访问模式的判断和管理功能,支持前端和后端两种权限控制模式。
+ * 根据环境变量 VITE_ACCESS_MODE 自动识别当前运行模式。
+ *
+ * ## 主要功能
+ *
+ * 1. 模式识别 - 自动识别前端模式或后端模式
+ * 2. 前端模式 - 权限由前端路由配置控制,适合小型项目或演示环境
+ * 3. 后端模式 - 权限由后端接口返回的菜单数据控制,适合企业级应用
+ * 4. 响应式状态 - 提供响应式的模式判断,方便在组件中使用
+ *
+ * @module useAppMode
+ * @author Art Design Pro Team
+ */
+
+import { computed } from 'vue'
+
+export function useAppMode() {
+ // 获取访问模式配置
+ const accessMode = import.meta.env.VITE_ACCESS_MODE
+
+ /**
+ * 是否为前端控制模式
+ * 前端模式:权限由前端路由配置控制
+ */
+ const isFrontendMode = computed(() => accessMode === 'frontend')
+ /**
+ * 是否为后端控制模式
+ * 后端模式:权限由后端接口返回的菜单数据控制
+ */
+ const isBackendMode = computed(() => accessMode === 'backend')
+
+ /**
+ * 当前应用模式
+ */
+ const currentMode = computed(() => accessMode)
+
+ return {
+ isFrontendMode,
+ isBackendMode,
+ currentMode
+ }
+}
diff --git a/adminSystem/src/hooks/core/useAuth.ts b/adminSystem/src/hooks/core/useAuth.ts
new file mode 100644
index 0000000..283b859
--- /dev/null
+++ b/adminSystem/src/hooks/core/useAuth.ts
@@ -0,0 +1,74 @@
+/**
+ * useAuth - 权限验证管理
+ *
+ * 提供统一的权限验证功能,支持前端和后端两种权限模式。
+ * 用于控制页面按钮、操作等功能的显示和访问权限。
+ *
+ * ## 主要功能
+ *
+ * 1. 权限检查 - 检查用户是否拥有指定的权限标识
+ * 2. 双模式支持 - 自动适配前端模式和后端模式的权限验证
+ * 3. 前端模式 - 从用户信息中获取按钮权限列表(如 ['add', 'edit', 'delete'])
+ * 4. 后端模式 - 从路由 meta 配置中获取权限列表(如 [{ authMark: 'add' }])
+ *
+ * ## 使用示例
+ *
+ * ```typescript
+ * const { hasAuth } = useAuth()
+ *
+ * // 检查是否有新增权限
+ * if (hasAuth('add')) {
+ * // 显示新增按钮
+ * }
+ *
+ * // 在模板中使用
+ * 编辑
+ * 删除
+ * ```
+ *
+ * @module useAuth
+ * @author Art Design Pro Team
+ */
+
+import { useRoute } from 'vue-router'
+import { storeToRefs } from 'pinia'
+import { useUserStore } from '@/store/modules/user'
+import { useAppMode } from '@/hooks/core/useAppMode'
+import type { AppRouteRecord } from '@/types/router'
+
+type AuthItem = NonNullable[number]
+
+const userStore = useUserStore()
+
+export const useAuth = () => {
+ const route = useRoute()
+ const { isFrontendMode } = useAppMode()
+ const { info } = storeToRefs(userStore)
+
+ // 前端按钮权限(例如:['add', 'edit'])
+ const frontendAuthList = info.value?.buttons ?? []
+
+ // 后端路由 meta 配置的权限列表(例如:[{ authMark: 'add' }])
+ const backendAuthList: AuthItem[] = Array.isArray(route.meta.authList)
+ ? (route.meta.authList as AuthItem[])
+ : []
+
+ /**
+ * 检查是否拥有某权限标识(前后端模式通用)
+ * @param auth 权限标识
+ * @returns 是否有权限
+ */
+ const hasAuth = (auth: string): boolean => {
+ // 前端模式
+ if (isFrontendMode.value) {
+ return frontendAuthList.includes(auth)
+ }
+
+ // 后端模式
+ return backendAuthList.some((item) => item?.authMark === auth)
+ }
+
+ return {
+ hasAuth
+ }
+}
diff --git a/adminSystem/src/hooks/core/useCeremony.ts b/adminSystem/src/hooks/core/useCeremony.ts
new file mode 100644
index 0000000..ead2630
--- /dev/null
+++ b/adminSystem/src/hooks/core/useCeremony.ts
@@ -0,0 +1,184 @@
+/**
+ * useCeremony - 节日庆祝管理
+ *
+ * 提供节日烟花效果和祝福文本展示功能,为系统增添节日氛围。
+ * 自动检测当前日期是否为节日,并在首次进入时播放烟花动画和显示祝福语。
+ *
+ * ## 主要功能
+ *
+ * 1. 节日检测 - 自动匹配当前日期与节日配置列表,支持单日和跨日期节日
+ * 2. 烟花动画 - 播放节日烟花特效,支持自定义图片和触发次数
+ * 3. 祝福文本 - 烟花结束后显示节日祝福文本
+ * 4. 状态管理 - 记录烟花播放状态,避免重复播放
+ * 5. 清理机制 - 提供清理方法,支持手动停止和重置
+ *
+ * ## 使用示例
+ *
+ * ```typescript
+ * // 在配置文件中定义节日
+ * // 单日节日
+ * {
+ * date: '2024-12-25',
+ * name: '圣诞节',
+ * image: christmasImage,
+ * count: 3 // 可选,不设置则使用默认值 3 次
+ * scrollText: 'Merry Christmas!',
+ * }
+ *
+ * // 跨日期节日
+ * {
+ * date: '2025-11-07',
+ * endDate: '2025-11-10',
+ * name: 'v3.0 测试阶段',
+ * image: '',
+ * count: 5 // 自定义烟花播放次数
+ * scrollText: '系统 v3.0 测试阶段正式开启!',
+ * }
+ * ```
+ *
+ * @module useCeremony
+ * @author Art Design Pro Team
+ */
+
+import { useTimeoutFn, useIntervalFn, useDateFormat } from '@vueuse/core'
+import { storeToRefs } from 'pinia'
+import { computed } from 'vue'
+import { useSettingStore } from '@/store/modules/setting'
+import { mittBus } from '@/utils/sys'
+import { festivalConfigList } from '@/config/modules/festival'
+
+/**
+ * 节日庆祝配置常量
+ */
+const FESTIVAL_CONFIG = {
+ /** 初始延迟(毫秒) */
+ INITIAL_DELAY: 300,
+ /** 烟花播放间隔(毫秒) */
+ FIREWORK_INTERVAL: 1000,
+ /** 文本显示延迟(毫秒) */
+ TEXT_DELAY: 2000,
+ /** 默认烟花播放次数 */
+ DEFAULT_FIREWORKS_COUNT: 3
+} as const
+
+/**
+ * 节日庆祝功能
+ * 提供节日烟花效果和祝福文本展示
+ */
+export function useCeremony() {
+ const settingStore = useSettingStore()
+ const { holidayFireworksLoaded, isShowFireworks } = storeToRefs(settingStore)
+
+ let fireworksInterval: { pause: () => void } | null = null
+
+ /**
+ * 检查日期是否在节日范围内
+ * @param currentDate 当前日期
+ * @param festivalDate 节日开始日期
+ * @param festivalEndDate 节日结束日期(可选)
+ */
+ const isDateInRange = (
+ currentDate: string,
+ festivalDate: string,
+ festivalEndDate?: string
+ ): boolean => {
+ if (!festivalEndDate) {
+ // 单日节日
+ return currentDate === festivalDate
+ }
+
+ // 跨日期节日
+ const current = new Date(currentDate)
+ const start = new Date(festivalDate)
+ const end = new Date(festivalEndDate)
+
+ return current >= start && current <= end
+ }
+
+ /**
+ * 获取当前日期对应的节日数据
+ */
+ const currentFestivalData = computed(() => {
+ const currentDate = useDateFormat(new Date(), 'YYYY-MM-DD').value
+ return festivalConfigList.find((item) => isDateInRange(currentDate, item.date, item.endDate))
+ })
+
+ /**
+ * 更新节日日期到 store
+ */
+ const updateFestivalDate = () => {
+ settingStore.setFestivalDate(currentFestivalData.value?.date || '')
+ }
+
+ /**
+ * 触发烟花效果
+ */
+ const triggerFirework = () => {
+ mittBus.emit('triggerFireworks', currentFestivalData.value?.image)
+ }
+
+ /**
+ * 完成烟花效果后显示文本
+ */
+ const showFestivalText = () => {
+ settingStore.setholidayFireworksLoaded(true)
+
+ useTimeoutFn(() => {
+ settingStore.setShowFestivalText(true)
+ updateFestivalDate()
+ }, FESTIVAL_CONFIG.TEXT_DELAY)
+ }
+
+ /**
+ * 启动烟花循环
+ */
+ const startFireworksLoop = () => {
+ let playedCount = 0
+ // 使用节日配置的播放次数,如果没有则使用默认值
+ const count = currentFestivalData.value?.count ?? FESTIVAL_CONFIG.DEFAULT_FIREWORKS_COUNT
+
+ const { pause } = useIntervalFn(() => {
+ triggerFirework()
+ playedCount++
+
+ if (playedCount >= count) {
+ pause()
+ showFestivalText()
+ }
+ }, FESTIVAL_CONFIG.FIREWORK_INTERVAL)
+
+ fireworksInterval = { pause }
+ }
+
+ /**
+ * 开启节日庆祝
+ */
+ const openFestival = () => {
+ if (!currentFestivalData.value || !isShowFireworks.value) {
+ return
+ }
+
+ const { start } = useTimeoutFn(startFireworksLoop, FESTIVAL_CONFIG.INITIAL_DELAY)
+ start()
+ }
+
+ /**
+ * 清理烟花效果
+ */
+ const cleanup = () => {
+ if (fireworksInterval) {
+ fireworksInterval.pause()
+ fireworksInterval = null
+ }
+ settingStore.setShowFestivalText(false)
+ updateFestivalDate()
+ }
+
+ return {
+ openFestival,
+ cleanup,
+ holidayFireworksLoaded,
+ currentFestivalData,
+ isShowFireworks
+ }
+}
diff --git a/adminSystem/src/hooks/core/useChart.ts b/adminSystem/src/hooks/core/useChart.ts
new file mode 100644
index 0000000..29ba1d1
--- /dev/null
+++ b/adminSystem/src/hooks/core/useChart.ts
@@ -0,0 +1,745 @@
+/**
+ * useChart - ECharts 图表管理
+ *
+ * 提供完整的 ECharts 图表生命周期管理和配置能力,简化图表开发流程。
+ * 自动处理图表初始化、更新、销毁、主题切换、响应式调整等复杂逻辑。
+ *
+ * ## 核心功能
+ *
+ * 1. 图表生命周期管理 - 自动处理初始化、更新、销毁,支持延迟加载和可见性检测
+ * 2. 主题自动适配 - 响应系统主题变化,自动更新图表样式和配色
+ * 3. 响应式调整 - 监听窗口大小、菜单展开等变化,自动调整图表尺寸
+ * 4. 空状态处理 - 优雅的空数据展示,自动显示"暂无数据"提示
+ * 5. 样式配置统一 - 提供坐标轴、图例、提示框等统一的样式配置方法
+ * 6. 性能优化 - 防抖处理、样式缓存、requestAnimationFrame 优化
+ * 7. 高级组件抽象 - useChartComponent 提供更高层次的图表组件封装
+ *
+ * ## 使用示例
+ *
+ * ```typescript
+ * // 基础用法
+ * const {
+ * chartRef,
+ * initChart,
+ * updateChart,
+ * getAxisLineStyle,
+ * getTooltipStyle
+ * } = useChart()
+ *
+ * onMounted(() => {
+ * initChart({
+ * xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed'] },
+ * yAxis: { type: 'value' },
+ * series: [{ data: [120, 200, 150], type: 'bar' }]
+ * })
+ * })
+ *
+ * // 高级用法 - 组件抽象
+ * const chart = useChartComponent({
+ * props,
+ * generateOptions: () => ({
+ * // ECharts 配置
+ * }),
+ * checkEmpty: () => data.value.length === 0,
+ * watchSources: [() => props.data]
+ * })
+ * ```
+ *
+ * @module useChart
+ * @author Art Design Pro Team
+ */
+
+import { echarts, type EChartsOption } from '@/plugins/echarts'
+import { storeToRefs } from 'pinia'
+import { useSettingStore } from '@/store/modules/setting'
+import { getCssVar } from '@/utils/ui'
+import type { BaseChartProps, ChartThemeConfig, UseChartOptions } from '@/types/component/chart'
+
+// 图表主题配置
+export const useChartOps = (): ChartThemeConfig => ({
+ /** */
+ chartHeight: '16rem',
+ /** 字体大小 */
+ fontSize: 13,
+ /** 字体颜色 */
+ fontColor: '#999',
+ /** 主题颜色 */
+ themeColor: getCssVar('--el-color-primary-light-1'),
+ /** 颜色组 */
+ colors: [
+ getCssVar('--el-color-primary-light-1'),
+ '#4ABEFF',
+ '#EDF2FF',
+ '#14DEBA',
+ '#FFAF20',
+ '#FA8A6C',
+ '#FFAF20'
+ ]
+})
+
+// 常量定义
+const RESIZE_DELAYS = [50, 100, 200, 350] as const
+const MENU_RESIZE_DELAYS = [50, 100, 200] as const
+const RESIZE_DEBOUNCE_DELAY = 100
+
+export function useChart(options: UseChartOptions = {}) {
+ const { initOptions, initDelay = 0, threshold = 0.1, autoTheme = true } = options
+
+ const settingStore = useSettingStore()
+ const { isDark, menuOpen, menuType } = storeToRefs(settingStore)
+
+ const chartRef = ref()
+ let chart: echarts.ECharts | null = null
+ let intersectionObserver: IntersectionObserver | null = null
+ let pendingOptions: EChartsOption | null = null
+ let resizeTimeoutId: number | null = null
+ let resizeFrameId: number | null = null
+ let isDestroyed = false
+ let emptyStateDiv: HTMLElement | null = null
+
+ // 清理定时器的统一方法
+ const clearTimers = () => {
+ if (resizeTimeoutId) {
+ clearTimeout(resizeTimeoutId)
+ resizeTimeoutId = null
+ }
+ if (resizeFrameId) {
+ cancelAnimationFrame(resizeFrameId)
+ resizeFrameId = null
+ }
+ }
+
+ // 使用 requestAnimationFrame 优化 resize 处理
+ const requestAnimationResize = () => {
+ if (resizeFrameId) {
+ cancelAnimationFrame(resizeFrameId)
+ }
+ resizeFrameId = requestAnimationFrame(() => {
+ handleResize()
+ resizeFrameId = null
+ })
+ }
+
+ // 防抖的resize处理(用于窗口resize事件)
+ const debouncedResize = () => {
+ if (resizeTimeoutId) {
+ clearTimeout(resizeTimeoutId)
+ }
+ resizeTimeoutId = window.setTimeout(() => {
+ requestAnimationResize()
+ resizeTimeoutId = null
+ }, RESIZE_DEBOUNCE_DELAY)
+ }
+
+ // 多延迟resize处理 - 统一方法
+ const multiDelayResize = (delays: readonly number[]) => {
+ // 立即调用一次,快速响应
+ nextTick(requestAnimationResize)
+
+ // 使用延迟时间,确保图表正确适应变化
+ delays.forEach((delay) => {
+ setTimeout(requestAnimationResize, delay)
+ })
+ }
+
+ // 收缩菜单时,重新计算图表大小(仅在图表存在时监听)
+ let menuOpenStopHandle: (() => void) | null = null
+ let menuTypeStopHandle: (() => void) | null = null
+
+ const setupMenuWatchers = () => {
+ menuOpenStopHandle = watch(menuOpen, () => multiDelayResize(RESIZE_DELAYS))
+ menuTypeStopHandle = watch(menuType, () => {
+ nextTick(requestAnimationResize)
+ setTimeout(() => multiDelayResize(MENU_RESIZE_DELAYS), 0)
+ })
+ }
+
+ const cleanupMenuWatchers = () => {
+ menuOpenStopHandle?.()
+ menuTypeStopHandle?.()
+ menuOpenStopHandle = null
+ menuTypeStopHandle = null
+ }
+
+ // 主题变化时重新设置图表选项
+ let themeStopHandle: (() => void) | null = null
+
+ const setupThemeWatcher = () => {
+ if (autoTheme) {
+ themeStopHandle = watch(isDark, () => {
+ // 更新空状态样式
+ emptyStateManager.updateStyle()
+
+ if (chart && !isDestroyed) {
+ // 使用 requestAnimationFrame 优化主题更新
+ requestAnimationFrame(() => {
+ if (chart && !isDestroyed) {
+ const currentOptions = chart.getOption()
+ if (currentOptions) {
+ updateChart(currentOptions as EChartsOption)
+ }
+ }
+ })
+ }
+ })
+ }
+ }
+
+ const cleanupThemeWatcher = () => {
+ themeStopHandle?.()
+ themeStopHandle = null
+ }
+
+ // 样式生成器 - 统一的样式配置
+ const createLineStyle = (color: string, width = 1, type?: 'solid' | 'dashed') => ({
+ color,
+ width,
+ ...(type && { type })
+ })
+
+ // 缓存样式配置以减少重复计算
+ const styleCache = {
+ axisLine: null as any,
+ splitLine: null as any,
+ axisLabel: null as any,
+ lastDarkValue: isDark.value
+ }
+
+ const clearStyleCache = () => {
+ styleCache.axisLine = null
+ styleCache.splitLine = null
+ styleCache.axisLabel = null
+ styleCache.lastDarkValue = isDark.value
+ }
+
+ // 坐标轴线样式
+ const getAxisLineStyle = (show: boolean = true) => {
+ if (styleCache.lastDarkValue !== isDark.value) {
+ clearStyleCache()
+ }
+ if (!styleCache.axisLine) {
+ styleCache.axisLine = {
+ show,
+ lineStyle: createLineStyle(isDark.value ? '#444' : '#EDEDED')
+ }
+ }
+ return styleCache.axisLine
+ }
+
+ // 分割线样式
+ const getSplitLineStyle = (show: boolean = true) => {
+ if (styleCache.lastDarkValue !== isDark.value) {
+ clearStyleCache()
+ }
+ if (!styleCache.splitLine) {
+ styleCache.splitLine = {
+ show,
+ lineStyle: createLineStyle(isDark.value ? '#444' : '#EDEDED', 1, 'dashed')
+ }
+ }
+ return styleCache.splitLine
+ }
+
+ // 坐标轴标签样式
+ const getAxisLabelStyle = (show: boolean = true) => {
+ if (styleCache.lastDarkValue !== isDark.value) {
+ clearStyleCache()
+ }
+ if (!styleCache.axisLabel) {
+ const { fontColor, fontSize } = useChartOps()
+ styleCache.axisLabel = {
+ show,
+ color: fontColor,
+ fontSize
+ }
+ }
+ return styleCache.axisLabel
+ }
+
+ // 坐标轴刻度样式(静态配置,无需缓存)
+ const getAxisTickStyle = () => ({
+ show: false
+ })
+
+ // 获取动画配置
+ const getAnimationConfig = (animationDelay: number = 50, animationDuration: number = 1500) => ({
+ animationDelay: (idx: number) => idx * animationDelay + 200,
+ animationDuration: (idx: number) => animationDuration - idx * 50,
+ animationEasing: 'quarticOut' as const
+ })
+
+ // 获取统一的 tooltip 配置
+ const getTooltipStyle = (trigger: 'item' | 'axis' = 'axis', customOptions: any = {}) => ({
+ trigger,
+ backgroundColor: isDark.value ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.9)',
+ borderColor: isDark.value ? '#333' : '#ddd',
+ borderWidth: 1,
+ textStyle: {
+ color: isDark.value ? '#fff' : '#333'
+ },
+ ...customOptions
+ })
+
+ // 获取统一的图例配置
+ const getLegendStyle = (
+ position: 'bottom' | 'top' | 'left' | 'right' = 'bottom',
+ customOptions: any = {}
+ ) => {
+ const baseConfig = {
+ textStyle: {
+ color: isDark.value ? '#fff' : '#333'
+ },
+ itemWidth: 12,
+ itemHeight: 12,
+ itemGap: 20,
+ ...customOptions
+ }
+
+ // 根据位置设置不同的配置
+ switch (position) {
+ case 'bottom':
+ return {
+ ...baseConfig,
+ bottom: 0,
+ left: 'center',
+ orient: 'horizontal',
+ icon: 'roundRect'
+ }
+ case 'top':
+ return {
+ ...baseConfig,
+ top: 0,
+ left: 'center',
+ orient: 'horizontal',
+ icon: 'roundRect'
+ }
+ case 'left':
+ return {
+ ...baseConfig,
+ left: 0,
+ top: 'center',
+ orient: 'vertical',
+ icon: 'roundRect'
+ }
+ case 'right':
+ return {
+ ...baseConfig,
+ right: 0,
+ top: 'center',
+ orient: 'vertical',
+ icon: 'roundRect'
+ }
+ default:
+ return baseConfig
+ }
+ }
+
+ // 根据图例位置计算 grid 配置
+ const getGridWithLegend = (
+ showLegend: boolean,
+ legendPosition: 'bottom' | 'top' | 'left' | 'right' = 'bottom',
+ baseGrid: any = {}
+ ) => {
+ const defaultGrid = {
+ top: 15,
+ right: 15,
+ bottom: 8,
+ left: 0,
+ containLabel: true,
+ ...baseGrid
+ }
+
+ if (!showLegend) {
+ return defaultGrid
+ }
+
+ // 根据图例位置调整 grid
+ switch (legendPosition) {
+ case 'bottom':
+ return {
+ ...defaultGrid,
+ bottom: 40
+ }
+ case 'top':
+ return {
+ ...defaultGrid,
+ top: 40
+ }
+ case 'left':
+ return {
+ ...defaultGrid,
+ left: 120
+ }
+ case 'right':
+ return {
+ ...defaultGrid,
+ right: 120
+ }
+ default:
+ return defaultGrid
+ }
+ }
+
+ // 创建IntersectionObserver
+ const createIntersectionObserver = () => {
+ if (intersectionObserver || !chartRef.value) return
+
+ intersectionObserver = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting && pendingOptions && !isDestroyed) {
+ // 使用 requestAnimationFrame 确保在下一帧初始化图表
+ requestAnimationFrame(() => {
+ if (!isDestroyed && pendingOptions) {
+ try {
+ // 元素变为可见,初始化图表
+ if (!chart) {
+ chart = echarts.init(entry.target as HTMLElement)
+ }
+
+ // 触发自定义事件,让组件处理动画逻辑
+ const event = new CustomEvent('chartVisible', {
+ detail: { options: pendingOptions }
+ })
+ entry.target.dispatchEvent(event)
+
+ pendingOptions = null
+ cleanupIntersectionObserver()
+ } catch (error) {
+ console.error('图表初始化失败:', error)
+ }
+ }
+ })
+ }
+ })
+ },
+ { threshold }
+ )
+
+ intersectionObserver.observe(chartRef.value)
+ }
+
+ // 清理IntersectionObserver
+ const cleanupIntersectionObserver = () => {
+ if (intersectionObserver) {
+ intersectionObserver.disconnect()
+ intersectionObserver = null
+ }
+ }
+
+ // 检查容器是否可见
+ const isContainerVisible = (element: HTMLElement): boolean => {
+ const rect = element.getBoundingClientRect()
+ return rect.width > 0 && rect.height > 0 && rect.top < window.innerHeight && rect.bottom > 0
+ }
+
+ // 图表初始化核心逻辑
+ const performChartInit = (options: EChartsOption) => {
+ if (!chart && chartRef.value && !isDestroyed) {
+ chart = echarts.init(chartRef.value)
+ // 图表创建后立即设置监听器
+ setupMenuWatchers()
+ setupThemeWatcher()
+ }
+ if (chart && !isDestroyed) {
+ chart.setOption(options)
+ pendingOptions = null
+ }
+ }
+
+ // 空状态管理器
+ const emptyStateManager = {
+ create: () => {
+ if (!chartRef.value || emptyStateDiv) return
+
+ emptyStateDiv = document.createElement('div')
+ emptyStateDiv.style.cssText = `
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ color: ${isDark.value ? '#555555' : '#B3B2B2'};
+ background: transparent;
+ z-index: 10;
+ `
+ emptyStateDiv.innerHTML = `暂无数据`
+
+ // 确保父容器有相对定位
+ if (
+ chartRef.value.style.position !== 'relative' &&
+ chartRef.value.style.position !== 'absolute'
+ ) {
+ chartRef.value.style.position = 'relative'
+ }
+
+ chartRef.value.appendChild(emptyStateDiv)
+ },
+
+ remove: () => {
+ if (emptyStateDiv && chartRef.value) {
+ chartRef.value.removeChild(emptyStateDiv)
+ emptyStateDiv = null
+ }
+ },
+
+ updateStyle: () => {
+ if (emptyStateDiv) {
+ emptyStateDiv.style.color = isDark.value ? '#666' : '#999'
+ }
+ }
+ }
+
+ // 初始化图表
+ const initChart = (options: EChartsOption = {}, isEmpty: boolean = false) => {
+ if (!chartRef.value || isDestroyed) return
+
+ const mergedOptions = { ...initOptions, ...options }
+
+ try {
+ if (isEmpty) {
+ // 处理空数据情况 - 显示自定义空状态div
+ if (chart) {
+ chart.clear()
+ }
+ emptyStateManager.create()
+ return
+ } else {
+ // 有数据时移除空状态div
+ emptyStateManager.remove()
+ }
+
+ if (isContainerVisible(chartRef.value)) {
+ // 容器可见,正常初始化
+ if (initDelay > 0) {
+ setTimeout(() => performChartInit(mergedOptions), initDelay)
+ } else {
+ performChartInit(mergedOptions)
+ }
+ } else {
+ // 容器不可见,保存选项并设置监听器
+ pendingOptions = mergedOptions
+ createIntersectionObserver()
+ }
+ } catch (error) {
+ console.error('图表初始化失败:', error)
+ }
+ }
+
+ // 更新图表
+ const updateChart = (options: EChartsOption) => {
+ if (isDestroyed) return
+
+ try {
+ if (!chart) {
+ // 如果图表不存在,先初始化
+ initChart(options)
+ return
+ }
+ chart.setOption(options)
+ } catch (error) {
+ console.error('图表更新失败:', error)
+ }
+ }
+
+ // 处理窗口大小变化
+ const handleResize = () => {
+ if (chart && !isDestroyed) {
+ try {
+ chart.resize()
+ } catch (error) {
+ console.error('图表resize失败:', error)
+ }
+ }
+ }
+
+ // 销毁图表
+ const destroyChart = () => {
+ isDestroyed = true
+
+ if (chart) {
+ try {
+ chart.dispose()
+ } catch (error) {
+ console.error('图表销毁失败:', error)
+ } finally {
+ chart = null
+ }
+ }
+
+ // 清理所有监听器和资源
+ cleanupMenuWatchers()
+ cleanupThemeWatcher()
+ emptyStateManager.remove()
+ cleanupIntersectionObserver()
+ clearTimers()
+ clearStyleCache()
+ pendingOptions = null
+ }
+
+ // 获取图表实例
+ const getChartInstance = () => chart
+
+ // 获取图表是否已初始化
+ const isChartInitialized = () => chart !== null
+
+ onMounted(() => {
+ window.addEventListener('resize', debouncedResize)
+ })
+
+ onBeforeUnmount(() => {
+ window.removeEventListener('resize', debouncedResize)
+ })
+
+ onUnmounted(() => {
+ destroyChart()
+ })
+
+ return {
+ isDark,
+ chartRef,
+ initChart,
+ updateChart,
+ handleResize,
+ destroyChart,
+ getChartInstance,
+ isChartInitialized,
+ emptyStateManager,
+ getAxisLineStyle,
+ getSplitLineStyle,
+ getAxisLabelStyle,
+ getAxisTickStyle,
+ getAnimationConfig,
+ getTooltipStyle,
+ getLegendStyle,
+ useChartOps,
+ getGridWithLegend
+ }
+}
+
+// 高级图表组件抽象
+interface UseChartComponentOptions {
+ /** Props响应式对象 */
+ props: T
+ /** 图表配置生成函数 */
+ generateOptions: () => EChartsOption
+ /** 空数据检查函数 */
+ checkEmpty?: () => boolean
+ /** 自定义监听的响应式数据 */
+ watchSources?: (() => any)[]
+ /** 自定义可视事件处理 */
+ onVisible?: () => void
+ /** useChart选项 */
+ chartOptions?: UseChartOptions
+}
+
+export function useChartComponent(options: UseChartComponentOptions) {
+ const {
+ props,
+ generateOptions,
+ checkEmpty,
+ watchSources = [],
+ onVisible,
+ chartOptions = {}
+ } = options
+
+ const chart = useChart(chartOptions)
+ const { chartRef, initChart, isDark, emptyStateManager } = chart
+
+ // 检查是否为空数据
+ const isEmpty = computed(() => {
+ if (props.isEmpty) return true
+ if (checkEmpty) return checkEmpty()
+ return false
+ })
+
+ // 更新图表
+ const updateChart = () => {
+ nextTick(() => {
+ if (isEmpty.value) {
+ // 处理空数据情况 - 显示自定义空状态div
+ if (chart.getChartInstance()) {
+ chart.getChartInstance()?.clear()
+ }
+ emptyStateManager.create()
+ } else {
+ // 有数据时移除空状态div并初始化图表
+ emptyStateManager.remove()
+ initChart(generateOptions())
+ }
+ })
+ }
+
+ // 处理图表进入可视区域时的逻辑
+ const handleChartVisible = () => {
+ if (onVisible) {
+ onVisible()
+ } else {
+ updateChart()
+ }
+ }
+
+ // 存储监听器停止函数
+ const stopHandles: (() => void)[] = []
+
+ // 设置数据监听
+ const setupWatchers = () => {
+ // 监听自定义数据源
+ if (watchSources.length > 0) {
+ const stopHandle = watch(watchSources, updateChart, { deep: true })
+ stopHandles.push(stopHandle)
+ }
+
+ // 监听主题变化
+ const themeStopHandle = watch(isDark, () => {
+ emptyStateManager.updateStyle()
+ updateChart()
+ })
+ stopHandles.push(themeStopHandle)
+ }
+
+ // 清理所有监听器
+ const cleanupWatchers = () => {
+ stopHandles.forEach((stop) => stop())
+ stopHandles.length = 0
+ }
+
+ // 设置生命周期
+ const setupLifecycle = () => {
+ onMounted(() => {
+ updateChart()
+
+ // 监听图表可见事件
+ if (chartRef.value) {
+ chartRef.value.addEventListener('chartVisible', handleChartVisible)
+ }
+ })
+
+ onBeforeUnmount(() => {
+ // 清理事件监听器
+ if (chartRef.value) {
+ chartRef.value.removeEventListener('chartVisible', handleChartVisible)
+ }
+ // 清理所有监听器
+ cleanupWatchers()
+ // 清理空状态div
+ emptyStateManager.remove()
+ })
+ }
+
+ // 初始化
+ setupWatchers()
+ setupLifecycle()
+
+ return {
+ ...chart,
+ isEmpty,
+ updateChart,
+ handleChartVisible
+ }
+}
diff --git a/adminSystem/src/hooks/core/useCommon.ts b/adminSystem/src/hooks/core/useCommon.ts
new file mode 100644
index 0000000..c936854
--- /dev/null
+++ b/adminSystem/src/hooks/core/useCommon.ts
@@ -0,0 +1,87 @@
+/**
+ * useCommon - 通用功能集合
+ *
+ * 提供常用的页面操作功能,包括页面刷新、滚动控制、路径获取等。
+ * 这些功能在多个页面和组件中都会用到,统一封装便于复用。
+ *
+ * ## 主要功能
+ *
+ * 1. 首页路径 - 获取系统配置的首页路径
+ * 2. 页面刷新 - 刷新当前页面内容
+ * 3. 滚动控制 - 提供多种滚动到顶部和指定位置的方法
+ * 4. 平滑滚动 - 支持平滑滚动动画效果
+ *
+ * @module useCommon
+ * @author Art Design Pro Team
+ */
+
+import { computed } from 'vue'
+import { useMenuStore } from '@/store/modules/menu'
+import { useSettingStore } from '@/store/modules/setting'
+
+export function useCommon() {
+ const menuStore = useMenuStore()
+ const settingStore = useSettingStore()
+
+ /**
+ * 首页路径
+ * 从菜单 store 中获取配置的首页路径
+ */
+ const homePath = computed(() => menuStore.getHomePath())
+
+ /**
+ * 刷新当前页面
+ * 通过切换 setting store 中的 refresh 状态触发页面重新渲染
+ */
+ const refresh = () => {
+ settingStore.reload()
+ }
+
+ /**
+ * 滚动到页面顶部
+ * 查找主内容区域并将其滚动位置重置为顶部
+ */
+ const scrollToTop = () => {
+ const scrollContainer = document.getElementById('app-main')
+ if (scrollContainer) {
+ scrollContainer.scrollTop = 0
+ }
+ }
+
+ /**
+ * 平滑滚动到页面顶部
+ * 使用 smooth 行为实现平滑滚动效果
+ */
+ const smoothScrollToTop = () => {
+ const scrollContainer = document.getElementById('app-main')
+ if (scrollContainer) {
+ scrollContainer.scrollTo({
+ top: 0,
+ behavior: 'smooth'
+ })
+ }
+ }
+
+ /**
+ * 滚动到指定位置
+ * @param top 目标滚动位置(像素)
+ * @param smooth 是否使用平滑滚动
+ */
+ const scrollTo = (top: number, smooth: boolean = false) => {
+ const scrollContainer = document.getElementById('app-main')
+ if (scrollContainer) {
+ scrollContainer.scrollTo({
+ top,
+ behavior: smooth ? 'smooth' : 'auto'
+ })
+ }
+ }
+
+ return {
+ homePath,
+ refresh,
+ scrollTo,
+ scrollToTop,
+ smoothScrollToTop
+ }
+}
diff --git a/adminSystem/src/hooks/core/useFastEnter.ts b/adminSystem/src/hooks/core/useFastEnter.ts
new file mode 100644
index 0000000..555eb65
--- /dev/null
+++ b/adminSystem/src/hooks/core/useFastEnter.ts
@@ -0,0 +1,55 @@
+/**
+ * useFastEnter - 快速入口管理
+ *
+ * 管理顶部栏的快速入口功能,提供应用列表和快速链接的配置和过滤。
+ * 支持动态启用/禁用、自定义排序、响应式宽度控制等功能。
+ *
+ * ## 主要功能
+ *
+ * 1. 应用列表管理 - 获取启用的应用列表,自动按排序权重排序
+ * 2. 快速链接管理 - 获取启用的快速链接,支持自定义排序
+ * 3. 响应式配置 - 所有配置自动响应变化,无需手动更新
+ * 4. 宽度控制 - 提供最小显示宽度配置,支持响应式布局
+ *
+ * @module useFastEnter
+ * @author Art Design Pro Team
+ */
+
+import { computed } from 'vue'
+import appConfig from '@/config'
+import type { FastEnterApplication, FastEnterQuickLink } from '@/types/config'
+
+export function useFastEnter() {
+ // 获取快速入口配置
+ const fastEnterConfig = computed(() => appConfig.fastEnter)
+
+ // 获取启用的应用列表(按排序权重排序)
+ const enabledApplications = computed(() => {
+ if (!fastEnterConfig.value?.applications) return []
+
+ return fastEnterConfig.value.applications
+ .filter((app) => app.enabled !== false)
+ .sort((a, b) => (a.order || 0) - (b.order || 0))
+ })
+
+ // 获取启用的快速链接(按排序权重排序)
+ const enabledQuickLinks = computed(() => {
+ if (!fastEnterConfig.value?.quickLinks) return []
+
+ return fastEnterConfig.value.quickLinks
+ .filter((link) => link.enabled !== false)
+ .sort((a, b) => (a.order || 0) - (b.order || 0))
+ })
+
+ // 获取最小显示宽度
+ const minWidth = computed(() => {
+ return fastEnterConfig.value?.minWidth || 1200
+ })
+
+ return {
+ fastEnterConfig,
+ enabledApplications,
+ enabledQuickLinks,
+ minWidth
+ }
+}
diff --git a/adminSystem/src/hooks/core/useHeaderBar.ts b/adminSystem/src/hooks/core/useHeaderBar.ts
new file mode 100644
index 0000000..be10712
--- /dev/null
+++ b/adminSystem/src/hooks/core/useHeaderBar.ts
@@ -0,0 +1,201 @@
+/**
+ * useHeaderBar - 顶部栏功能管理
+ *
+ * 统一管理顶部栏各个功能模块的显示状态和配置信息。
+ * 提供灵活的功能开关控制,支持动态显示/隐藏顶部栏的各个功能按钮。
+ *
+ * ## 主要功能
+ *
+ * 1. 功能开关控制 - 统一管理菜单按钮、刷新按钮、快速入口等功能的显示状态
+ * 2. 配置信息获取 - 获取各个功能模块的详细配置信息
+ * 3. 功能列表查询 - 快速获取所有启用或禁用的功能列表
+ * 4. 响应式状态 - 所有状态自动响应配置和 store 变化
+ *
+ * @module useHeaderBar
+ * @author Art Design Pro Team
+ */
+
+import { computed } from 'vue'
+import { storeToRefs } from 'pinia'
+import { useSettingStore } from '@/store/modules/setting'
+import { headerBarConfig } from '@/config/modules/headerBar'
+import { HeaderBarFeatureConfig } from '@/types'
+
+/**
+ * 顶部栏功能管理
+ * @returns 顶部栏功能相关的状态和方法
+ */
+export function useHeaderBar() {
+ const settingStore = useSettingStore()
+
+ // 获取顶部栏配置
+ const headerBarConfigRef = computed(() => headerBarConfig)
+
+ // 从store中获取相关状态
+ const { showMenuButton, showFastEnter, showRefreshButton, showCrumbs, showLanguage } =
+ storeToRefs(settingStore)
+
+ /**
+ * 检查特定功能是否启用
+ * @param feature 功能名称
+ * @returns 是否启用
+ */
+ const isFeatureEnabled = (feature: keyof HeaderBarFeatureConfig): boolean => {
+ return headerBarConfigRef.value[feature]?.enabled ?? false
+ }
+
+ /**
+ * 获取功能配置信息
+ * @param feature 功能名称
+ * @returns 功能配置信息
+ */
+ const getFeatureConfig = (feature: keyof HeaderBarFeatureConfig) => {
+ return headerBarConfigRef.value[feature]
+ }
+
+ // 检查菜单按钮是否显示
+ const shouldShowMenuButton = computed(() => {
+ return isFeatureEnabled('menuButton') && showMenuButton.value
+ })
+
+ // 检查刷新按钮是否显示
+ const shouldShowRefreshButton = computed(() => {
+ return isFeatureEnabled('refreshButton') && showRefreshButton.value
+ })
+
+ // 检查快速入口是否显示
+ const shouldShowFastEnter = computed(() => {
+ return isFeatureEnabled('fastEnter') && showFastEnter.value
+ })
+
+ // 检查面包屑是否显示
+ const shouldShowBreadcrumb = computed(() => {
+ return isFeatureEnabled('breadcrumb') && showCrumbs.value
+ })
+
+ // 检查全局搜索是否显示
+ const shouldShowGlobalSearch = computed(() => {
+ return isFeatureEnabled('globalSearch')
+ })
+
+ // 检查全屏按钮是否显示
+ const shouldShowFullscreen = computed(() => {
+ return isFeatureEnabled('fullscreen')
+ })
+
+ // 检查通知中心是否显示
+ const shouldShowNotification = computed(() => {
+ return isFeatureEnabled('notification')
+ })
+
+ // 检查聊天功能是否显示
+ const shouldShowChat = computed(() => {
+ return isFeatureEnabled('chat')
+ })
+
+ // 检查语言切换是否显示
+ const shouldShowLanguage = computed(() => {
+ return isFeatureEnabled('language') && showLanguage.value
+ })
+
+ // 检查设置面板是否显示
+ const shouldShowSettings = computed(() => {
+ return isFeatureEnabled('settings')
+ })
+
+ // 检查主题切换是否显示
+ const shouldShowThemeToggle = computed(() => {
+ return isFeatureEnabled('themeToggle')
+ })
+
+ // 获取快速入口的最小宽度
+ const fastEnterMinWidth = computed(() => {
+ const config = getFeatureConfig('fastEnter')
+ return (config as any)?.minWidth || 1200
+ })
+
+ /**
+ * 检查功能是否启用(别名)
+ * @param feature 功能名称
+ * @returns 是否启用
+ */
+ const isFeatureActive = (feature: keyof HeaderBarFeatureConfig): boolean => {
+ return isFeatureEnabled(feature)
+ }
+
+ /**
+ * 获取功能配置(别名)
+ * @param feature 功能名称
+ * @returns 功能配置
+ */
+ const getFeatureInfo = (feature: keyof HeaderBarFeatureConfig) => {
+ return getFeatureConfig(feature)
+ }
+
+ /**
+ * 获取所有启用的功能列表
+ * @returns 启用的功能名称数组
+ */
+ const getEnabledFeatures = (): (keyof HeaderBarFeatureConfig)[] => {
+ return Object.keys(headerBarConfigRef.value).filter(
+ (key) => headerBarConfigRef.value[key as keyof HeaderBarFeatureConfig]?.enabled
+ ) as (keyof HeaderBarFeatureConfig)[]
+ }
+
+ /**
+ * 获取所有禁用的功能列表
+ * @returns 禁用的功能名称数组
+ */
+ const getDisabledFeatures = (): (keyof HeaderBarFeatureConfig)[] => {
+ return Object.keys(headerBarConfigRef.value).filter(
+ (key) => !headerBarConfigRef.value[key as keyof HeaderBarFeatureConfig]?.enabled
+ ) as (keyof HeaderBarFeatureConfig)[]
+ }
+
+ /**
+ * 获取所有启用的功能(别名)
+ * @returns 启用的功能列表
+ */
+ const getActiveFeatures = () => {
+ return getEnabledFeatures()
+ }
+
+ /**
+ * 获取所有禁用的功能(别名)
+ * @returns 禁用的功能列表
+ */
+ const getInactiveFeatures = () => {
+ return getDisabledFeatures()
+ }
+
+ return {
+ // 配置
+ headerBarConfig: headerBarConfigRef,
+
+ // 显示状态计算属性
+ shouldShowMenuButton, // 是否显示菜单按钮
+ shouldShowRefreshButton, // 是否显示刷新按钮
+ shouldShowFastEnter, // 是否显示快速入口
+ shouldShowBreadcrumb, // 是否显示面包屑
+ shouldShowGlobalSearch, // 是否显示全局搜索
+ shouldShowFullscreen, // 是否显示全屏按钮
+ shouldShowNotification, // 是否显示通知中心
+ shouldShowChat, // 是否显示聊天功能
+ shouldShowLanguage, // 是否显示语言切换
+ shouldShowSettings, // 是否显示设置面板
+ shouldShowThemeToggle, // 是否显示主题切换
+
+ // 配置相关
+ fastEnterMinWidth, // 快速入口最小宽度
+
+ // 方法
+ isFeatureEnabled, // 检查功能是否启用
+ isFeatureActive, // 检查功能是否启用(别名)
+ getFeatureConfig, // 获取功能配置
+ getFeatureInfo, // 获取功能配置(别名)
+ getEnabledFeatures, // 获取所有启用的功能
+ getDisabledFeatures, // 获取所有禁用的功能
+ getActiveFeatures, // 获取所有启用的功能(别名)
+ getInactiveFeatures // 获取所有禁用的功能(别名)
+ }
+}
diff --git a/adminSystem/src/hooks/core/useLayoutHeight.ts b/adminSystem/src/hooks/core/useLayoutHeight.ts
new file mode 100644
index 0000000..4b1171a
--- /dev/null
+++ b/adminSystem/src/hooks/core/useLayoutHeight.ts
@@ -0,0 +1,148 @@
+/**
+ * useLayoutHeight - 页面布局高度管理
+ *
+ * 自动计算和管理页面内容区域的高度,确保内容区域能够正确填充剩余空间。
+ * 监听头部元素高度变化,动态调整内容区域高度,避免出现滚动条或布局错乱。
+ *
+ * ## 主要功能
+ *
+ * 1. 动态高度计算 - 根据头部元素高度自动计算内容区域高度
+ * 2. 响应式监听 - 自动监听元素尺寸变化并更新高度
+ * 3. CSS 变量同步 - 自动更新 CSS 变量,方便全局使用
+ * 4. 灵活配置 - 支持自定义间距、CSS 变量名等
+ * 5. 自动查找模式 - 提供通过 ID 自动查找元素的便捷方式
+ *
+ * @module useLayoutHeight
+ * @author Art Design Pro Team
+ */
+
+import { ref, computed, watch, onMounted } from 'vue'
+import { useElementSize } from '@vueuse/core'
+
+/**
+ * 页面容器高度配置
+ */
+interface LayoutHeightOptions {
+ /** 额外的间距(默认 15px) */
+ extraSpacing?: number
+ /** 是否自动更新 CSS 变量(默认 true) */
+ updateCssVar?: boolean
+ /** CSS 变量名称(默认 '--art-full-height') */
+ cssVarName?: string
+}
+
+export function useLayoutHeight(options: LayoutHeightOptions = {}) {
+ const { extraSpacing = 15, updateCssVar = true, cssVarName = '--art-full-height' } = options
+
+ // 元素引用
+ const headerRef = ref()
+ const contentHeaderRef = ref()
+
+ // 使用 VueUse 自动监听元素尺寸变化
+ const { height: headerHeight } = useElementSize(headerRef)
+ const { height: contentHeaderHeight } = useElementSize(contentHeaderRef)
+
+ // 计算容器最小高度(响应式)
+ const containerMinHeight = computed(() => {
+ const totalHeight = headerHeight.value + contentHeaderHeight.value + extraSpacing
+ return `calc(100vh - ${totalHeight}px)`
+ })
+
+ if (updateCssVar) {
+ watch(
+ containerMinHeight,
+ (newHeight) => {
+ requestAnimationFrame(() => {
+ document.documentElement.style.setProperty(cssVarName, newHeight)
+ })
+ },
+ { immediate: true }
+ )
+ }
+
+ return {
+ /** 容器最小高度(响应式) */
+ containerMinHeight,
+ /** 头部元素引用 */
+ headerRef,
+ /** 内容头部元素引用 */
+ contentHeaderRef,
+ /** 头部高度(响应式) */
+ headerHeight,
+ /** 内容头部高度(响应式) */
+ contentHeaderHeight
+ }
+}
+
+/**
+ * 通过 ID 自动查找元素的布局高度管理
+ * 适用于无法直接获取元素引用的场景
+ *
+ * @param headerIds 头部元素的 ID 数组
+ * @param options 配置选项
+ *
+ * ```
+ */
+export function useAutoLayoutHeight(
+ headerIds: string[] = ['app-header', 'app-content-header'],
+ options: LayoutHeightOptions = {}
+) {
+ const { extraSpacing = 15, updateCssVar = true, cssVarName = '--art-full-height' } = options
+
+ // 创建元素引用
+ const headerRef = ref()
+ const contentHeaderRef = ref()
+
+ // 使用 VueUse 自动监听元素尺寸变化
+ const { height: headerHeight } = useElementSize(headerRef)
+ const { height: contentHeaderHeight } = useElementSize(contentHeaderRef)
+
+ // 计算容器最小高度(响应式)
+ const containerMinHeight = computed(() => {
+ const totalHeight = headerHeight.value + contentHeaderHeight.value + extraSpacing
+ return `calc(100vh - ${totalHeight}px)`
+ })
+
+ if (updateCssVar) {
+ watch(
+ containerMinHeight,
+ (newHeight) => {
+ requestAnimationFrame(() => {
+ document.documentElement.style.setProperty(cssVarName, newHeight)
+ })
+ },
+ { immediate: true }
+ )
+ }
+
+ // 在 DOM 挂载后查找元素
+ onMounted(() => {
+ if (typeof document !== 'undefined') {
+ // 使用 nextTick 确保 DOM 完全渲染
+ requestAnimationFrame(() => {
+ const header = document.getElementById(headerIds[0])
+ const contentHeader = document.getElementById(headerIds[1])
+
+ if (header) {
+ headerRef.value = header
+ }
+ if (contentHeader) {
+ contentHeaderRef.value = contentHeader
+ }
+ })
+ }
+ })
+
+ return {
+ /** 容器最小高度(响应式) */
+ containerMinHeight,
+ /** 头部元素引用 */
+ headerRef,
+ /** 内容头部元素引用 */
+ contentHeaderRef,
+ /** 头部高度(响应式) */
+ headerHeight,
+ /** 内容头部高度(响应式) */
+ contentHeaderHeight
+ }
+}
diff --git a/adminSystem/src/hooks/core/useTable.ts b/adminSystem/src/hooks/core/useTable.ts
new file mode 100644
index 0000000..afe9ebe
--- /dev/null
+++ b/adminSystem/src/hooks/core/useTable.ts
@@ -0,0 +1,736 @@
+/**
+ * useTable - 企业级表格数据管理方案
+ *
+ * 功能完整的表格数据管理解决方案,专为后台管理系统设计。
+ * 封装了表格开发中的所有常见需求,让你专注于业务逻辑。
+ *
+ * ## 主要功能
+ *
+ * 1. 数据管理 - 自动处理 API 请求、响应转换、加载状态和错误处理
+ * 2. 分页控制 - 自动同步分页状态、移动端适配、智能页码边界处理
+ * 3. 搜索功能 - 防抖搜索优化、参数管理、一键重置、参数过滤
+ * 4. 缓存系统 - 智能请求缓存、多种清理策略、自动过期管理、统计信息
+ * 5. 刷新策略 - 提供 5 种刷新方法适配不同业务场景(新增/更新/删除/手动/定时)
+ * 6. 列配置管理 - 动态显示/隐藏列、列排序、配置持久化、批量操作(可选)
+ *
+ * @module useTable
+ * @author Art Design Pro Team
+ */
+
+import { ref, reactive, computed, onMounted, onUnmounted, nextTick, readonly } from 'vue'
+import { useWindowSize } from '@vueuse/core'
+import { useTableColumns } from './useTableColumns'
+import type { ColumnOption } from '@/types/component'
+import {
+ TableCache,
+ CacheInvalidationStrategy,
+ type ApiResponse
+} from '../../utils/table/tableCache'
+import {
+ type TableError,
+ defaultResponseAdapter,
+ extractTableData,
+ updatePaginationFromResponse,
+ createSmartDebounce,
+ createErrorHandler
+} from '../../utils/table/tableUtils'
+import { tableConfig } from '../../utils/table/tableConfig'
+
+// 类型推导工具类型
+type InferApiParams = T extends (params: infer P) => any ? P : never
+type InferApiResponse = T extends (params: any) => Promise ? R : never
+type InferRecordType = T extends Api.Common.PaginatedResponse ? U : never
+
+// 优化的配置接口 - 支持自动类型推导
+export interface UseTableConfig<
+ TApiFn extends (params: any) => Promise = (params: any) => Promise,
+ TRecord = InferRecordType>,
+ TParams = InferApiParams,
+ TResponse = InferApiResponse
+> {
+ // 核心配置
+ core: {
+ /** API 请求函数 */
+ apiFn: TApiFn
+ /** 默认请求参数 */
+ apiParams?: Partial
+ /** 排除 apiParams 中的属性 */
+ excludeParams?: string[]
+ /** 是否立即加载数据 */
+ immediate?: boolean
+ /** 列配置工厂函数 */
+ columnsFactory?: () => ColumnOption[]
+ /** 自定义分页字段映射 */
+ paginationKey?: {
+ /** 当前页码字段名,默认为 'current' */
+ current?: string
+ /** 每页条数字段名,默认为 'size' */
+ size?: string
+ }
+ }
+
+ // 数据处理
+ transform?: {
+ /** 数据转换函数 */
+ dataTransformer?: (data: TRecord[]) => TRecord[]
+ /** 响应数据适配器 */
+ responseAdapter?: (response: TResponse) => ApiResponse
+ }
+
+ // 性能优化
+ performance?: {
+ /** 是否启用缓存 */
+ enableCache?: boolean
+ /** 缓存时间(毫秒) */
+ cacheTime?: number
+ /** 防抖延迟时间(毫秒) */
+ debounceTime?: number
+ /** 最大缓存条数限制 */
+ maxCacheSize?: number
+ }
+
+ // 生命周期钩子
+ hooks?: {
+ /** 数据加载成功回调(仅网络请求成功时触发) */
+ onSuccess?: (data: TRecord[], response: ApiResponse) => void
+ /** 错误处理回调 */
+ onError?: (error: TableError) => void
+ /** 缓存命中回调(从缓存获取数据时触发) */
+ onCacheHit?: (data: TRecord[], response: ApiResponse) => void
+ /** 加载状态变化回调 */
+ onLoading?: (loading: boolean) => void
+ /** 重置表单回调函数 */
+ resetFormCallback?: () => void
+ }
+
+ // 调试配置
+ debug?: {
+ /** 是否启用日志输出 */
+ enableLog?: boolean
+ /** 日志级别 */
+ logLevel?: 'info' | 'warn' | 'error'
+ }
+}
+
+export function useTable Promise>(
+ config: UseTableConfig
+) {
+ return useTableImpl(config)
+}
+
+/**
+ * useTable 的核心实现 - 强大的表格数据管理 Hook
+ *
+ * 提供完整的表格解决方案,包括:
+ * - 数据获取与缓存
+ * - 分页控制
+ * - 搜索功能
+ * - 智能刷新策略
+ * - 错误处理
+ * - 列配置管理
+ */
+function useTableImpl Promise>(
+ config: UseTableConfig
+) {
+ type TRecord = InferRecordType>
+ type TParams = InferApiParams
+ const {
+ core: {
+ apiFn,
+ apiParams = {} as Partial,
+ excludeParams = [],
+ immediate = true,
+ columnsFactory,
+ paginationKey
+ },
+ transform: { dataTransformer, responseAdapter = defaultResponseAdapter } = {},
+ performance: {
+ enableCache = false,
+ cacheTime = 5 * 60 * 1000,
+ debounceTime = 300,
+ maxCacheSize = 50
+ } = {},
+ hooks: { onSuccess, onError, onCacheHit, resetFormCallback } = {},
+ debug: { enableLog = false } = {}
+ } = config
+
+ // 分页字段名配置:优先使用传入的配置,否则使用全局配置
+ const pageKey = paginationKey?.current || tableConfig.paginationKey.current
+ const sizeKey = paginationKey?.size || tableConfig.paginationKey.size
+
+ // 响应式触发器,用于手动更新缓存统计信息
+ const cacheUpdateTrigger = ref(0)
+
+ // 日志工具函数
+ const logger = {
+ log: (message: string, ...args: unknown[]) => {
+ if (enableLog) {
+ console.log(`[useTable] ${message}`, ...args)
+ }
+ },
+ warn: (message: string, ...args: unknown[]) => {
+ if (enableLog) {
+ console.warn(`[useTable] ${message}`, ...args)
+ }
+ },
+ error: (message: string, ...args: unknown[]) => {
+ if (enableLog) {
+ console.error(`[useTable] ${message}`, ...args)
+ }
+ }
+ }
+
+ // 缓存实例
+ const cache = enableCache ? new TableCache(cacheTime, maxCacheSize, enableLog) : null
+
+ // 加载状态机
+ type LoadingState = 'idle' | 'loading' | 'success' | 'error'
+ const loadingState = ref('idle')
+ const loading = computed(() => loadingState.value === 'loading')
+
+ // 错误状态
+ const error = ref(null)
+
+ // 表格数据
+ const data = ref([])
+
+ // 请求取消控制器
+ let abortController: AbortController | null = null
+
+ // 缓存清理定时器
+ let cacheCleanupTimer: NodeJS.Timeout | null = null
+
+ // 搜索参数
+ const searchParams = reactive(
+ Object.assign(
+ {
+ [pageKey]: 1,
+ [sizeKey]: 10
+ },
+ apiParams || {}
+ ) as TParams
+ )
+
+ // 分页配置
+ const pagination = reactive({
+ current: ((searchParams as Record)[pageKey] as number) || 1,
+ size: ((searchParams as Record)[sizeKey] as number) || 10,
+ total: 0
+ })
+
+ // 移动端分页 (响应式)
+ const { width } = useWindowSize()
+ const mobilePagination = computed(() => ({
+ ...pagination,
+ small: width.value < 768
+ }))
+
+ // 列配置
+ const columnConfig = columnsFactory ? useTableColumns(columnsFactory) : null
+ const columns = columnConfig?.columns
+ const columnChecks = columnConfig?.columnChecks
+
+ // 是否有数据
+ const hasData = computed(() => data.value.length > 0)
+
+ // 缓存统计信息
+ const cacheInfo = computed(() => {
+ // 依赖触发器,确保缓存变化时重新计算
+ void cacheUpdateTrigger.value
+ if (!cache) return { total: 0, size: '0KB', hitRate: '0 avg hits' }
+ return cache.getStats()
+ })
+
+ // 错误处理函数
+ const handleError = createErrorHandler(onError, enableLog)
+
+ // 清理缓存,根据不同的业务场景选择性地清理缓存
+ const clearCache = (strategy: CacheInvalidationStrategy, context?: string): void => {
+ if (!cache) return
+
+ let clearedCount = 0
+
+ switch (strategy) {
+ case CacheInvalidationStrategy.CLEAR_ALL:
+ cache.clear()
+ logger.log(`清空所有缓存 - ${context || ''}`)
+ break
+
+ case CacheInvalidationStrategy.CLEAR_CURRENT:
+ clearedCount = cache.clearCurrentSearch(searchParams)
+ logger.log(`清空当前搜索缓存 ${clearedCount} 条 - ${context || ''}`)
+ break
+
+ case CacheInvalidationStrategy.CLEAR_PAGINATION:
+ clearedCount = cache.clearPagination()
+ logger.log(`清空分页缓存 ${clearedCount} 条 - ${context || ''}`)
+ break
+
+ case CacheInvalidationStrategy.KEEP_ALL:
+ default:
+ logger.log(`保持缓存不变 - ${context || ''}`)
+ break
+ }
+ // 手动触发缓存状态更新
+ cacheUpdateTrigger.value++
+ }
+
+ // 获取数据的核心方法
+ const fetchData = async (
+ params?: Partial,
+ useCache = enableCache
+ ): Promise> => {
+ // 取消上一个请求
+ if (abortController) {
+ abortController.abort()
+ }
+
+ // 创建新的取消控制器
+ const currentController = new AbortController()
+ abortController = currentController
+
+ // 状态机:进入 loading 状态
+ loadingState.value = 'loading'
+ error.value = null
+
+ try {
+ let requestParams = Object.assign(
+ {},
+ searchParams,
+ {
+ [pageKey]: pagination.current,
+ [sizeKey]: pagination.size
+ },
+ params || {}
+ ) as TParams
+
+ // 剔除不需要的参数
+ if (excludeParams.length > 0) {
+ const filteredParams = { ...requestParams }
+ excludeParams.forEach((key) => {
+ delete (filteredParams as Record)[key]
+ })
+ requestParams = filteredParams as TParams
+ }
+
+ // 检查缓存
+ if (useCache && cache) {
+ const cachedItem = cache.get(requestParams)
+ if (cachedItem) {
+ data.value = cachedItem.data
+ updatePaginationFromResponse(pagination, cachedItem.response)
+
+ // 修复:避免重复设置相同的值,防止响应式循环更新
+ const paramsRecord = searchParams as Record
+ if (paramsRecord[pageKey] !== pagination.current) {
+ paramsRecord[pageKey] = pagination.current
+ }
+ if (paramsRecord[sizeKey] !== pagination.size) {
+ paramsRecord[sizeKey] = pagination.size
+ }
+
+ // 状态机:缓存命中,进入 success 状态
+ loadingState.value = 'success'
+
+ // 缓存命中时触发专门的回调,而不是 onSuccess
+ if (onCacheHit) {
+ onCacheHit(cachedItem.data, cachedItem.response)
+ }
+
+ logger.log(`缓存命中`)
+ return cachedItem.response
+ }
+ }
+
+ const response = await apiFn(requestParams)
+
+ // 检查请求是否被取消
+ if (currentController.signal.aborted) {
+ throw new Error('请求已取消')
+ }
+
+ // 使用响应适配器转换为标准格式
+ const standardResponse = responseAdapter(response)
+
+ // 处理响应数据
+ let tableData = extractTableData(standardResponse)
+
+ // 应用数据转换函数
+ if (dataTransformer) {
+ tableData = dataTransformer(tableData)
+ }
+
+ // 更新状态
+ data.value = tableData
+ updatePaginationFromResponse(pagination, standardResponse)
+
+ // 修复:避免重复设置相同的值,防止响应式循环更新
+ const paramsRecord = searchParams as Record
+ if (paramsRecord[pageKey] !== pagination.current) {
+ paramsRecord[pageKey] = pagination.current
+ }
+ if (paramsRecord[sizeKey] !== pagination.size) {
+ paramsRecord[sizeKey] = pagination.size
+ }
+
+ // 缓存数据
+ if (useCache && cache) {
+ cache.set(requestParams, tableData, standardResponse)
+ // 手动触发缓存状态更新
+ cacheUpdateTrigger.value++
+ logger.log(`数据已缓存`)
+ }
+
+ // 状态机:请求成功,进入 success 状态
+ loadingState.value = 'success'
+
+ // 成功回调
+ if (onSuccess) {
+ onSuccess(tableData, standardResponse)
+ }
+
+ return standardResponse
+ } catch (err) {
+ if (err instanceof Error && err.message === '请求已取消') {
+ // 请求被取消,回到 idle 状态
+ loadingState.value = 'idle'
+ return { records: [], total: 0, current: 1, size: 10 }
+ }
+
+ // 状态机:请求失败,进入 error 状态
+ loadingState.value = 'error'
+ data.value = []
+ const tableError = handleError(err, '获取表格数据失败')
+ throw tableError
+ } finally {
+ // 只有当前控制器是活跃的才清空
+ if (abortController === currentController) {
+ abortController = null
+ }
+ }
+ }
+
+ // 获取数据 (保持当前页)
+ const getData = async (params?: Partial): Promise | void> => {
+ try {
+ return await fetchData(params)
+ } catch {
+ // 错误已在 fetchData 中处理
+ return Promise.resolve()
+ }
+ }
+
+ // 分页获取数据 (重置到第一页) - 专门用于搜索场景
+ const getDataByPage = async (params?: Partial): Promise | void> => {
+ pagination.current = 1
+ ;(searchParams as Record)[pageKey] = 1
+
+ // 搜索时清空当前搜索条件的缓存,确保获取最新数据
+ clearCache(CacheInvalidationStrategy.CLEAR_CURRENT, '搜索数据')
+
+ try {
+ return await fetchData(params, false) // 搜索时不使用缓存
+ } catch {
+ // 错误已在 fetchData 中处理
+ return Promise.resolve()
+ }
+ }
+
+ // 智能防抖搜索函数
+ const debouncedGetDataByPage = createSmartDebounce(getDataByPage, debounceTime)
+
+ // 重置搜索参数
+ const resetSearchParams = async (): Promise => {
+ // 取消防抖的搜索
+ debouncedGetDataByPage.cancel()
+
+ // 保存分页相关的默认值
+ const paramsRecord = searchParams as Record
+ const defaultPagination = {
+ [pageKey]: 1,
+ [sizeKey]: (paramsRecord[sizeKey] as number) || 10
+ }
+
+ // 清空所有搜索参数
+ Object.keys(searchParams).forEach((key) => {
+ delete paramsRecord[key]
+ })
+
+ // 重新设置默认参数
+ Object.assign(searchParams, apiParams || {}, defaultPagination)
+
+ // 重置分页
+ pagination.current = 1
+ pagination.size = defaultPagination[sizeKey] as number
+
+ // 清空错误状态
+ error.value = null
+
+ // 清空缓存
+ clearCache(CacheInvalidationStrategy.CLEAR_ALL, '重置搜索')
+
+ // 重新获取数据
+ await getData()
+
+ // 执行重置回调
+ if (resetFormCallback) {
+ await nextTick()
+ resetFormCallback()
+ }
+ }
+
+ // 防重复调用的标志
+ let isCurrentChanging = false
+
+ // 处理分页大小变化
+ const handleSizeChange = async (newSize: number): Promise => {
+ if (newSize <= 0) return
+
+ debouncedGetDataByPage.cancel()
+
+ const paramsRecord = searchParams as Record
+ pagination.size = newSize
+ pagination.current = 1
+ paramsRecord[sizeKey] = newSize
+ paramsRecord[pageKey] = 1
+
+ clearCache(CacheInvalidationStrategy.CLEAR_CURRENT, '分页大小变化')
+
+ await getData()
+ }
+
+ // 处理当前页变化
+ const handleCurrentChange = async (newCurrent: number): Promise => {
+ if (newCurrent <= 0) return
+
+ // 修复:防止重复调用
+ if (isCurrentChanging) {
+ return
+ }
+
+ // 修复:如果当前页没有变化,不需要重新请求
+ if (pagination.current === newCurrent) {
+ logger.log('分页页码未变化,跳过请求')
+ return
+ }
+
+ try {
+ isCurrentChanging = true
+
+ // 修复:只更新必要的状态
+ const paramsRecord = searchParams as Record
+ pagination.current = newCurrent
+ // 只有当 searchParams 的分页字段与新值不同时才更新
+ if (paramsRecord[pageKey] !== newCurrent) {
+ paramsRecord[pageKey] = newCurrent
+ }
+
+ await getData()
+ } finally {
+ isCurrentChanging = false
+ }
+ }
+
+ // 针对不同业务场景的刷新方法
+
+ // 新增后刷新:回到第一页并清空分页缓存(适用于新增数据后)
+ const refreshCreate = async (): Promise => {
+ debouncedGetDataByPage.cancel()
+ pagination.current = 1
+ ;(searchParams as Record)[pageKey] = 1
+ clearCache(CacheInvalidationStrategy.CLEAR_PAGINATION, '新增数据')
+ await getData()
+ }
+
+ // 更新后刷新:保持当前页,仅清空当前搜索缓存(适用于更新数据后)
+ const refreshUpdate = async (): Promise => {
+ clearCache(CacheInvalidationStrategy.CLEAR_CURRENT, '编辑数据')
+ await getData()
+ }
+
+ // 删除后刷新:智能处理页码,避免空页面(适用于删除数据后)
+ const refreshRemove = async (): Promise => {
+ const { current } = pagination
+
+ // 清除缓存并获取最新数据
+ clearCache(CacheInvalidationStrategy.CLEAR_CURRENT, '删除数据')
+ await getData()
+
+ // 如果当前页为空且不是第一页,回到上一页
+ if (data.value.length === 0 && current > 1) {
+ pagination.current = current - 1
+ ;(searchParams as Record)[pageKey] = current - 1
+ await getData()
+ }
+ }
+
+ // 全量刷新:清空所有缓存,重新获取数据(适用于手动刷新按钮)
+ const refreshData = async (): Promise => {
+ debouncedGetDataByPage.cancel()
+ clearCache(CacheInvalidationStrategy.CLEAR_ALL, '手动刷新')
+ await getData()
+ }
+
+ // 轻量刷新:仅清空当前搜索条件的缓存,保持分页状态(适用于定时刷新)
+ const refreshSoft = async (): Promise => {
+ clearCache(CacheInvalidationStrategy.CLEAR_CURRENT, '软刷新')
+ await getData()
+ }
+
+ // 取消当前请求
+ const cancelRequest = (): void => {
+ if (abortController) {
+ abortController.abort()
+ }
+ debouncedGetDataByPage.cancel()
+ }
+
+ // 清空数据
+ const clearData = (): void => {
+ data.value = []
+ error.value = null
+ clearCache(CacheInvalidationStrategy.CLEAR_ALL, '清空数据')
+ }
+
+ // 清理已过期的缓存条目,释放内存空间
+ const clearExpiredCache = (): number => {
+ if (!cache) return 0
+ const cleanedCount = cache.cleanupExpired()
+ if (cleanedCount > 0) {
+ // 手动触发缓存状态更新
+ cacheUpdateTrigger.value++
+ }
+ return cleanedCount
+ }
+
+ // 设置定期清理过期缓存
+ if (enableCache && cache) {
+ cacheCleanupTimer = setInterval(() => {
+ const cleanedCount = cache.cleanupExpired()
+ if (cleanedCount > 0) {
+ logger.log(`自动清理 ${cleanedCount} 条过期缓存`)
+ // 手动触发缓存状态更新
+ cacheUpdateTrigger.value++
+ }
+ }, cacheTime / 2) // 每半个缓存周期清理一次
+ }
+
+ // 挂载时自动加载数据
+ if (immediate) {
+ onMounted(async () => {
+ await getData()
+ })
+ }
+
+ // 组件卸载时彻底清理
+ onUnmounted(() => {
+ cancelRequest()
+ if (cache) {
+ cache.clear()
+ }
+ if (cacheCleanupTimer) {
+ clearInterval(cacheCleanupTimer)
+ }
+ })
+
+ // 优化的返回值结构
+ return {
+ // 数据相关
+ /** 表格数据 */
+ data,
+ /** 数据加载状态 */
+ loading: readonly(loading),
+ /** 错误状态 */
+ error: readonly(error),
+ /** 数据是否为空 */
+ isEmpty: computed(() => data.value.length === 0),
+ /** 是否有数据 */
+ hasData,
+
+ // 分页相关
+ /** 分页状态信息 */
+ pagination: readonly(pagination),
+ /** 移动端分页配置 */
+ paginationMobile: mobilePagination,
+ /** 页面大小变化处理 */
+ handleSizeChange,
+ /** 当前页变化处理 */
+ handleCurrentChange,
+
+ // 搜索相关 - 统一前缀
+ /** 搜索参数 */
+ searchParams,
+ /** 重置搜索参数 */
+ resetSearchParams,
+
+ // 数据操作 - 更明确的操作意图
+ /** 加载数据 */
+ fetchData: getData,
+ /** 获取数据 */
+ getData: getDataByPage,
+ /** 获取数据(防抖) */
+ getDataDebounced: debouncedGetDataByPage,
+ /** 清空数据 */
+ clearData,
+
+ // 刷新策略
+ /** 全量刷新:清空所有缓存,重新获取数据(适用于手动刷新按钮) */
+ refreshData,
+ /** 轻量刷新:仅清空当前搜索条件的缓存,保持分页状态(适用于定时刷新) */
+ refreshSoft,
+ /** 新增后刷新:回到第一页并清空分页缓存(适用于新增数据后) */
+ refreshCreate,
+ /** 更新后刷新:保持当前页,仅清空当前搜索缓存(适用于更新数据后) */
+ refreshUpdate,
+ /** 删除后刷新:智能处理页码,避免空页面(适用于删除数据后) */
+ refreshRemove,
+
+ // 缓存控制
+ /** 缓存统计信息 */
+ cacheInfo,
+ /** 清除缓存,根据不同的业务场景选择性地清理缓存: */
+ clearCache,
+ // 支持4种清理策略
+ // clearCache(CacheInvalidationStrategy.CLEAR_ALL, '手动刷新') // 清空所有缓存
+ // clearCache(CacheInvalidationStrategy.CLEAR_CURRENT, '搜索数据') // 只清空当前搜索条件的缓存
+ // clearCache(CacheInvalidationStrategy.CLEAR_PAGINATION, '新增数据') // 清空分页相关缓存
+ // clearCache(CacheInvalidationStrategy.KEEP_ALL, '保持缓存') // 不清理任何缓存
+ /** 清理已过期的缓存条目,释放内存空间 */
+ clearExpiredCache,
+
+ // 请求控制
+ /** 取消当前请求 */
+ cancelRequest,
+
+ // 列配置 (如果提供了 columnsFactory)
+ ...(columnConfig && {
+ /** 表格列配置 */
+ columns,
+ /** 列显示控制 */
+ columnChecks,
+ /** 新增列 */
+ addColumn: columnConfig.addColumn,
+ /** 删除列 */
+ removeColumn: columnConfig.removeColumn,
+ /** 切换列显示状态 */
+ toggleColumn: columnConfig.toggleColumn,
+ /** 更新列配置 */
+ updateColumn: columnConfig.updateColumn,
+ /** 批量更新列配置 */
+ batchUpdateColumns: columnConfig.batchUpdateColumns,
+ /** 重新排序列 */
+ reorderColumns: columnConfig.reorderColumns,
+ /** 获取指定列配置 */
+ getColumnConfig: columnConfig.getColumnConfig,
+ /** 获取所有列配置 */
+ getAllColumns: columnConfig.getAllColumns,
+ /** 重置所有列配置到默认状态 */
+ resetColumns: columnConfig.resetColumns
+ })
+ }
+}
+
+// 重新导出类型和枚举,方便使用
+export { CacheInvalidationStrategy } from '../../utils/table/tableCache'
+export type { ApiResponse, CacheItem } from '../../utils/table/tableCache'
+export type { BaseRequestParams, TableError } from '../../utils/table/tableUtils'
diff --git a/adminSystem/src/hooks/core/useTableColumns.ts b/adminSystem/src/hooks/core/useTableColumns.ts
new file mode 100644
index 0000000..e4daf92
--- /dev/null
+++ b/adminSystem/src/hooks/core/useTableColumns.ts
@@ -0,0 +1,256 @@
+/**
+ * useTableColumns - 表格列配置管理
+ *
+ * 提供动态的表格列配置管理能力,支持运行时灵活控制列的显示、隐藏、排序等操作。
+ * 通常与 useTable 配合使用,为表格提供完整的列管理功能。
+ *
+ * ## 主要功能
+ *
+ * 1. 列显示控制 - 动态显示/隐藏列,支持批量操作
+ * 2. 列排序 - 拖拽或编程方式重新排列列顺序
+ * 3. 列配置管理 - 新增、删除、更新列配置
+ * 4. 特殊列支持 - 自动处理 selection、expand、index 等特殊列
+ * 5. 状态持久化 - 保持列的显示状态,支持重置到初始状态
+ *
+ * ## 使用示例
+ *
+ * ```typescript
+ * const { columns, columnChecks, toggleColumn, reorderColumns } = useTableColumns(() => [
+ * { prop: 'name', label: '姓名', visible: true },
+ * { prop: 'email', label: '邮箱', visible: true },
+ * { prop: 'status', label: '状态', visible: false }
+ * ])
+ *
+ * // 切换列显示
+ * toggleColumn('email', false)
+ *
+ * // 重新排序
+ * reorderColumns(0, 2)
+ * ```
+ *
+ * @module useTableColumns
+ * @author Art Design Pro Team
+ */
+
+import { ref, computed, watch } from 'vue'
+import { $t } from '@/locales'
+import type { ColumnOption } from '@/types/component'
+
+/**
+ * 特殊列类型
+ */
+const SPECIAL_COLUMNS: Record = {
+ selection: { prop: '__selection__', label: $t('table.column.selection') },
+ expand: { prop: '__expand__', label: $t('table.column.expand') },
+ index: { prop: '__index__', label: $t('table.column.index') }
+}
+
+/**
+ * 获取列的唯一标识
+ */
+export const getColumnKey = (col: ColumnOption) =>
+ SPECIAL_COLUMNS[col.type as keyof typeof SPECIAL_COLUMNS]?.prop ?? (col.prop as string)
+
+/**
+ * 获取列的显示状态
+ * 优先使用 visible 字段,如果不存在则使用 checked 字段
+ */
+export const getColumnVisibility = (col: ColumnOption): boolean => {
+ // visible 优先级高于 checked
+ if (col.visible !== undefined) {
+ return col.visible
+ }
+ // 如果 visible 未定义,使用 checked,默认为 true
+ return col.checked ?? true
+}
+
+/**
+ * 获取列的检查状态
+ */
+export const getColumnChecks = (columns: ColumnOption[]) =>
+ columns.map((col) => {
+ const special = col.type && SPECIAL_COLUMNS[col.type]
+ const visibility = getColumnVisibility(col)
+
+ if (special) {
+ return { ...col, prop: special.prop, label: special.label, checked: true, visible: true }
+ }
+ return { ...col, checked: visibility, visible: visibility }
+ })
+
+/**
+ * 动态列配置接口
+ */
+export interface DynamicColumnConfig {
+ /**
+ * 新增列
+ * @param column 列配置
+ * @param index 可选的插入位置,默认末尾
+ */
+ addColumn: (column: ColumnOption, index?: number) => void
+ /**
+ * 删除列
+ * @param prop 列的唯一标识或标识数组
+ */
+ removeColumn: (prop: string | string[]) => void
+ /**
+ * 切换列显示状态
+ * @param prop 列的唯一标识
+ * @param visible 可选的显示状态,默认取反
+ */
+ toggleColumn: (prop: string, visible?: boolean) => void
+
+ /**
+ * 更新列
+ * @param prop 列的唯一标识
+ * @param updates 列配置更新
+ */
+ updateColumn: (prop: string, updates: Partial>) => void
+ /**
+ * 批量更新列
+ * @param updates 列更新配置
+ */
+ batchUpdateColumns: (updates: Array<{ prop: string; updates: Partial> }>) => void
+ /**
+ * 重新排序列
+ * @param fromIndex 源索引
+ * @param toIndex 目标索引
+ */
+ reorderColumns: (fromIndex: number, toIndex: number) => void
+ /**
+ * 获取列配置
+ * @param prop 列的唯一标识
+ * @returns 列配置
+ */
+ getColumnConfig: (prop: string) => ColumnOption | undefined
+ /**
+ * 获取所有列配置
+ * @returns 所有列配置
+ */
+ getAllColumns: () => ColumnOption[]
+ /**
+ * 重置所有列
+ */
+ resetColumns: () => void
+}
+
+export function useTableColumns(
+ columnsFactory: () => ColumnOption[]
+): {
+ columns: any
+ columnChecks: any
+} & DynamicColumnConfig {
+ const dynamicColumns = ref[]>(columnsFactory())
+ const columnChecks = ref[]>(getColumnChecks(dynamicColumns.value))
+
+ // 当 dynamicColumns 变动时,重新生成 columnChecks 且保留已存在的显示状态
+ watch(
+ dynamicColumns,
+ (newCols) => {
+ const visibilityMap = new Map(
+ columnChecks.value.map((c) => [getColumnKey(c), getColumnVisibility(c)])
+ )
+ const newChecks = getColumnChecks(newCols).map((c) => {
+ const key = getColumnKey(c)
+ const visibility = visibilityMap.has(key) ? visibilityMap.get(key) : getColumnVisibility(c)
+ return {
+ ...c,
+ checked: visibility,
+ visible: visibility
+ }
+ })
+ columnChecks.value = newChecks
+ },
+ { deep: true }
+ )
+
+ // 当前显示列(基于 columnChecks 的 checked 或 visible)
+ const columns = computed(() => {
+ const colMap = new Map(dynamicColumns.value.map((c) => [getColumnKey(c), c]))
+ return columnChecks.value
+ .filter((c) => getColumnVisibility(c))
+ .map((c) => colMap.get(getColumnKey(c)))
+ .filter(Boolean) as ColumnOption[]
+ })
+
+ // 支持 updater 返回新数组或直接在传入数组上 mutate
+ const setDynamicColumns = (updater: (cols: ColumnOption[]) => void | ColumnOption[]) => {
+ const copy = [...dynamicColumns.value]
+ const result = updater(copy)
+ dynamicColumns.value = Array.isArray(result) ? result : copy
+ }
+
+ return {
+ columns,
+ columnChecks,
+
+ addColumn: (column: ColumnOption, index?: number) =>
+ setDynamicColumns((cols) => {
+ const next = [...cols]
+ if (typeof index === 'number' && index >= 0 && index <= next.length) {
+ next.splice(index, 0, column)
+ } else {
+ next.push(column)
+ }
+ return next
+ }),
+
+ removeColumn: (prop: string | string[]) =>
+ setDynamicColumns((cols) => {
+ const propsToRemove = Array.isArray(prop) ? prop : [prop]
+ return cols.filter((c) => !propsToRemove.includes(getColumnKey(c)))
+ }),
+
+ updateColumn: (prop: string, updates: Partial>) =>
+ setDynamicColumns((cols) =>
+ cols.map((c) => (getColumnKey(c) === prop ? { ...c, ...updates } : c))
+ ),
+
+ toggleColumn: (prop: string, visible?: boolean) => {
+ const i = columnChecks.value.findIndex((c) => getColumnKey(c) === prop)
+ if (i > -1) {
+ const next = [...columnChecks.value]
+ const currentVisibility = getColumnVisibility(next[i])
+ const newVisibility = visible ?? !currentVisibility
+ // 同时更新 checked 和 visible 以保持兼容性
+ next[i] = { ...next[i], checked: newVisibility, visible: newVisibility }
+ columnChecks.value = next
+ }
+ },
+
+ resetColumns: () => {
+ dynamicColumns.value = columnsFactory()
+ },
+
+ batchUpdateColumns: (updates) =>
+ setDynamicColumns((cols) => {
+ const map = new Map(updates.map((u) => [u.prop, u.updates]))
+ return cols.map((c) => {
+ const key = getColumnKey(c)
+ const upd = map.get(key)
+ return upd ? { ...c, ...upd } : c
+ })
+ }),
+
+ reorderColumns: (fromIndex: number, toIndex: number) =>
+ setDynamicColumns((cols) => {
+ if (
+ fromIndex < 0 ||
+ fromIndex >= cols.length ||
+ toIndex < 0 ||
+ toIndex >= cols.length ||
+ fromIndex === toIndex
+ ) {
+ return cols
+ }
+ const next = [...cols]
+ const [moved] = next.splice(fromIndex, 1)
+ next.splice(toIndex, 0, moved)
+ return next
+ }),
+
+ getColumnConfig: (prop: string) => dynamicColumns.value.find((c) => getColumnKey(c) === prop),
+
+ getAllColumns: () => [...dynamicColumns.value]
+ }
+}
diff --git a/adminSystem/src/hooks/core/useTableHeight.ts b/adminSystem/src/hooks/core/useTableHeight.ts
new file mode 100644
index 0000000..8fdf6da
--- /dev/null
+++ b/adminSystem/src/hooks/core/useTableHeight.ts
@@ -0,0 +1,105 @@
+/**
+ * useTableHeight - 表格高度自动计算
+ *
+ * 自动计算表格容器的最佳高度,确保表格在不同布局场景下都能正确显示。
+ * 根据表格头部、分页器等元素的高度动态调整容器高度,避免出现滚动条或布局错乱。
+ *
+ * ## 主要功能
+ *
+ * 1. 动态高度计算 - 根据表格头部、分页器高度自动计算容器高度
+ * 2. 响应式更新 - 配置变化时自动重新计算高度
+ * 3. 灵活配置 - 支持自定义各部分高度和间距
+ * 4. 智能适配 - 无额外元素时自动使用 100% 高度
+ *
+ * @module useTableHeight
+ * @author Art Design Pro Team
+ */
+
+import { computed, type Ref } from 'vue'
+
+/**
+ * 表格高度计算器配置接口
+ */
+interface TableHeightOptions {
+ /** 是否显示表格头部 */
+ showTableHeader: Ref
+ /** 分页器高度 */
+ paginationHeight: Ref
+ /** 表格头部高度 */
+ tableHeaderHeight: Ref
+ /** 分页器间距 */
+ paginationSpacing: Ref
+}
+
+/**
+ * 表格高度计算器类
+ */
+class TableHeightCalculator {
+ // 常量配置
+ private static readonly DEFAULT_TABLE_HEADER_HEIGHT = 44
+ private static readonly TABLE_HEADER_SPACING = 12
+
+ constructor(private options: TableHeightOptions) {}
+
+ /**
+ * 计算容器高度
+ */
+ calculate(): { height: string } {
+ const offset = this.calculateOffset()
+ return {
+ height: offset === 0 ? '100%' : `calc(100% - ${offset}px)`
+ }
+ }
+
+ /**
+ * 计算偏移量
+ */
+ private calculateOffset(): number {
+ if (!this.options.showTableHeader.value) {
+ return this.calculatePaginationOffset()
+ }
+
+ const headerHeight = this.getHeaderHeight()
+ const paginationOffset = this.calculatePaginationOffset()
+
+ return headerHeight + paginationOffset + TableHeightCalculator.TABLE_HEADER_SPACING
+ }
+
+ /**
+ * 获取表格头部高度
+ */
+ private getHeaderHeight(): number {
+ return this.options.tableHeaderHeight.value || TableHeightCalculator.DEFAULT_TABLE_HEADER_HEIGHT
+ }
+
+ /**
+ * 计算分页器偏移量
+ */
+ private calculatePaginationOffset(): number {
+ const { paginationHeight, paginationSpacing } = this.options
+ return paginationHeight.value === 0 ? 0 : paginationHeight.value + paginationSpacing.value
+ }
+}
+
+/**
+ * 表格高度计算 Hook
+ *
+ * 提供表格容器高度的自动计算功能,支持:
+ * - 表格头部高度
+ * - 分页器高度
+ * - 动态间距计算
+ *
+ * @param options 配置选项
+ * @returns 容器高度计算结果
+ */
+export function useTableHeight(options: TableHeightOptions) {
+ const containerHeight = computed(() => {
+ const calculator = new TableHeightCalculator(options)
+ return calculator.calculate()
+ })
+
+ return {
+ /** 容器高度样式对象 */
+ containerHeight
+ }
+}
diff --git a/adminSystem/src/hooks/core/useTheme.ts b/adminSystem/src/hooks/core/useTheme.ts
new file mode 100644
index 0000000..187c3e0
--- /dev/null
+++ b/adminSystem/src/hooks/core/useTheme.ts
@@ -0,0 +1,174 @@
+/**
+ * useTheme - 系统主题管理
+ *
+ * 提供完整的主题切换和管理功能,支持亮色、暗色和自动模式。
+ * 自动处理主题切换时的过渡效果,确保切换流畅无闪烁。
+ *
+ * ## 主要功能
+ *
+ * 1. 主题切换 - 支持亮色、暗色、自动三种主题模式
+ * 2. 自动模式 - 根据系统偏好自动切换主题
+ * 3. 颜色适配 - 自动调整主题色的明暗变体(9 个层级)
+ * 4. 过渡优化 - 切换时临时禁用过渡效果,避免闪烁
+ * 5. 状态持久化 - 主题设置自动保存到 store
+ *
+ * ## 使用示例
+ *
+ * ```typescript
+ * const { switchThemeStyles } = useTheme()
+ *
+ * // 切换到暗色主题
+ * switchThemeStyles(SystemThemeEnum.DARK)
+ *
+ * // 切换到亮色主题
+ * switchThemeStyles(SystemThemeEnum.LIGHT)
+ *
+ * // 切换到自动模式(跟随系统)
+ * switchThemeStyles(SystemThemeEnum.AUTO)
+ * ```
+ *
+ * @module useTheme
+ * @author Art Design Pro Team
+ */
+
+import { useSettingStore } from '@/store/modules/setting'
+import { SystemThemeEnum } from '@/enums/appEnum'
+import AppConfig from '@/config'
+import { SystemThemeTypes } from '@/types/store'
+import { getDarkColor, getLightColor, setElementThemeColor } from '@/utils/ui'
+import { usePreferredDark } from '@vueuse/core'
+import { watch } from 'vue'
+
+export function useTheme() {
+ const settingStore = useSettingStore()
+
+ // 禁用过渡效果
+ const disableTransitions = () => {
+ const style = document.createElement('style')
+ style.setAttribute('id', 'disable-transitions')
+ style.textContent = '* { transition: none !important; }'
+ document.head.appendChild(style)
+ }
+
+ // 启用过渡效果
+ const enableTransitions = () => {
+ const style = document.getElementById('disable-transitions')
+ if (style) {
+ style.remove()
+ }
+ }
+
+ // 设置系统主题
+ const setSystemTheme = (theme: SystemThemeEnum, themeMode?: SystemThemeEnum) => {
+ // 临时禁用过渡效果
+ disableTransitions()
+
+ const el = document.getElementsByTagName('html')[0]
+ const isDark = theme === SystemThemeEnum.DARK
+
+ if (!themeMode) {
+ themeMode = theme
+ }
+
+ const currentTheme = AppConfig.systemThemeStyles[theme as keyof SystemThemeTypes]
+
+ if (currentTheme) {
+ el.setAttribute('class', currentTheme.className)
+ }
+
+ // 设置按钮颜色加深或变浅
+ const primary = settingStore.systemThemeColor
+
+ for (let i = 1; i <= 9; i++) {
+ document.documentElement.style.setProperty(
+ `--el-color-primary-light-${i}`,
+ isDark ? `${getDarkColor(primary, i / 10)}` : `${getLightColor(primary, i / 10)}`
+ )
+ }
+
+ // 更新store中的主题设置
+ settingStore.setGlopTheme(theme, themeMode)
+
+ // 使用 requestAnimationFrame 确保在下一帧恢复过渡效果
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ enableTransitions()
+ })
+ })
+ }
+
+ // 使用 VueUse 的 usePreferredDark 检测系统主题偏好
+ const prefersDark = usePreferredDark()
+
+ // 自动设置系统主题
+ const setSystemAutoTheme = () => {
+ const theme = prefersDark.value ? SystemThemeEnum.DARK : SystemThemeEnum.LIGHT
+ setSystemTheme(theme, SystemThemeEnum.AUTO)
+ }
+
+ // 切换主题
+ const switchThemeStyles = (theme: SystemThemeEnum) => {
+ if (theme === SystemThemeEnum.AUTO) {
+ setSystemAutoTheme()
+ } else {
+ setSystemTheme(theme)
+ }
+ }
+
+ return {
+ setSystemTheme,
+ setSystemAutoTheme,
+ switchThemeStyles,
+ prefersDark
+ }
+}
+
+/**
+ * 初始化主题系统
+ */
+export function initializeTheme() {
+ const settingStore = useSettingStore()
+ const prefersDark = usePreferredDark()
+
+ // 根据系统偏好应用主题
+ const applyThemeByMode = () => {
+ const el = document.getElementsByTagName('html')[0]
+ let actualTheme = settingStore.systemThemeType
+
+ // 如果是 AUTO 模式,检测系统偏好
+ if (settingStore.systemThemeMode === SystemThemeEnum.AUTO) {
+ actualTheme = prefersDark.value ? SystemThemeEnum.DARK : SystemThemeEnum.LIGHT
+ // 更新实际应用的主题类型
+ settingStore.systemThemeType = actualTheme
+ }
+
+ // 设置主题 class
+ const currentTheme = AppConfig.systemThemeStyles[actualTheme as keyof SystemThemeTypes]
+ if (currentTheme) {
+ el.setAttribute('class', currentTheme.className)
+ }
+
+ // 设置主题颜色
+ setElementThemeColor(settingStore.systemThemeColor)
+
+ // 设置圆角
+ document.documentElement.style.setProperty('--custom-radius', `${settingStore.customRadius}rem`)
+ }
+
+ // 应用主题
+ applyThemeByMode()
+
+ // 如果是 AUTO 模式,监听系统主题变化(使用 VueUse 的响应式特性)
+ if (settingStore.systemThemeMode === SystemThemeEnum.AUTO) {
+ watch(
+ prefersDark,
+ () => {
+ // 只有在 AUTO 模式下才响应系统主题变化
+ if (settingStore.systemThemeMode === SystemThemeEnum.AUTO) {
+ applyThemeByMode()
+ }
+ },
+ { immediate: false }
+ )
+ }
+}
diff --git a/adminSystem/src/hooks/index.ts b/adminSystem/src/hooks/index.ts
new file mode 100644
index 0000000..472b09c
--- /dev/null
+++ b/adminSystem/src/hooks/index.ts
@@ -0,0 +1,32 @@
+// 通用功能集合
+export { useCommon } from './core/useCommon'
+
+// 应用模式
+export { useAppMode } from './core/useAppMode'
+
+// 权限控制
+export { useAuth } from './core/useAuth'
+
+// 表格数据管理方案
+export { useTable } from './core/useTable'
+
+// 表格列配置管理
+export { useTableColumns } from './core/useTableColumns'
+
+// 主题相关
+export { useTheme } from './core/useTheme'
+
+// 礼花+文字滚动
+export { useCeremony } from './core/useCeremony'
+
+// 顶栏快速入口
+export { useFastEnter } from './core/useFastEnter'
+
+// 顶栏功能管理
+export { useHeaderBar } from './core/useHeaderBar'
+
+// 图表相关
+export { useChart, useChartComponent, useChartOps } from './core/useChart'
+
+// 布局高度
+export { useLayoutHeight, useAutoLayoutHeight } from './core/useLayoutHeight'
diff --git a/adminSystem/src/locales/index.ts b/adminSystem/src/locales/index.ts
new file mode 100644
index 0000000..36c2648
--- /dev/null
+++ b/adminSystem/src/locales/index.ts
@@ -0,0 +1,123 @@
+/**
+ * 国际化配置
+ *
+ * 基于 vue-i18n 实现的多语言国际化解决方案。
+ * 支持中文和英文切换,自动从本地存储恢复用户的语言偏好。
+ *
+ * ## 主要功能
+ *
+ * - 多语言支持 - 支持中文(简体)和英文两种语言
+ * - 语言切换 - 运行时动态切换语言,无需刷新页面
+ * - 持久化存储 - 自动保存和恢复用户的语言偏好
+ * - 全局注入 - 在任何组件中都可以使用 $t 函数进行翻译
+ * - 类型安全 - 提供 TypeScript 类型支持
+ *
+ * ## 支持的语言
+ *
+ * - zh: 简体中文
+ * - en: English
+ *
+ * @module locales
+ * @author Art Design Pro Team
+ */
+
+import { createI18n } from 'vue-i18n'
+import type { I18n, I18nOptions } from 'vue-i18n'
+import { LanguageEnum } from '@/enums/appEnum'
+import { getSystemStorage } from '@/utils/storage'
+import { StorageKeyManager } from '@/utils/storage/storage-key-manager'
+
+// 同步导入语言文件
+import enMessages from './langs/en.json'
+import zhMessages from './langs/zh.json'
+
+/**
+ * 存储键管理器实例
+ */
+const storageKeyManager = new StorageKeyManager()
+
+/**
+ * 语言消息对象
+ */
+const messages = {
+ [LanguageEnum.EN]: enMessages,
+ [LanguageEnum.ZH]: zhMessages
+}
+
+/**
+ * 语言选项列表
+ * 用于语言切换下拉框
+ */
+export const languageOptions = [
+ { value: LanguageEnum.ZH, label: '简体中文' },
+ { value: LanguageEnum.EN, label: 'English' }
+]
+
+/**
+ * 从存储中获取语言设置
+ * @returns 语言设置,如果获取失败则返回默认语言
+ */
+const getDefaultLanguage = (): LanguageEnum => {
+ // 尝试从版本化的存储中获取语言设置
+ try {
+ const storageKey = storageKeyManager.getStorageKey('user')
+ const userStore = localStorage.getItem(storageKey)
+
+ if (userStore) {
+ const { language } = JSON.parse(userStore)
+ if (language && Object.values(LanguageEnum).includes(language)) {
+ return language
+ }
+ }
+ } catch (error) {
+ console.warn('[i18n] 从版本化存储获取语言设置失败:', error)
+ }
+
+ // 尝试从系统存储中获取语言设置
+ try {
+ const sys = getSystemStorage()
+ if (sys) {
+ const { user } = JSON.parse(sys)
+ if (user?.language && Object.values(LanguageEnum).includes(user.language)) {
+ return user.language
+ }
+ }
+ } catch (error) {
+ console.warn('[i18n] 从系统存储获取语言设置失败:', error)
+ }
+
+ // 返回默认语言
+ console.debug('[i18n] 使用默认语言:', LanguageEnum.ZH)
+ return LanguageEnum.ZH
+}
+
+/**
+ * i18n 配置选项
+ */
+const i18nOptions: I18nOptions = {
+ locale: getDefaultLanguage(),
+ legacy: false,
+ globalInjection: true,
+ fallbackLocale: LanguageEnum.ZH,
+ messages
+}
+
+/**
+ * i18n 实例
+ */
+const i18n: I18n = createI18n(i18nOptions)
+
+/**
+ * 翻译函数类型
+ */
+interface Translation {
+ (key: string): string
+}
+
+/**
+ * 全局翻译函数
+ * 可在任何地方使用,无需导入 useI18n
+ */
+export const $t = i18n.global.t as Translation
+
+export default i18n
diff --git a/adminSystem/src/locales/langs/en.json b/adminSystem/src/locales/langs/en.json
new file mode 100644
index 0000000..51e196b
--- /dev/null
+++ b/adminSystem/src/locales/langs/en.json
@@ -0,0 +1,296 @@
+{
+ "httpMsg": {
+ "unauthorized": "Unauthorized access, please login again",
+ "forbidden": "Access to this resource is forbidden",
+ "notFound": "The requested resource does not exist",
+ "methodNotAllowed": "Request method not allowed",
+ "requestTimeout": "Request timeout, please try again later",
+ "internalServerError": "Internal server error, please try again later",
+ "badGateway": "Bad gateway error, please try again later",
+ "serviceUnavailable": "Service temporarily unavailable, please try again later",
+ "gatewayTimeout": "Gateway timeout, please try again later",
+ "requestCancelled": "Request cancelled",
+ "networkError": "Network connection error, please check your connection",
+ "requestFailed": "Request failed",
+ "requestConfigError": "Request configuration error"
+ },
+ "topBar": {
+ "search": {
+ "title": "Search"
+ },
+ "user": {
+ "userCenter": "User center",
+ "docs": "Document",
+ "github": "Github",
+ "lockScreen": "Lock screen",
+ "logout": "Log out"
+ },
+ "guide": {
+ "title": "Click here to view",
+ "theme": "Theme style",
+ "menu": "Open top menu",
+ "description": "More configurations"
+ }
+ },
+ "common": {
+ "tips": "Prompt",
+ "cancel": "Cancel",
+ "confirm": "Confirm",
+ "logOutTips": "Do you want to log out?"
+ },
+ "search": {
+ "placeholder": "Search page",
+ "historyTitle": "Search history",
+ "switchKeydown": "Navigate",
+ "selectKeydown": "Select",
+ "exitKeydown": "Close"
+ },
+ "setting": {
+ "menuType": {
+ "title": "Menu Layout",
+ "list": [
+ "Vertical",
+ "Horizontal",
+ "Mixed",
+ "Dual"
+ ]
+ },
+ "theme": {
+ "title": "Theme Style",
+ "list": [
+ "Light",
+ "Dark",
+ "System"
+ ]
+ },
+ "menu": {
+ "title": "Menu Style"
+ },
+ "color": {
+ "title": "Theme Color"
+ },
+ "box": {
+ "title": "Box Style",
+ "list": [
+ "Border",
+ "Shadow"
+ ]
+ },
+ "container": {
+ "title": "Container Width",
+ "list": [
+ "Full",
+ "Boxed"
+ ]
+ },
+ "basics": {
+ "title": "Basic Config",
+ "list": {
+ "multiTab": "Show work tab",
+ "accordion": "Sidebar opens accordion",
+ "collapseSidebar": "Show sidebar button",
+ "reloadPage": "Show reload page button",
+ "fastEnter": "Show fast enter",
+ "breadcrumb": "Show crumb navigation",
+ "language": "Show multilingual selection",
+ "progressBar": "Show top progress bar",
+ "weakMode": "Color Weakness Mode",
+ "watermark": "Global watermark",
+ "menuWidth": "Menu width",
+ "tabStyle": "Tab style",
+ "pageTransition": "Page animation",
+ "borderRadius": "Custom radius"
+ }
+ },
+ "tabStyle": {
+ "default": "Default",
+ "card": "Card",
+ "google": "Chrome"
+ },
+ "transition": {
+ "list": {
+ "none": "None",
+ "fade": "Fade",
+ "slideLeft": "Slide Left",
+ "slideBottom": "Slide Bottom",
+ "slideTop": "Slide Top"
+ }
+ },
+ "actions": {
+ "resetConfig": "Reset Config",
+ "copyConfig": "Copy Config",
+ "copySuccess": "Configuration copied to clipboard, paste it into src/config/setting.ts file",
+ "copyFailed": "Copy failed, please try again",
+ "resetFailed": "Reset failed, please refresh the page and try again"
+ }
+ },
+ "notice": {
+ "title": "Notice",
+ "btnRead": "Mark as read",
+ "bar": [
+ "Notice",
+ "Message",
+ "Todo"
+ ],
+ "text": [
+ "No"
+ ],
+ "viewAll": "View all"
+ },
+ "worktab": {
+ "btn": {
+ "refresh": "Refresh",
+ "fixed": "Fixed",
+ "unfixed": "Unfixed",
+ "closeLeft": "Close left",
+ "closeRight": "Close right",
+ "closeOther": "Close other",
+ "closeAll": "Close all"
+ }
+ },
+ "login": {
+ "leftView": {
+ "title": "A backend system of beauty and efficiency",
+ "subTitle": "A sleek and practical interface for a great user experience"
+ },
+ "title": "Welcome back",
+ "subTitle": "Please enter your account and password to login",
+ "roles": {
+ "super": "Super Admin",
+ "admin": "Admin",
+ "user": "User"
+ },
+ "placeholder": {
+ "username": "Please enter your account",
+ "password": "Please enter your password",
+ "slider": "Please slide to verify"
+ },
+ "sliderText": "Please slide to verify",
+ "sliderSuccessText": "Verification successful",
+ "rememberPwd": "Remember password",
+ "forgetPwd": "Forgot password",
+ "btnText": "Login",
+ "noAccount": "No account yet?",
+ "register": "Register",
+ "success": {
+ "title": "Login successful",
+ "message": "Welcome back"
+ }
+ },
+ "forgetPassword": {
+ "title": "Forgot password?",
+ "subTitle": "Enter your email to reset your password",
+ "placeholder": "Please enter your email",
+ "submitBtnText": "Submit",
+ "backBtnText": "Back"
+ },
+ "register": {
+ "title": "Create account",
+ "subTitle": "Welcome to join us, please fill in the following information to complete the registration",
+ "placeholder": {
+ "username": "Please enter your account",
+ "password": "Please enter your password",
+ "confirmPassword": "Please enter your password again"
+ },
+ "rule": {
+ "confirmPasswordRequired": "Please enter your password again",
+ "passwordMismatch": "The two passwords are inconsistent!",
+ "usernameLength": "The length is 3 to 20 characters",
+ "passwordLength": "The password length cannot be less than 6 digits",
+ "agreementRequired": "Please agree to the privacy policy"
+ },
+ "agreeText": "I agree",
+ "privacyPolicy": "Privacy policy",
+ "submitBtnText": "Register",
+ "hasAccount": "Already have an account?",
+ "toLogin": "To login"
+ },
+ "lockScreen": {
+ "pwdError": "Password error",
+ "lock": {
+ "inputPlaceholder": "Please input lock screen password",
+ "btnText": "Lock"
+ },
+ "unlock": {
+ "inputPlaceholder": "Please input unlock password",
+ "btnText": "Unlock",
+ "backBtnText": "Back to login"
+ }
+ },
+ "greeting": {
+ "dawn": "Good morning!",
+ "morning": "Good morning!",
+ "afternoon": "Good afternoon!",
+ "evening": "Good evening!"
+ },
+ "exceptionPage": {
+ "403": "Sorry, you do not have permission to access this page",
+ "404": "Sorry, the page you are trying to access does not exist",
+ "500": "Sorry, there was an error on the server",
+ "gohome": "Go Home"
+ },
+ "menus": {
+ "login": {
+ "title": "Login"
+ },
+ "register": {
+ "title": "Register"
+ },
+ "forgetPassword": {
+ "title": "Forget Password"
+ },
+ "outside": {
+ "title": "Outside"
+ },
+ "dashboard": {
+ "title": "Dashboard",
+ "console": "Console"
+ },
+ "result": {
+ "title": "Result Page",
+ "success": "Success",
+ "fail": "Fail"
+ },
+ "exception": {
+ "title": "Exception",
+ "forbidden": "403",
+ "notFound": "404",
+ "serverError": "500"
+ },
+ "system": {
+ "title": "System Settings",
+ "user": "User Manage",
+ "role": "Role Manage",
+ "userCenter": "User Center",
+ "menu": "Menu Manage"
+ }
+ },
+ "table": {
+ "form": {
+ "reset": "Reset",
+ "submit": "Submit"
+ },
+ "searchBar": {
+ "reset": "Reset",
+ "search": "Search",
+ "expand": "Expand",
+ "collapse": "Collapse",
+ "searchInputPlaceholder": "Please enter",
+ "searchSelectPlaceholder": "Please select"
+ },
+ "selection": "Select",
+ "sizeOptions": {
+ "small": "Compact",
+ "default": "Default",
+ "large": "Loose"
+ },
+ "column": {
+ "selection": "Select",
+ "expand": "Expand",
+ "index": "Index"
+ },
+ "zebra": "Zebra",
+ "border": "Border",
+ "headerBackground": "Header BG"
+ }
+}
\ No newline at end of file
diff --git a/adminSystem/src/locales/langs/zh.json b/adminSystem/src/locales/langs/zh.json
new file mode 100644
index 0000000..77a23a5
--- /dev/null
+++ b/adminSystem/src/locales/langs/zh.json
@@ -0,0 +1,296 @@
+{
+ "httpMsg": {
+ "unauthorized": "未授权访问,请重新登录",
+ "forbidden": "禁止访问该资源",
+ "notFound": "请求的资源不存在",
+ "methodNotAllowed": "请求方法不允许",
+ "requestTimeout": "请求超时,请稍后重试",
+ "internalServerError": "服务器内部错误,请稍后重试",
+ "badGateway": "网关错误,请稍后重试",
+ "serviceUnavailable": "服务暂时不可用,请稍后重试",
+ "gatewayTimeout": "网关超时,请稍后重试",
+ "requestCancelled": "请求已取消",
+ "networkError": "网络连接异常,请检查网络连接",
+ "requestFailed": "请求失败",
+ "requestConfigError": "请求配置错误"
+ },
+ "topBar": {
+ "search": {
+ "title": "搜索"
+ },
+ "user": {
+ "userCenter": "个人中心",
+ "docs": "使用文档",
+ "github": "Github",
+ "lockScreen": "锁定屏幕",
+ "logout": "退出登录"
+ },
+ "guide": {
+ "title": "点击这里查看",
+ "theme": "主题风格",
+ "menu": "开启顶栏菜单",
+ "description": "等更多配置"
+ }
+ },
+ "common": {
+ "tips": "提示",
+ "cancel": "取消",
+ "confirm": "确定",
+ "logOutTips": "您是否要退出登录?"
+ },
+ "search": {
+ "placeholder": "搜索页面",
+ "historyTitle": "搜索历史",
+ "switchKeydown": "切换",
+ "selectKeydown": "选择",
+ "exitKeydown": "关闭"
+ },
+ "setting": {
+ "menuType": {
+ "title": "菜单布局",
+ "list": [
+ "垂直",
+ "水平",
+ "混合",
+ "双列"
+ ]
+ },
+ "theme": {
+ "title": "主题风格",
+ "list": [
+ "浅色",
+ "深色",
+ "系统"
+ ]
+ },
+ "menu": {
+ "title": "菜单风格"
+ },
+ "color": {
+ "title": "系统主题色"
+ },
+ "box": {
+ "title": "盒子样式",
+ "list": [
+ "边框",
+ "阴影"
+ ]
+ },
+ "container": {
+ "title": "容器宽度",
+ "list": [
+ "铺满",
+ "定宽"
+ ]
+ },
+ "basics": {
+ "title": "基础配置",
+ "list": {
+ "multiTab": "开启多标签栏",
+ "accordion": "侧边栏开启手风琴模式",
+ "collapseSidebar": "显示折叠侧边栏按钮",
+ "fastEnter": "显示快速入口",
+ "reloadPage": "显示重载页面按钮",
+ "breadcrumb": "显示全局面包屑导航",
+ "language": "显示多语言选择",
+ "progressBar": "显示顶部进度条",
+ "weakMode": "色弱模式",
+ "watermark": "全局水印",
+ "menuWidth": "菜单宽度",
+ "tabStyle": "标签页风格",
+ "pageTransition": "页面切换动画",
+ "borderRadius": "自定义圆角"
+ }
+ },
+ "tabStyle": {
+ "default": "默认",
+ "card": "卡片",
+ "google": "谷歌"
+ },
+ "transition": {
+ "list": {
+ "none": "无动画",
+ "fade": "淡入淡出",
+ "slideLeft": "左侧滑入",
+ "slideBottom": "下方滑入",
+ "slideTop": "上方滑入"
+ }
+ },
+ "actions": {
+ "resetConfig": "重置配置",
+ "copyConfig": "复制配置",
+ "copySuccess": "配置已复制到剪贴板,可粘贴到 src/config/setting.ts 文件中",
+ "copyFailed": "复制失败,请重试",
+ "resetFailed": "重置失败,请刷新页面后重试"
+ }
+ },
+ "notice": {
+ "title": "通知",
+ "btnRead": "标为已读",
+ "bar": [
+ "通知",
+ "消息",
+ "代办"
+ ],
+ "text": [
+ "暂无"
+ ],
+ "viewAll": "查看全部"
+ },
+ "worktab": {
+ "btn": {
+ "refresh": "刷新",
+ "fixed": "固定",
+ "unfixed": "取消固定",
+ "closeLeft": "关闭左侧",
+ "closeRight": "关闭右侧",
+ "closeOther": "关闭其他",
+ "closeAll": "关闭全部"
+ }
+ },
+ "login": {
+ "leftView": {
+ "title": "一款兼具设计美学与高效开发的后台系统",
+ "subTitle": "美观实用的界面,经过视觉优化,确保卓越的用户体验"
+ },
+ "title": "欢迎回来",
+ "subTitle": "输入您的账号和密码登录",
+ "roles": {
+ "super": "超级管理员",
+ "admin": "管理员",
+ "user": "普通用户"
+ },
+ "placeholder": {
+ "username": "请输入账号",
+ "password": "请输入密码",
+ "slider": "请拖动滑块完成验证"
+ },
+ "sliderText": "按住滑块拖动",
+ "sliderSuccessText": "验证成功",
+ "rememberPwd": "记住密码",
+ "forgetPwd": "忘记密码",
+ "btnText": "登录",
+ "noAccount": "还没有账号?",
+ "register": "注册",
+ "success": {
+ "title": "登录成功",
+ "message": "欢迎回来"
+ }
+ },
+ "forgetPassword": {
+ "title": "忘记密码?",
+ "subTitle": "输入您的电子邮件来重置您的密码",
+ "placeholder": "请输入您的电子邮件",
+ "submitBtnText": "提交",
+ "backBtnText": "返回"
+ },
+ "register": {
+ "title": "创建账号",
+ "subTitle": "欢迎加入我们,请填写以下信息完成注册",
+ "placeholder": {
+ "username": "请输入账号",
+ "password": "请输入密码",
+ "confirmPassword": "请再次输入密码"
+ },
+ "rule": {
+ "confirmPasswordRequired": "请再次输入密码",
+ "passwordMismatch": "两次输入密码不一致!",
+ "usernameLength": "长度在 3 到 20 个字符",
+ "passwordLength": "密码长度不能小于6位",
+ "agreementRequired": "请同意隐私协议"
+ },
+ "agreeText": "我同意",
+ "privacyPolicy": "《隐私政策》",
+ "submitBtnText": "注册",
+ "hasAccount": "已有账号?",
+ "toLogin": "去登录"
+ },
+ "lockScreen": {
+ "pwdError": "密码错误",
+ "lock": {
+ "inputPlaceholder": "请输入锁屏密码",
+ "btnText": "锁定"
+ },
+ "unlock": {
+ "inputPlaceholder": "请输入解锁密码",
+ "btnText": "解锁",
+ "backBtnText": "返回登录"
+ }
+ },
+ "greeting": {
+ "dawn": "凌晨了!",
+ "morning": "上午好!",
+ "afternoon": "下午好!",
+ "evening": "晚上好!"
+ },
+ "exceptionPage": {
+ "403": "抱歉,您无权访问该页面",
+ "404": "抱歉,您访问的页面不存在",
+ "500": "抱歉,服务器出错了",
+ "gohome": "返回首页"
+ },
+ "menus": {
+ "login": {
+ "title": "登录"
+ },
+ "register": {
+ "title": "注册"
+ },
+ "forgetPassword": {
+ "title": "忘记密码"
+ },
+ "outside": {
+ "title": "内嵌页面"
+ },
+ "dashboard": {
+ "title": "仪表盘",
+ "console": "工作台"
+ },
+ "result": {
+ "title": "结果页面",
+ "success": "成功页",
+ "fail": "失败页"
+ },
+ "exception": {
+ "title": "异常页面",
+ "forbidden": "403",
+ "notFound": "404",
+ "serverError": "500"
+ },
+ "system": {
+ "title": "系统管理",
+ "user": "用户管理",
+ "role": "角色管理",
+ "userCenter": "个人中心",
+ "menu": "菜单管理"
+ }
+ },
+ "table": {
+ "form": {
+ "reset": "重置",
+ "submit": "提交"
+ },
+ "searchBar": {
+ "reset": "重置",
+ "search": "查询",
+ "expand": "展开",
+ "collapse": "收起",
+ "searchInputPlaceholder": "请输入",
+ "searchSelectPlaceholder": "请选择"
+ },
+ "selection": "选择",
+ "sizeOptions": {
+ "small": "紧凑",
+ "default": "默认",
+ "large": "宽松"
+ },
+ "column": {
+ "selection": "勾选",
+ "expand": "展开",
+ "index": "序号"
+ },
+ "zebra": "斑马纹",
+ "border": "边框",
+ "headerBackground": "表头背景"
+ }
+}
\ No newline at end of file
diff --git a/adminSystem/src/main.ts b/adminSystem/src/main.ts
new file mode 100644
index 0000000..7df948a
--- /dev/null
+++ b/adminSystem/src/main.ts
@@ -0,0 +1,25 @@
+import App from './App.vue'
+import { createApp } from 'vue'
+import { initStore } from './store' // Store
+import { initRouter } from './router' // Router
+import language from './locales' // 国际化
+import '@styles/core/tailwind.css' // tailwind
+import '@styles/index.scss' // 样式
+import '@utils/sys/console.ts' // 控制台输出内容
+import { setupGlobDirectives } from './directives'
+import { setupErrorHandle } from './utils/sys/error-handle'
+
+document.addEventListener(
+ 'touchstart',
+ function () {},
+ { passive: false }
+)
+
+const app = createApp(App)
+initStore(app)
+initRouter(app)
+setupGlobDirectives(app)
+setupErrorHandle(app)
+
+app.use(language)
+app.mount('#app')
\ No newline at end of file
diff --git a/adminSystem/src/mock/temp/formData.ts b/adminSystem/src/mock/temp/formData.ts
new file mode 100644
index 0000000..8aa7ae5
--- /dev/null
+++ b/adminSystem/src/mock/temp/formData.ts
@@ -0,0 +1,273 @@
+import avatar1 from '@/assets/images/avatar/avatar1.webp'
+import avatar2 from '@/assets/images/avatar/avatar2.webp'
+import avatar3 from '@/assets/images/avatar/avatar3.webp'
+import avatar4 from '@/assets/images/avatar/avatar4.webp'
+import avatar5 from '@/assets/images/avatar/avatar5.webp'
+import avatar6 from '@/assets/images/avatar/avatar6.webp'
+import avatar7 from '@/assets/images/avatar/avatar7.webp'
+import avatar8 from '@/assets/images/avatar/avatar8.webp'
+import avatar9 from '@/assets/images/avatar/avatar9.webp'
+import avatar10 from '@/assets/images/avatar/avatar10.webp'
+
+export interface User {
+ id: number
+ username: string
+ gender: 1 | 0
+ mobile: string
+ email: string
+ dep: string
+ status: string
+ create_time: string
+ avatar: string
+}
+
+// 用户列表
+export const ACCOUNT_TABLE_DATA: User[] = [
+ {
+ id: 1,
+ username: 'alexmorgan',
+ gender: 1,
+ mobile: '18670001591',
+ email: 'alexmorgan@company.com',
+ dep: '研发部',
+ status: '1',
+ create_time: '2020-09-09 10:01:10',
+ avatar: avatar1
+ },
+ {
+ id: 2,
+ username: 'sophiabaker',
+ gender: 1,
+ mobile: '17766664444',
+ email: 'sophiabaker@company.com',
+ dep: '电商部',
+ status: '1',
+ create_time: '2020-10-10 13:01:12',
+ avatar: avatar2
+ },
+ {
+ id: 3,
+ username: 'liampark',
+ gender: 1,
+ mobile: '18670001597',
+ email: 'liampark@company.com',
+ dep: '人事部',
+ status: '1',
+ create_time: '2020-11-14 12:01:45',
+ avatar: avatar3
+ },
+ {
+ id: 4,
+ username: 'oliviagrant',
+ gender: 0,
+ mobile: '18670001596',
+ email: 'oliviagrant@company.com',
+ dep: '产品部',
+ status: '1',
+ create_time: '2020-11-14 09:01:20',
+ avatar: avatar4
+ },
+ {
+ id: 5,
+ username: 'emmawilson',
+ gender: 0,
+ mobile: '18670001595',
+ email: 'emmawilson@company.com',
+ dep: '财务部',
+ status: '1',
+ create_time: '2020-11-13 11:01:05',
+ avatar: avatar5
+ },
+ {
+ id: 6,
+ username: 'noahevan',
+ gender: 1,
+ mobile: '18670001594',
+ email: 'noahevan@company.com',
+ dep: '运营部',
+ status: '1',
+ create_time: '2020-10-11 13:10:26',
+ avatar: avatar6
+ },
+ {
+ id: 7,
+ username: 'avamartin',
+ gender: 1,
+ mobile: '18123820191',
+ email: 'avamartin@company.com',
+ dep: '客服部',
+ status: '2',
+ create_time: '2020-05-14 12:05:10',
+ avatar: avatar7
+ },
+ {
+ id: 8,
+ username: 'jacoblee',
+ gender: 1,
+ mobile: '18670001592',
+ email: 'jacoblee@company.com',
+ dep: '总经办',
+ status: '3',
+ create_time: '2020-11-12 07:22:25',
+ avatar: avatar8
+ },
+ {
+ id: 9,
+ username: 'miaclark',
+ gender: 0,
+ mobile: '18670001581',
+ email: 'miaclark@company.com',
+ dep: '研发部',
+ status: '4',
+ create_time: '2020-06-12 05:04:20',
+ avatar: avatar9
+ },
+ {
+ id: 10,
+ username: 'ethanharris',
+ gender: 1,
+ mobile: '13755554444',
+ email: 'ethanharris@company.com',
+ dep: '研发部',
+ status: '1',
+ create_time: '2020-11-12 16:01:10',
+ avatar: avatar10
+ },
+ {
+ id: 11,
+ username: 'isabellamoore',
+ gender: 1,
+ mobile: '13766660000',
+ email: 'isabellamoore@company.com',
+ dep: '研发部',
+ status: '1',
+ create_time: '2020-11-14 12:01:20',
+ avatar: avatar6
+ },
+ {
+ id: 12,
+ username: 'masonwhite',
+ gender: 1,
+ mobile: '18670001502',
+ email: 'masonwhite@company.com',
+ dep: '研发部',
+ status: '1',
+ create_time: '2020-11-14 12:01:20',
+ avatar: avatar7
+ },
+ {
+ id: 13,
+ username: 'charlottehall',
+ gender: 1,
+ mobile: '13006644977',
+ email: 'charlottehall@company.com',
+ dep: '研发部',
+ status: '1',
+ create_time: '2020-11-14 12:01:20',
+ avatar: avatar8
+ },
+ {
+ id: 14,
+ username: 'benjaminscott',
+ gender: 0,
+ mobile: '13599998888',
+ email: 'benjaminscott@company.com',
+ dep: '研发部',
+ status: '1',
+ create_time: '2020-11-14 12:01:20',
+ avatar: avatar9
+ },
+ {
+ id: 15,
+ username: 'ameliaking',
+ gender: 1,
+ mobile: '13799998888',
+ email: 'ameliaking@company.com',
+ dep: '研发部',
+ status: '1',
+ create_time: '2020-11-14 12:01:20',
+ avatar: avatar10
+ }
+]
+
+export interface Role {
+ roleName: string
+ roleCode: string
+ des: string
+ date: string
+ enable: boolean
+}
+
+// 角色列表
+export const ROLE_LIST_DATA: Role[] = [
+ {
+ roleName: '超级管理员',
+ roleCode: 'R_SUPER',
+ des: '拥有系统全部权限',
+ date: '2025-05-15 12:30:45',
+ enable: true
+ },
+ {
+ roleName: '管理员',
+ roleCode: 'R_ADMIN',
+ des: '拥有系统管理权限',
+ date: '2025-05-15 12:30:45',
+ enable: true
+ },
+ {
+ roleName: '普通用户',
+ roleCode: 'R_USER',
+ des: '拥有系统普通权限',
+ date: '2025-05-15 12:30:45',
+ enable: true
+ },
+ {
+ roleName: '财务管理员',
+ roleCode: 'R_FINANCE',
+ des: '管理财务相关权限',
+ date: '2025-05-16 09:15:30',
+ enable: true
+ },
+ {
+ roleName: '数据分析师',
+ roleCode: 'R_ANALYST',
+ des: '拥有数据分析权限',
+ date: '2025-05-16 11:45:00',
+ enable: false
+ },
+ {
+ roleName: '客服专员',
+ roleCode: 'R_SUPPORT',
+ des: '处理客户支持请求',
+ date: '2025-05-17 14:30:22',
+ enable: true
+ },
+ {
+ roleName: '营销经理',
+ roleCode: 'R_MARKETING',
+ des: '管理营销活动权限',
+ date: '2025-05-17 15:10:50',
+ enable: true
+ },
+ {
+ roleName: '访客用户',
+ roleCode: 'R_GUEST',
+ des: '仅限浏览权限',
+ date: '2025-05-18 08:25:40',
+ enable: false
+ },
+ {
+ roleName: '系统维护员',
+ roleCode: 'R_MAINTAINER',
+ des: '负责系统维护和更新',
+ date: '2025-05-18 09:50:12',
+ enable: true
+ },
+ {
+ roleName: '项目经理',
+ roleCode: 'R_PM',
+ des: '管理项目相关权限',
+ date: '2025-05-19 13:40:35',
+ enable: true
+ }
+]
diff --git a/adminSystem/src/mock/upgrade/changeLog.ts b/adminSystem/src/mock/upgrade/changeLog.ts
new file mode 100644
index 0000000..dd6b772
--- /dev/null
+++ b/adminSystem/src/mock/upgrade/changeLog.ts
@@ -0,0 +1,12 @@
+import { ref } from 'vue'
+
+interface UpgradeLog {
+ version: string // 版本号
+ title: string // 更新标题
+ date: string // 更新日期
+ detail?: string[] // 更新内容
+ requireReLogin?: boolean // 是否需要重新登录
+ remark?: string // 备注
+}
+
+export const upgradeLogList = ref([])
diff --git a/adminSystem/src/plugins/echarts.ts b/adminSystem/src/plugins/echarts.ts
new file mode 100644
index 0000000..4f56d89
--- /dev/null
+++ b/adminSystem/src/plugins/echarts.ts
@@ -0,0 +1,76 @@
+/**
+ * ECharts 插件配置
+ *
+ * 按需导入 ECharts 图表和组件,减小打包体积。
+ * 只注册项目中实际使用的图表类型和组件。
+ *
+ * @module plugins/echarts
+ * @author Art Design Pro Team
+ */
+
+// ECharts 按需导入配置
+import * as echarts from 'echarts/core'
+
+// 导入图表类型
+import {
+ BarChart,
+ LineChart,
+ PieChart,
+ ScatterChart,
+ RadarChart,
+ MapChart,
+ CandlestickChart
+} from 'echarts/charts'
+
+// 导入组件
+import {
+ TitleComponent,
+ TooltipComponent,
+ GridComponent,
+ LegendComponent,
+ DataZoomComponent,
+ MarkPointComponent,
+ MarkLineComponent,
+ ToolboxComponent,
+ BrushComponent,
+ GeoComponent,
+ VisualMapComponent
+} from 'echarts/components'
+
+// 导入渲染器
+import { CanvasRenderer } from 'echarts/renderers'
+
+// 注册必要的组件
+echarts.use([
+ // 图表类型
+ BarChart,
+ LineChart,
+ PieChart,
+ ScatterChart,
+ RadarChart,
+ MapChart,
+ CandlestickChart,
+
+ // 组件
+ TitleComponent,
+ TooltipComponent,
+ GridComponent,
+ LegendComponent,
+ DataZoomComponent,
+ MarkPointComponent,
+ MarkLineComponent,
+ ToolboxComponent,
+ BrushComponent,
+ GeoComponent,
+ VisualMapComponent,
+
+ // 渲染器
+ CanvasRenderer
+])
+
+// 导出 echarts 实例和类型
+export { echarts }
+export type { EChartsOption, BarSeriesOption } from 'echarts'
+
+// 导出常用的图形工具
+export const graphic = echarts.graphic
diff --git a/adminSystem/src/plugins/index.ts b/adminSystem/src/plugins/index.ts
new file mode 100644
index 0000000..4536a86
--- /dev/null
+++ b/adminSystem/src/plugins/index.ts
@@ -0,0 +1,6 @@
+/**
+ * 插件统一导出
+ * 集中管理第三方库的封装和配置
+ */
+
+export * from './echarts'
diff --git a/adminSystem/src/router/core/ComponentLoader.ts b/adminSystem/src/router/core/ComponentLoader.ts
new file mode 100644
index 0000000..8af3ce3
--- /dev/null
+++ b/adminSystem/src/router/core/ComponentLoader.ts
@@ -0,0 +1,82 @@
+/**
+ * 组件加载器
+ *
+ * 负责动态加载 Vue 组件
+ *
+ * @module router/core/ComponentLoader
+ * @author Art Design Pro Team
+ */
+
+import { h } from 'vue'
+
+export class ComponentLoader {
+ private modules: Record Promise>
+
+ constructor() {
+ // 动态导入 views 目录下所有 .vue 组件
+ this.modules = import.meta.glob('../../views/**/*.vue')
+ }
+
+ /**
+ * 加载组件
+ */
+ load(componentPath: string): () => Promise {
+ if (!componentPath) {
+ return this.createEmptyComponent()
+ }
+
+ // 构建可能的路径
+ const fullPath = `../../views${componentPath}.vue`
+ const fullPathWithIndex = `../../views${componentPath}/index.vue`
+
+ // 先尝试直接路径,再尝试添加/index的路径
+ const module = this.modules[fullPath] || this.modules[fullPathWithIndex]
+
+ if (!module) {
+ console.error(
+ `[ComponentLoader] 未找到组件: ${componentPath},尝试过的路径: ${fullPath} 和 ${fullPathWithIndex}`
+ )
+ return this.createErrorComponent(componentPath)
+ }
+
+ return module
+ }
+
+ /**
+ * 加载布局组件
+ */
+ loadLayout(): () => Promise {
+ return () => import('@/views/index/index.vue')
+ }
+
+ /**
+ * 加载 iframe 组件
+ */
+ loadIframe(): () => Promise {
+ return () => import('@/views/outside/Iframe.vue')
+ }
+
+ /**
+ * 创建空组件
+ */
+ private createEmptyComponent(): () => Promise {
+ return () =>
+ Promise.resolve({
+ render() {
+ return h('div', {})
+ }
+ })
+ }
+
+ /**
+ * 创建错误提示组件
+ */
+ private createErrorComponent(componentPath: string): () => Promise {
+ return () =>
+ Promise.resolve({
+ render() {
+ return h('div', { class: 'route-error' }, `组件未找到: ${componentPath}`)
+ }
+ })
+ }
+}
diff --git a/adminSystem/src/router/core/IframeRouteManager.ts b/adminSystem/src/router/core/IframeRouteManager.ts
new file mode 100644
index 0000000..c054ca1
--- /dev/null
+++ b/adminSystem/src/router/core/IframeRouteManager.ts
@@ -0,0 +1,78 @@
+/**
+ * Iframe 路由管理器
+ *
+ * 负责管理 iframe 类型的路由
+ *
+ * @module router/core/IframeRouteManager
+ * @author Art Design Pro Team
+ */
+
+import type { AppRouteRecord } from '@/types/router'
+
+export class IframeRouteManager {
+ private static instance: IframeRouteManager
+ private iframeRoutes: AppRouteRecord[] = []
+
+ private constructor() {}
+
+ static getInstance(): IframeRouteManager {
+ if (!IframeRouteManager.instance) {
+ IframeRouteManager.instance = new IframeRouteManager()
+ }
+ return IframeRouteManager.instance
+ }
+
+ /**
+ * 添加 iframe 路由
+ */
+ add(route: AppRouteRecord): void {
+ if (!this.iframeRoutes.find((r) => r.path === route.path)) {
+ this.iframeRoutes.push(route)
+ }
+ }
+
+ /**
+ * 获取所有 iframe 路由
+ */
+ getAll(): AppRouteRecord[] {
+ return this.iframeRoutes
+ }
+
+ /**
+ * 根据路径查找 iframe 路由
+ */
+ findByPath(path: string): AppRouteRecord | undefined {
+ return this.iframeRoutes.find((route) => route.path === path)
+ }
+
+ /**
+ * 清空所有 iframe 路由
+ */
+ clear(): void {
+ this.iframeRoutes = []
+ }
+
+ /**
+ * 保存到 sessionStorage
+ */
+ save(): void {
+ if (this.iframeRoutes.length > 0) {
+ sessionStorage.setItem('iframeRoutes', JSON.stringify(this.iframeRoutes))
+ }
+ }
+
+ /**
+ * 从 sessionStorage 加载
+ */
+ load(): void {
+ try {
+ const data = sessionStorage.getItem('iframeRoutes')
+ if (data) {
+ this.iframeRoutes = JSON.parse(data)
+ }
+ } catch (error) {
+ console.error('[IframeRouteManager] 加载 iframe 路由失败:', error)
+ this.iframeRoutes = []
+ }
+ }
+}
diff --git a/adminSystem/src/router/core/MenuProcessor.ts b/adminSystem/src/router/core/MenuProcessor.ts
new file mode 100644
index 0000000..57bf183
--- /dev/null
+++ b/adminSystem/src/router/core/MenuProcessor.ts
@@ -0,0 +1,241 @@
+/**
+ * 菜单处理器
+ *
+ * 负责菜单数据的获取、过滤和处理
+ *
+ * @module router/core/MenuProcessor
+ * @author Art Design Pro Team
+ */
+
+import type { AppRouteRecord } from '@/types/router'
+import { useUserStore } from '@/store/modules/user'
+import { useAppMode } from '@/hooks/core/useAppMode'
+import { fetchGetMenuList } from '@/api/system-manage'
+import { asyncRoutes } from '../routes/asyncRoutes'
+import { RoutesAlias } from '../routesAlias'
+import { formatMenuTitle } from '@/utils'
+
+export class MenuProcessor {
+ /**
+ * 获取菜单数据
+ */
+ async getMenuList(): Promise {
+ const { isFrontendMode } = useAppMode()
+
+ let menuList: AppRouteRecord[]
+ if (isFrontendMode.value) {
+ menuList = await this.processFrontendMenu()
+ } else {
+ menuList = await this.processBackendMenu()
+ }
+
+ // 在规范化路径之前,验证原始路径配置
+ this.validateMenuPaths(menuList)
+
+ // 规范化路径(将相对路径转换为完整路径)
+ return this.normalizeMenuPaths(menuList)
+ }
+
+ /**
+ * 处理前端控制模式的菜单
+ */
+ private async processFrontendMenu(): Promise {
+ const userStore = useUserStore()
+ const roles = userStore.info?.roles
+
+ let menuList = [...asyncRoutes]
+
+ // 根据角色过滤菜单
+ if (roles && roles.length > 0) {
+ menuList = this.filterMenuByRoles(menuList, roles)
+ }
+
+ return this.filterEmptyMenus(menuList)
+ }
+
+ /**
+ * 处理后端控制模式的菜单
+ */
+ private async processBackendMenu(): Promise {
+ const list = await fetchGetMenuList()
+ return this.filterEmptyMenus(list)
+ }
+
+ /**
+ * 根据角色过滤菜单
+ */
+ private filterMenuByRoles(menu: AppRouteRecord[], roles: string[]): AppRouteRecord[] {
+ return menu.reduce((acc: AppRouteRecord[], item) => {
+ const itemRoles = item.meta?.roles
+ const hasPermission = !itemRoles || itemRoles.some((role) => roles?.includes(role))
+
+ if (hasPermission) {
+ const filteredItem = { ...item }
+ if (filteredItem.children?.length) {
+ filteredItem.children = this.filterMenuByRoles(filteredItem.children, roles)
+ }
+ acc.push(filteredItem)
+ }
+
+ return acc
+ }, [])
+ }
+
+ /**
+ * 递归过滤空菜单项
+ */
+ private filterEmptyMenus(menuList: AppRouteRecord[]): AppRouteRecord[] {
+ return menuList
+ .map((item) => {
+ // 如果有子菜单,先递归过滤子菜单
+ if (item.children && item.children.length > 0) {
+ const filteredChildren = this.filterEmptyMenus(item.children)
+ return {
+ ...item,
+ children: filteredChildren
+ }
+ }
+ return item
+ })
+ .filter((item) => {
+ // 如果定义了 children 属性(即使是空数组),说明这是一个目录菜单,应该保留
+ if ('children' in item) {
+ return true
+ }
+
+ // 如果有外链或 iframe,保留
+ if (item.meta?.isIframe === true || item.meta?.link) {
+ return true
+ }
+
+ // 如果有有效的 component,保留
+ if (item.component && item.component !== '' && item.component !== RoutesAlias.Layout) {
+ return true
+ }
+
+ // 其他情况过滤掉
+ return false
+ })
+ }
+
+ /**
+ * 验证菜单列表是否有效
+ */
+ validateMenuList(menuList: AppRouteRecord[]): boolean {
+ return Array.isArray(menuList) && menuList.length > 0
+ }
+
+ /**
+ * 规范化菜单路径
+ * 将相对路径转换为完整路径,确保菜单跳转正确
+ */
+ private normalizeMenuPaths(menuList: AppRouteRecord[], parentPath = ''): AppRouteRecord[] {
+ return menuList.map((item) => {
+ // 构建完整路径
+ const fullPath = this.buildFullPath(item.path || '', parentPath)
+
+ // 递归处理子菜单
+ const children = item.children?.length
+ ? this.normalizeMenuPaths(item.children, fullPath)
+ : item.children
+
+ return {
+ ...item,
+ path: fullPath,
+ children
+ }
+ })
+ }
+
+ /**
+ * 验证菜单路径配置
+ * 检测非一级菜单是否错误使用了 / 开头的路径
+ */
+ /**
+ * 验证菜单路径配置
+ * 检测非一级菜单是否错误使用了 / 开头的路径
+ */
+ private validateMenuPaths(menuList: AppRouteRecord[], level = 1): void {
+ menuList.forEach((route) => {
+ if (!route.children?.length) return
+
+ const parentName = String(route.name || route.path || '未知路由')
+
+ route.children.forEach((child) => {
+ const childPath = child.path || ''
+
+ // 跳过合法的绝对路径:外部链接和 iframe 路由
+ if (this.isValidAbsolutePath(childPath)) return
+
+ // 检测非法的绝对路径
+ if (childPath.startsWith('/')) {
+ this.logPathError(child, childPath, parentName, level)
+ }
+ })
+
+ // 递归检查更深层级的子路由
+ this.validateMenuPaths(route.children, level + 1)
+ })
+ }
+
+ /**
+ * 判断是否为合法的绝对路径
+ */
+ private isValidAbsolutePath(path: string): boolean {
+ return (
+ path.startsWith('http://') ||
+ path.startsWith('https://') ||
+ path.startsWith('/outside/iframe/')
+ )
+ }
+
+ /**
+ * 输出路径配置错误日志
+ */
+ private logPathError(
+ route: AppRouteRecord,
+ path: string,
+ parentName: string,
+ level: number
+ ): void {
+ const routeName = String(route.name || path || '未知路由')
+ const menuTitle = route.meta?.title || routeName
+ const suggestedPath = path.split('/').pop() || path.slice(1)
+
+ console.error(
+ `[路由配置错误] 菜单 "${formatMenuTitle(menuTitle)}" (name: ${routeName}, path: ${path}) 配置错误\n` +
+ ` 位置: ${parentName} > ${routeName}\n` +
+ ` 问题: ${level + 1}级菜单的 path 不能以 / 开头\n` +
+ ` 当前配置: path: '${path}'\n` +
+ ` 应该改为: path: '${suggestedPath}'`
+ )
+ }
+
+ /**
+ * 构建完整路径
+ */
+ private buildFullPath(path: string, parentPath: string): string {
+ if (!path) return ''
+
+ // 外部链接直接返回
+ if (path.startsWith('http://') || path.startsWith('https://')) {
+ return path
+ }
+
+ // 如果已经是绝对路径,直接返回
+ if (path.startsWith('/')) {
+ return path
+ }
+
+ // 拼接父路径和当前路径
+ if (parentPath) {
+ // 移除父路径末尾的斜杠,移除子路径开头的斜杠,然后拼接
+ const cleanParent = parentPath.replace(/\/$/, '')
+ const cleanChild = path.replace(/^\//, '')
+ return `${cleanParent}/${cleanChild}`
+ }
+
+ // 没有父路径,添加前导斜杠
+ return `/${path}`
+ }
+}
diff --git a/adminSystem/src/router/core/RoutePermissionValidator.ts b/adminSystem/src/router/core/RoutePermissionValidator.ts
new file mode 100644
index 0000000..c33e663
--- /dev/null
+++ b/adminSystem/src/router/core/RoutePermissionValidator.ts
@@ -0,0 +1,119 @@
+/**
+ * 路由权限验证模块
+ *
+ * 提供路由权限验证和路径检查功能
+ *
+ * ## 主要功能
+ *
+ * - 验证路径是否在用户菜单权限中
+ * - 构建菜单路径集合(扁平化处理)
+ * - 支持动态路由参数匹配
+ * - 路径前缀匹配
+ *
+ * ## 使用场景
+ *
+ * - 路由守卫中验证用户权限
+ * - 动态路由注册后的权限检查
+ * - 防止用户访问无权限的页面
+ *
+ * @module router/core/RoutePermissionValidator
+ * @author Art Design Pro Team
+ */
+
+import type { AppRouteRecord } from '@/types/router'
+
+/**
+ * 路由权限验证器
+ */
+export class RoutePermissionValidator {
+ /**
+ * 验证路径是否在用户菜单权限中
+ * @param targetPath 目标路径
+ * @param menuList 菜单列表
+ * @returns 是否有权限访问
+ */
+ static hasPermission(targetPath: string, menuList: AppRouteRecord[]): boolean {
+ // 根路径始终允许访问
+ if (targetPath === '/') {
+ return true
+ }
+
+ // 构建路径集合
+ const pathSet = this.buildMenuPathSet(menuList)
+
+ // 检查路径是否在集合中(精确匹配或前缀匹配)
+ return pathSet.has(targetPath) || this.checkPathPrefix(targetPath, pathSet)
+ }
+
+ /**
+ * 构建菜单路径集合(扁平化处理)
+ * @param menuList 菜单列表
+ * @param pathSet 路径集合
+ * @returns 路径集合
+ */
+ static buildMenuPathSet(
+ menuList: AppRouteRecord[],
+ pathSet: Set = new Set()
+ ): Set {
+ if (!Array.isArray(menuList) || menuList.length === 0) {
+ return pathSet
+ }
+
+ for (const menuItem of menuList) {
+ // 跳过隐藏的菜单项
+ if (menuItem.meta?.isHide || !menuItem.path) {
+ continue
+ }
+
+ // 标准化路径并添加到集合
+ const menuPath = menuItem.path.startsWith('/') ? menuItem.path : `/${menuItem.path}`
+ pathSet.add(menuPath)
+
+ // 递归处理子菜单
+ if (menuItem.children?.length) {
+ this.buildMenuPathSet(menuItem.children, pathSet)
+ }
+ }
+
+ return pathSet
+ }
+
+ /**
+ * 检查目标路径是否匹配集合中的某个路径前缀
+ * 用于支持动态路由参数匹配,如 /user/123 匹配 /user
+ * @param targetPath 目标路径
+ * @param pathSet 路径集合
+ * @returns 是否匹配
+ */
+ static checkPathPrefix(targetPath: string, pathSet: Set): boolean {
+ // 遍历路径集合,检查是否有前缀匹配
+ for (const menuPath of pathSet) {
+ if (targetPath.startsWith(`${menuPath}/`)) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /**
+ * 验证并返回有效的路径
+ * 如果目标路径无权限,返回首页路径
+ * @param targetPath 目标路径
+ * @param menuList 菜单列表
+ * @param homePath 首页路径
+ * @returns 验证后的路径
+ */
+ static validatePath(
+ targetPath: string,
+ menuList: AppRouteRecord[],
+ homePath: string = '/'
+ ): { path: string; hasPermission: boolean } {
+ const hasPermission = this.hasPermission(targetPath, menuList)
+
+ if (hasPermission) {
+ return { path: targetPath, hasPermission: true }
+ }
+
+ return { path: homePath, hasPermission: false }
+ }
+}
diff --git a/adminSystem/src/router/core/RouteRegistry.ts b/adminSystem/src/router/core/RouteRegistry.ts
new file mode 100644
index 0000000..e1acb9e
--- /dev/null
+++ b/adminSystem/src/router/core/RouteRegistry.ts
@@ -0,0 +1,90 @@
+/**
+ * 路由注册核心类
+ *
+ * 负责动态路由的注册、验证和管理
+ *
+ * @module router/core/RouteRegistry
+ * @author Art Design Pro Team
+ */
+
+import type { Router, RouteRecordRaw } from 'vue-router'
+import type { AppRouteRecord } from '@/types/router'
+import { ComponentLoader } from './ComponentLoader'
+import { RouteValidator } from './RouteValidator'
+import { RouteTransformer } from './RouteTransformer'
+
+export class RouteRegistry {
+ private router: Router
+ private componentLoader: ComponentLoader
+ private validator: RouteValidator
+ private transformer: RouteTransformer
+ private removeRouteFns: (() => void)[] = []
+ private registered = false
+
+ constructor(router: Router) {
+ this.router = router
+ this.componentLoader = new ComponentLoader()
+ this.validator = new RouteValidator()
+ this.transformer = new RouteTransformer(this.componentLoader)
+ }
+
+ /**
+ * 注册动态路由
+ */
+ register(menuList: AppRouteRecord[]): void {
+ if (this.registered) {
+ console.warn('[RouteRegistry] 路由已注册,跳过重复注册')
+ return
+ }
+
+ // 验证路由配置
+ const validationResult = this.validator.validate(menuList)
+ if (!validationResult.valid) {
+ throw new Error(`路由配置验证失败: ${validationResult.errors.join(', ')}`)
+ }
+
+ // 转换并注册路由
+ const removeRouteFns: (() => void)[] = []
+
+ menuList.forEach((route) => {
+ if (route.name && !this.router.hasRoute(route.name)) {
+ const routeConfig = this.transformer.transform(route)
+ const removeRouteFn = this.router.addRoute(routeConfig as RouteRecordRaw)
+ removeRouteFns.push(removeRouteFn)
+ }
+ })
+
+ this.removeRouteFns = removeRouteFns
+ this.registered = true
+ }
+
+ /**
+ * 移除所有动态路由
+ */
+ unregister(): void {
+ this.removeRouteFns.forEach((fn) => fn())
+ this.removeRouteFns = []
+ this.registered = false
+ }
+
+ /**
+ * 检查是否已注册
+ */
+ isRegistered(): boolean {
+ return this.registered
+ }
+
+ /**
+ * 获取移除函数列表(用于 store 管理)
+ */
+ getRemoveRouteFns(): (() => void)[] {
+ return this.removeRouteFns
+ }
+
+ /**
+ * 标记为已注册(用于错误处理场景,避免重复请求)
+ */
+ markAsRegistered(): void {
+ this.registered = true
+ }
+}
diff --git a/adminSystem/src/router/core/RouteTransformer.ts b/adminSystem/src/router/core/RouteTransformer.ts
new file mode 100644
index 0000000..0f6900c
--- /dev/null
+++ b/adminSystem/src/router/core/RouteTransformer.ts
@@ -0,0 +1,132 @@
+/**
+ * 路由转换器
+ *
+ * 负责将菜单数据转换为 Vue Router 路由配置
+ *
+ * @module router/core/RouteTransformer
+ * @author Art Design Pro Team
+ */
+
+import type { RouteRecordRaw } from 'vue-router'
+import type { AppRouteRecord } from '@/types/router'
+import { ComponentLoader } from './ComponentLoader'
+import { IframeRouteManager } from './IframeRouteManager'
+
+interface ConvertedRoute extends Omit {
+ id?: number
+ children?: ConvertedRoute[]
+ component?: RouteRecordRaw['component'] | (() => Promise)
+}
+
+export class RouteTransformer {
+ private componentLoader: ComponentLoader
+ private iframeManager: IframeRouteManager
+
+ constructor(componentLoader: ComponentLoader) {
+ this.componentLoader = componentLoader
+ this.iframeManager = IframeRouteManager.getInstance()
+ }
+
+ /**
+ * 转换路由配置
+ */
+ transform(route: AppRouteRecord, depth = 0): ConvertedRoute {
+ const { component, children, ...routeConfig } = route
+
+ // 基础路由配置
+ const converted: ConvertedRoute = {
+ ...routeConfig,
+ component: undefined
+ }
+
+ // 处理不同类型的路由
+ if (route.meta.isIframe) {
+ this.handleIframeRoute(converted, route, depth)
+ } else if (this.isFirstLevelRoute(route, depth)) {
+ this.handleFirstLevelRoute(converted, route, component as string)
+ } else {
+ this.handleNormalRoute(converted, component as string)
+ }
+
+ // 递归处理子路由
+ if (children?.length) {
+ converted.children = children.map((child) => this.transform(child, depth + 1))
+ }
+
+ return converted
+ }
+
+ /**
+ * 判断是否为一级路由(需要 Layout 包裹)
+ */
+ private isFirstLevelRoute(route: AppRouteRecord, depth: number): boolean {
+ return depth === 0 && (!route.children || route.children.length === 0)
+ }
+
+ /**
+ * 处理 iframe 类型路由
+ */
+ private handleIframeRoute(
+ targetRoute: ConvertedRoute,
+ sourceRoute: AppRouteRecord,
+ depth: number
+ ): void {
+ if (depth === 0) {
+ // 顶级 iframe:用 Layout 包裹
+ targetRoute.component = this.componentLoader.loadLayout()
+ targetRoute.path = this.extractFirstSegment(sourceRoute.path || '')
+ targetRoute.name = ''
+
+ targetRoute.children = [
+ {
+ ...sourceRoute,
+ component: this.componentLoader.loadIframe()
+ } as ConvertedRoute
+ ]
+ } else {
+ // 非顶级(嵌套)iframe:直接使用 Iframe.vue
+ targetRoute.component = this.componentLoader.loadIframe()
+ }
+
+ // 记录 iframe 路由
+ this.iframeManager.add(sourceRoute)
+ }
+
+ /**
+ * 处理一级菜单路由
+ */
+ private handleFirstLevelRoute(
+ converted: ConvertedRoute,
+ route: AppRouteRecord,
+ component: string | undefined
+ ): void {
+ converted.component = this.componentLoader.loadLayout()
+ converted.path = this.extractFirstSegment(route.path || '')
+ converted.name = ''
+ route.meta.isFirstLevel = true
+
+ converted.children = [
+ {
+ ...route,
+ component: component ? this.componentLoader.load(component) : undefined
+ } as ConvertedRoute
+ ]
+ }
+
+ /**
+ * 处理普通路由
+ */
+ private handleNormalRoute(converted: ConvertedRoute, component: string | undefined): void {
+ if (component) {
+ converted.component = this.componentLoader.load(component)
+ }
+ }
+
+ /**
+ * 提取路径的第一段
+ */
+ private extractFirstSegment(path: string): string {
+ const segments = path.split('/').filter(Boolean)
+ return segments.length > 0 ? `/${segments[0]}` : '/'
+ }
+}
diff --git a/adminSystem/src/router/core/RouteValidator.ts b/adminSystem/src/router/core/RouteValidator.ts
new file mode 100644
index 0000000..f8e58fc
--- /dev/null
+++ b/adminSystem/src/router/core/RouteValidator.ts
@@ -0,0 +1,187 @@
+/**
+ * 路由验证器
+ *
+ * 负责验证路由配置的合法性
+ *
+ * @module router/core/RouteValidator
+ * @author Art Design Pro Team
+ */
+
+import type { AppRouteRecord } from '@/types/router'
+import { RoutesAlias } from '../routesAlias'
+
+export interface ValidationResult {
+ valid: boolean
+ errors: string[]
+ warnings: string[]
+}
+
+export class RouteValidator {
+ // 用于记录已经提示过的路由,避免重复提示
+ private warnedRoutes = new Set()
+
+ /**
+ * 验证路由配置
+ */
+ validate(routes: AppRouteRecord[]): ValidationResult {
+ const errors: string[] = []
+ const warnings: string[] = []
+
+ // 检测重复路由
+ this.checkDuplicates(routes, errors, warnings)
+
+ // 检测组件配置
+ this.checkComponents(routes, errors, warnings)
+
+ // 检测嵌套菜单的 /index/index 配置
+ this.checkNestedIndexComponent(routes)
+
+ return {
+ valid: errors.length === 0,
+ errors,
+ warnings
+ }
+ }
+
+ /**
+ * 检测重复路由
+ */
+ private checkDuplicates(
+ routes: AppRouteRecord[],
+ errors: string[],
+ warnings: string[],
+ parentPath = ''
+ ): void {
+ const routeNameMap = new Map()
+ const componentPathMap = new Map()
+
+ const checkRoutes = (routes: AppRouteRecord[], parentPath = '') => {
+ routes.forEach((route) => {
+ const currentPath = route.path || ''
+ const fullPath = this.resolvePath(parentPath, currentPath)
+
+ // 名称重复检测
+ if (route.name) {
+ const routeName = String(route.name)
+ if (routeNameMap.has(routeName)) {
+ warnings.push(`路由名称重复: "${routeName}" (${fullPath})`)
+ } else {
+ routeNameMap.set(routeName, fullPath)
+ }
+ }
+
+ // 组件路径重复检测
+ if (route.component && typeof route.component === 'string') {
+ const componentPath = route.component
+ if (componentPath !== RoutesAlias.Layout) {
+ const componentKey = `${parentPath}:${componentPath}`
+ if (componentPathMap.has(componentKey)) {
+ warnings.push(`组件路径重复: "${componentPath}" (${fullPath})`)
+ } else {
+ componentPathMap.set(componentKey, fullPath)
+ }
+ }
+ }
+
+ // 递归处理子路由
+ if (route.children?.length) {
+ checkRoutes(route.children, fullPath)
+ }
+ })
+ }
+
+ checkRoutes(routes, parentPath)
+ }
+
+ /**
+ * 检测组件配置
+ */
+ private checkComponents(
+ routes: AppRouteRecord[],
+ errors: string[],
+ warnings: string[],
+ parentPath = ''
+ ): void {
+ routes.forEach((route) => {
+ const hasExternalLink = !!route.meta?.link?.trim()
+ const hasChildren = Array.isArray(route.children) && route.children.length > 0
+ const routePath = route.path || '[未定义路径]'
+ const isIframe = route.meta?.isIframe
+
+ // 如果配置了 component,则无需校验
+ if (route.component) {
+ // 递归检查子路由
+ if (route.children?.length) {
+ const fullPath = this.resolvePath(parentPath, route.path || '')
+ this.checkComponents(route.children, errors, warnings, fullPath)
+ }
+ return
+ }
+
+ // 一级菜单:必须指定 Layout,除非是外链或 iframe
+ if (parentPath === '' && !hasExternalLink && !isIframe) {
+ errors.push(`一级菜单(${routePath}) 缺少 component,必须指向 ${RoutesAlias.Layout}`)
+ return
+ }
+
+ // 非一级菜单:如果既不是外链、iframe,也没有子路由,则必须配置 component
+ if (!hasExternalLink && !isIframe && !hasChildren) {
+ errors.push(`路由(${routePath}) 缺少 component 配置`)
+ }
+
+ // 递归检查子路由
+ if (route.children?.length) {
+ const fullPath = this.resolvePath(parentPath, route.path || '')
+ this.checkComponents(route.children, errors, warnings, fullPath)
+ }
+ })
+ }
+
+ /**
+ * 检测嵌套菜单的 Layout 组件配置
+ * 只有一级菜单才能使用 Layout,二级及以下菜单不能使用
+ */
+ private checkNestedIndexComponent(routes: AppRouteRecord[], level = 1): void {
+ routes.forEach((route) => {
+ // 检查二级及以下菜单是否错误使用了 Layout
+ if (level > 1 && route.component === RoutesAlias.Layout) {
+ this.logLayoutError(route, level)
+ }
+
+ // 递归检查子路由
+ if (route.children?.length) {
+ this.checkNestedIndexComponent(route.children, level + 1)
+ }
+ })
+ }
+
+ /**
+ * 输出 Layout 组件配置错误日志
+ */
+ private logLayoutError(route: AppRouteRecord, level: number): void {
+ const routeName = String(route.name || route.path || '未知路由')
+ const routeKey = `${routeName}_${route.path}`
+
+ // 避免重复提示
+ if (this.warnedRoutes.has(routeKey)) return
+ this.warnedRoutes.add(routeKey)
+
+ const menuTitle = route.meta?.title || routeName
+ const routePath = route.path || '/'
+
+ console.error(
+ `[路由配置错误] 菜单 "${menuTitle}" (name: ${routeName}, path: ${routePath}) 配置错误\n` +
+ ` 问题: ${level}级菜单不能使用 ${RoutesAlias.Layout} 作为 component\n` +
+ ` 说明: 只有一级菜单才能使用 ${RoutesAlias.Layout},二级及以下菜单应该指向具体的组件路径\n` +
+ ` 当前配置: component: '${RoutesAlias.Layout}'\n` +
+ ` 应该改为: component: '/your/component/path' 或留空 ''(如果是目录菜单)`
+ )
+ }
+
+ /**
+ * 路径解析
+ */
+ private resolvePath(parent: string, child: string): string {
+ return [parent.replace(/\/$/, ''), child.replace(/^\//, '')].filter(Boolean).join('/')
+ }
+}
diff --git a/adminSystem/src/router/core/index.ts b/adminSystem/src/router/core/index.ts
new file mode 100644
index 0000000..fcfecfc
--- /dev/null
+++ b/adminSystem/src/router/core/index.ts
@@ -0,0 +1,14 @@
+/**
+ * 路由核心模块导出
+ *
+ * @module router/core
+ * @author Art Design Pro Team
+ */
+
+export { RouteRegistry } from './RouteRegistry'
+export { ComponentLoader } from './ComponentLoader'
+export { RouteValidator } from './RouteValidator'
+export { RouteTransformer } from './RouteTransformer'
+export { IframeRouteManager } from './IframeRouteManager'
+export { MenuProcessor } from './MenuProcessor'
+export { RoutePermissionValidator } from './RoutePermissionValidator'
diff --git a/adminSystem/src/router/guards/afterEach.ts b/adminSystem/src/router/guards/afterEach.ts
new file mode 100644
index 0000000..d60572d
--- /dev/null
+++ b/adminSystem/src/router/guards/afterEach.ts
@@ -0,0 +1,34 @@
+import { nextTick } from 'vue'
+import { useSettingStore } from '@/store/modules/setting'
+import { Router } from 'vue-router'
+import NProgress from 'nprogress'
+import { useCommon } from '@/hooks/core/useCommon'
+import { loadingService } from '@/utils/ui'
+import { getPendingLoading, resetPendingLoading } from './beforeEach'
+
+/** 路由全局后置守卫 */
+export function setupAfterEachGuard(router: Router) {
+ const { scrollToTop } = useCommon()
+
+ router.afterEach(() => {
+ scrollToTop()
+
+ // 关闭进度条
+ const settingStore = useSettingStore()
+ if (settingStore.showNprogress) {
+ NProgress.done()
+ // 确保进度条完全移除,避免残影
+ setTimeout(() => {
+ NProgress.remove()
+ }, 600)
+ }
+
+ // 关闭 loading 效果
+ if (getPendingLoading()) {
+ nextTick(() => {
+ loadingService.hideLoading()
+ resetPendingLoading()
+ })
+ }
+ })
+}
diff --git a/adminSystem/src/router/guards/beforeEach.ts b/adminSystem/src/router/guards/beforeEach.ts
new file mode 100644
index 0000000..0571e4f
--- /dev/null
+++ b/adminSystem/src/router/guards/beforeEach.ts
@@ -0,0 +1,360 @@
+/**
+ * 路由全局前置守卫模块
+ *
+ * 提供完整的路由导航守卫功能
+ *
+ * ## 主要功能
+ *
+ * - 登录状态验证和重定向
+ * - 动态路由注册和权限控制
+ * - 菜单数据获取和处理(前端/后端模式)
+ * - 用户信息获取和缓存
+ * - 页面标题设置
+ * - 工作标签页管理
+ * - 进度条和加载动画控制
+ * - 静态路由识别和处理
+ * - 错误处理和异常跳转
+ *
+ * ## 使用场景
+ *
+ * - 路由跳转前的权限验证
+ * - 动态菜单加载和路由注册
+ * - 用户登录状态管理
+ * - 页面访问控制
+ * - 路由级别的加载状态管理
+ *
+ * ## 工作流程
+ *
+ * 1. 检查登录状态,未登录跳转到登录页
+ * 2. 首次访问时获取用户信息和菜单数据
+ * 3. 根据权限动态注册路由
+ * 4. 设置页面标题和工作标签页
+ * 5. 处理根路径重定向到首页
+ * 6. 未匹配路由跳转到 404 页面
+ *
+ * @module router/guards/beforeEach
+ * @author Art Design Pro Team
+ */
+import type { Router, RouteLocationNormalized, NavigationGuardNext } from 'vue-router'
+import { nextTick } from 'vue'
+import NProgress from 'nprogress'
+import { useSettingStore } from '@/store/modules/setting'
+import { useUserStore } from '@/store/modules/user'
+import { useMenuStore } from '@/store/modules/menu'
+import { setWorktab } from '@/utils/navigation'
+import { setPageTitle } from '@/utils/router'
+import { RoutesAlias } from '../routesAlias'
+import { staticRoutes } from '../routes/staticRoutes'
+import { loadingService } from '@/utils/ui'
+import { useCommon } from '@/hooks/core/useCommon'
+import { useWorktabStore } from '@/store/modules/worktab'
+import { fetchGetUserInfo } from '@/api/auth'
+import { ApiStatus } from '@/utils/http/status'
+import { isHttpError } from '@/utils/http/error'
+import { RouteRegistry, MenuProcessor, IframeRouteManager, RoutePermissionValidator } from '../core'
+
+// 路由注册器实例
+let routeRegistry: RouteRegistry | null = null
+
+// 菜单处理器实例
+const menuProcessor = new MenuProcessor()
+
+// 跟踪是否需要关闭 loading
+let pendingLoading = false
+
+/**
+ * 获取 pendingLoading 状态
+ */
+export function getPendingLoading(): boolean {
+ return pendingLoading
+}
+
+/**
+ * 重置 pendingLoading 状态
+ */
+export function resetPendingLoading(): void {
+ pendingLoading = false
+}
+
+/**
+ * 设置路由全局前置守卫
+ */
+export function setupBeforeEachGuard(router: Router): void {
+ // 初始化路由注册器
+ routeRegistry = new RouteRegistry(router)
+
+ router.beforeEach(
+ async (
+ to: RouteLocationNormalized,
+ from: RouteLocationNormalized,
+ next: NavigationGuardNext
+ ) => {
+ try {
+ await handleRouteGuard(to, from, next, router)
+ } catch (error) {
+ console.error('[RouteGuard] 路由守卫处理失败:', error)
+ closeLoading()
+ next({ name: 'Exception500' })
+ }
+ }
+ )
+}
+
+/**
+ * 关闭 loading 效果
+ */
+function closeLoading(): void {
+ if (pendingLoading) {
+ nextTick(() => {
+ loadingService.hideLoading()
+ pendingLoading = false
+ })
+ }
+}
+
+/**
+ * 处理路由守卫逻辑
+ */
+async function handleRouteGuard(
+ to: RouteLocationNormalized,
+ from: RouteLocationNormalized,
+ next: NavigationGuardNext,
+ router: Router
+): Promise {
+ const settingStore = useSettingStore()
+ const userStore = useUserStore()
+
+ // 启动进度条
+ if (settingStore.showNprogress) {
+ NProgress.start()
+ }
+
+ // 1. 检查登录状态
+ if (!handleLoginStatus(to, userStore, next)) {
+ return
+ }
+
+ // 2. 处理动态路由注册
+ if (!routeRegistry?.isRegistered() && userStore.isLogin) {
+ await handleDynamicRoutes(to, next, router)
+ return
+ }
+
+ // 3. 处理根路径重定向
+ if (handleRootPathRedirect(to, next)) {
+ return
+ }
+
+ // 4. 处理已匹配的路由
+ if (to.matched.length > 0) {
+ setWorktab(to)
+ setPageTitle(to)
+ next()
+ return
+ }
+
+ // 5. 未匹配到路由,跳转到 404
+ next({ name: 'Exception404' })
+}
+
+/**
+ * 处理登录状态
+ * @returns true 表示可以继续,false 表示已处理跳转
+ */
+function handleLoginStatus(
+ to: RouteLocationNormalized,
+ userStore: ReturnType,
+ next: NavigationGuardNext
+): boolean {
+ // 已登录或访问登录页或静态路由,直接放行
+ if (userStore.isLogin || to.path === RoutesAlias.Login || isStaticRoute(to.path)) {
+ return true
+ }
+
+ // 未登录且访问需要权限的页面,跳转到登录页并携带 redirect 参数
+ userStore.logOut()
+ next({
+ name: 'Login',
+ query: { redirect: to.fullPath }
+ })
+ return false
+}
+
+/**
+ * 检查路由是否为静态路由
+ */
+function isStaticRoute(path: string): boolean {
+ const checkRoute = (routes: any[], targetPath: string): boolean => {
+ return routes.some((route) => {
+ // 处理动态路由参数匹配
+ const routePath = route.path
+ const pattern = routePath.replace(/:[^/]+/g, '[^/]+').replace(/\*/g, '.*')
+ const regex = new RegExp(`^${pattern}$`)
+
+ if (regex.test(targetPath)) {
+ return true
+ }
+ if (route.children && route.children.length > 0) {
+ return checkRoute(route.children, targetPath)
+ }
+ return false
+ })
+ }
+
+ return checkRoute(staticRoutes, path)
+}
+
+/**
+ * 处理动态路由注册
+ */
+async function handleDynamicRoutes(
+ to: RouteLocationNormalized,
+ next: NavigationGuardNext,
+ router: Router
+): Promise {
+ // 显示 loading
+ pendingLoading = true
+ loadingService.showLoading()
+
+ try {
+ // 1. 获取用户信息
+ await fetchUserInfo()
+
+ // 2. 获取菜单数据
+ const menuList = await menuProcessor.getMenuList()
+
+ // 3. 验证菜单数据
+ if (!menuProcessor.validateMenuList(menuList)) {
+ throw new Error('获取菜单列表失败,请重新登录')
+ }
+
+ // 4. 注册动态路由
+ routeRegistry?.register(menuList)
+
+ // 5. 保存菜单数据到 store
+ const menuStore = useMenuStore()
+ menuStore.setMenuList(menuList)
+ menuStore.addRemoveRouteFns(routeRegistry?.getRemoveRouteFns() || [])
+
+ // 6. 保存 iframe 路由
+ IframeRouteManager.getInstance().save()
+
+ // 7. 验证工作标签页
+ useWorktabStore().validateWorktabs(router)
+
+ // 8. 验证目标路径权限
+ const { homePath } = useCommon()
+ const { path: validatedPath, hasPermission } = RoutePermissionValidator.validatePath(
+ to.path,
+ menuList,
+ homePath.value || '/'
+ )
+
+ // 9. 重新导航到目标路由
+ if (!hasPermission) {
+ // 无权限访问,跳转到首页
+ closeLoading()
+
+ // 输出警告信息
+ console.warn(`[RouteGuard] 用户无权限访问路径: ${to.path},已跳转到首页`)
+
+ // 直接跳转到首页
+ next({
+ path: validatedPath,
+ replace: true
+ })
+ } else {
+ // 有权限,正常导航
+ next({
+ path: to.path,
+ query: to.query,
+ hash: to.hash,
+ replace: true
+ })
+ }
+ } catch (error) {
+ console.error('[RouteGuard] 动态路由注册失败:', error)
+
+ // 401 错误:axios 拦截器已处理退出登录,取消当前导航
+ if (isUnauthorizedError(error)) {
+ closeLoading()
+ next(false)
+ return
+ }
+
+ // 404 错误:接口不存在,标记路由已注册避免重复请求
+ if (isNotFoundError(error)) {
+ console.error('[RouteGuard] 接口返回 404,请检查后端接口配置')
+ routeRegistry?.markAsRegistered()
+ closeLoading()
+ next({ name: 'Exception404' })
+ return
+ }
+
+ // 其他错误:跳转到 500 页面
+ next({ name: 'Exception500' })
+ }
+}
+
+/**
+ * 获取用户信息
+ *
+ * 注意:每次动态路由注册时都会重新获取用户信息,确保数据最新
+ * 这样可以避免以下问题:
+ * 1. 用户信息过期但仍使用 localStorage 中的旧数据
+ * 2. 权限变更后不能及时更新
+ * 3. 用户信息在后台被修改后前端不同步
+ */
+async function fetchUserInfo(): Promise {
+ const userStore = useUserStore()
+ const data = await fetchGetUserInfo()
+ userStore.setUserInfo(data)
+ // 检查并清理工作台标签页(如果是不同用户登录)
+ userStore.checkAndClearWorktabs()
+}
+
+/**
+ * 重置路由相关状态
+ */
+export function resetRouterState(delay: number): void {
+ setTimeout(() => {
+ routeRegistry?.unregister()
+ IframeRouteManager.getInstance().clear()
+
+ const menuStore = useMenuStore()
+ menuStore.removeAllDynamicRoutes()
+ menuStore.setMenuList([])
+ }, delay)
+}
+
+/**
+ * 处理根路径重定向到首页
+ * @returns true 表示已处理跳转,false 表示无需跳转
+ */
+function handleRootPathRedirect(to: RouteLocationNormalized, next: NavigationGuardNext): boolean {
+ if (to.path !== '/') {
+ return false
+ }
+
+ const { homePath } = useCommon()
+ if (homePath.value && homePath.value !== '/') {
+ next({ path: homePath.value, replace: true })
+ return true
+ }
+
+ return false
+}
+
+/**
+ * 判断是否为未授权错误(401)
+ */
+function isUnauthorizedError(error: unknown): boolean {
+ return isHttpError(error) && error.code === ApiStatus.unauthorized
+}
+
+/**
+ * 判断是否为 404 错误
+ */
+function isNotFoundError(error: unknown): boolean {
+ return isHttpError(error) && error.code === ApiStatus.notFound
+}
diff --git a/adminSystem/src/router/index.ts b/adminSystem/src/router/index.ts
new file mode 100644
index 0000000..286ae58
--- /dev/null
+++ b/adminSystem/src/router/index.ts
@@ -0,0 +1,23 @@
+import type { App } from 'vue'
+import { createRouter, createWebHashHistory } from 'vue-router'
+import { staticRoutes } from './routes/staticRoutes'
+import { configureNProgress } from '@/utils/router'
+import { setupBeforeEachGuard } from './guards/beforeEach'
+import { setupAfterEachGuard } from './guards/afterEach'
+
+// 创建路由实例
+export const router = createRouter({
+ history: createWebHashHistory(),
+ routes: staticRoutes // 静态路由
+})
+
+// 初始化路由
+export function initRouter(app: App): void {
+ configureNProgress() // 顶部进度条
+ setupBeforeEachGuard(router) // 路由前置守卫
+ setupAfterEachGuard(router) // 路由后置守卫
+ app.use(router)
+}
+
+// 主页路径,默认使用菜单第一个有效路径,配置后使用此路径
+export const HOME_PAGE_PATH = ''
diff --git a/adminSystem/src/router/modules/dashboard.ts b/adminSystem/src/router/modules/dashboard.ts
new file mode 100644
index 0000000..5f9c3e9
--- /dev/null
+++ b/adminSystem/src/router/modules/dashboard.ts
@@ -0,0 +1,24 @@
+import { AppRouteRecord } from '@/types/router'
+
+export const dashboardRoutes: AppRouteRecord = {
+ name: 'Dashboard',
+ path: '/dashboard',
+ component: '/index/index',
+ meta: {
+ title: 'menus.dashboard.title',
+ icon: 'ri:pie-chart-line',
+ roles: ['R_SUPER', 'R_ADMIN']
+ },
+ children: [
+ {
+ path: 'console',
+ name: 'Console',
+ component: '/dashboard/console',
+ meta: {
+ title: 'menus.dashboard.console',
+ keepAlive: false,
+ fixedTab: true
+ }
+ }
+ ]
+}
diff --git a/adminSystem/src/router/modules/exception.ts b/adminSystem/src/router/modules/exception.ts
new file mode 100644
index 0000000..07c5604
--- /dev/null
+++ b/adminSystem/src/router/modules/exception.ts
@@ -0,0 +1,46 @@
+import { AppRouteRecord } from '@/types/router'
+
+export const exceptionRoutes: AppRouteRecord = {
+ path: '/exception',
+ name: 'Exception',
+ component: '/index/index',
+ meta: {
+ title: 'menus.exception.title',
+ icon: 'ri:error-warning-line'
+ },
+ children: [
+ {
+ path: '403',
+ name: 'Exception403',
+ component: '/exception/403',
+ meta: {
+ title: 'menus.exception.forbidden',
+ keepAlive: true,
+ isHideTab: true,
+ isFullPage: true
+ }
+ },
+ {
+ path: '404',
+ name: 'Exception404',
+ component: '/exception/404',
+ meta: {
+ title: 'menus.exception.notFound',
+ keepAlive: true,
+ isHideTab: true,
+ isFullPage: true
+ }
+ },
+ {
+ path: '500',
+ name: 'Exception500',
+ component: '/exception/500',
+ meta: {
+ title: 'menus.exception.serverError',
+ keepAlive: true,
+ isHideTab: true,
+ isFullPage: true
+ }
+ }
+ ]
+}
diff --git a/adminSystem/src/router/modules/index.ts b/adminSystem/src/router/modules/index.ts
new file mode 100644
index 0000000..deff162
--- /dev/null
+++ b/adminSystem/src/router/modules/index.ts
@@ -0,0 +1,15 @@
+import { AppRouteRecord } from '@/types/router'
+import { dashboardRoutes } from './dashboard'
+import { systemRoutes } from './system'
+import { resultRoutes } from './result'
+import { exceptionRoutes } from './exception'
+
+/**
+ * 导出所有模块化路由
+ */
+export const routeModules: AppRouteRecord[] = [
+ dashboardRoutes,
+ systemRoutes,
+ resultRoutes,
+ exceptionRoutes
+]
diff --git a/adminSystem/src/router/modules/result.ts b/adminSystem/src/router/modules/result.ts
new file mode 100644
index 0000000..575a2f7
--- /dev/null
+++ b/adminSystem/src/router/modules/result.ts
@@ -0,0 +1,33 @@
+import { AppRouteRecord } from '@/types/router'
+
+export const resultRoutes: AppRouteRecord = {
+ path: '/result',
+ name: 'Result',
+ component: '/index/index',
+ meta: {
+ title: 'menus.result.title',
+ icon: 'ri:checkbox-circle-line'
+ },
+ children: [
+ {
+ path: 'success',
+ name: 'ResultSuccess',
+ component: '/result/success',
+ meta: {
+ title: 'menus.result.success',
+ icon: 'ri:checkbox-circle-line',
+ keepAlive: true
+ }
+ },
+ {
+ path: 'fail',
+ name: 'ResultFail',
+ component: '/result/fail',
+ meta: {
+ title: 'menus.result.fail',
+ icon: 'ri:close-circle-line',
+ keepAlive: true
+ }
+ }
+ ]
+}
diff --git a/adminSystem/src/router/modules/system.ts b/adminSystem/src/router/modules/system.ts
new file mode 100644
index 0000000..16df585
--- /dev/null
+++ b/adminSystem/src/router/modules/system.ts
@@ -0,0 +1,60 @@
+import { AppRouteRecord } from '@/types/router'
+
+export const systemRoutes: AppRouteRecord = {
+ path: '/system',
+ name: 'System',
+ component: '/index/index',
+ meta: {
+ title: 'menus.system.title',
+ icon: 'ri:user-3-line',
+ roles: ['R_SUPER', 'R_ADMIN']
+ },
+ children: [
+ {
+ path: 'user',
+ name: 'User',
+ component: '/system/user',
+ meta: {
+ title: 'menus.system.user',
+ keepAlive: true,
+ roles: ['R_SUPER', 'R_ADMIN']
+ }
+ },
+ {
+ path: 'role',
+ name: 'Role',
+ component: '/system/role',
+ meta: {
+ title: 'menus.system.role',
+ keepAlive: true,
+ roles: ['R_SUPER']
+ }
+ },
+ {
+ path: 'user-center',
+ name: 'UserCenter',
+ component: '/system/user-center',
+ meta: {
+ title: 'menus.system.userCenter',
+ isHide: true,
+ keepAlive: true,
+ isHideTab: true
+ }
+ },
+ {
+ path: 'menu',
+ name: 'Menus',
+ component: '/system/menu',
+ meta: {
+ title: 'menus.system.menu',
+ keepAlive: true,
+ roles: ['R_SUPER'],
+ authList: [
+ { title: '新增', authMark: 'add' },
+ { title: '编辑', authMark: 'edit' },
+ { title: '删除', authMark: 'delete' }
+ ]
+ }
+ }
+ ]
+}
diff --git a/adminSystem/src/router/routes/asyncRoutes.ts b/adminSystem/src/router/routes/asyncRoutes.ts
new file mode 100644
index 0000000..ccf1201
--- /dev/null
+++ b/adminSystem/src/router/routes/asyncRoutes.ts
@@ -0,0 +1,9 @@
+// 权限文档:https://www.artd.pro/docs/zh/guide/in-depth/permission.html
+import { AppRouteRecord } from '@/types/router'
+import { routeModules } from '../modules'
+
+/**
+ * 动态路由(需要权限才能访问的路由)
+ * 用于渲染菜单以及根据菜单权限动态加载路由,如果没有权限无法访问
+ */
+export const asyncRoutes: AppRouteRecord[] = routeModules
diff --git a/adminSystem/src/router/routes/staticRoutes.ts b/adminSystem/src/router/routes/staticRoutes.ts
new file mode 100644
index 0000000..334d0c2
--- /dev/null
+++ b/adminSystem/src/router/routes/staticRoutes.ts
@@ -0,0 +1,72 @@
+import { AppRouteRecordRaw } from '@/utils/router'
+
+/**
+ * 静态路由配置(不需要权限就能访问的路由)
+ *
+ * 属性说明:
+ * isHideTab: true 表示不在标签页中显示
+ *
+ * 注意事项:
+ * 1、path、name 不要和动态路由冲突,否则会导致路由冲突无法访问
+ * 2、静态路由不管是否登录都可以访问
+ */
+export const staticRoutes: AppRouteRecordRaw[] = [
+ // 不需要登录就能访问的路由示例
+ // {
+ // path: '/welcome',
+ // name: 'WelcomeStatic',
+ // component: () => import('@views/dashboard/console/index.vue'),
+ // meta: { title: 'menus.dashboard.title' }
+ // },
+ {
+ path: '/auth/login',
+ name: 'Login',
+ component: () => import('@views/auth/login/index.vue'),
+ meta: { title: 'menus.login.title', isHideTab: true }
+ },
+ {
+ path: '/auth/register',
+ name: 'Register',
+ component: () => import('@views/auth/register/index.vue'),
+ meta: { title: 'menus.register.title', isHideTab: true }
+ },
+ {
+ path: '/auth/forget-password',
+ name: 'ForgetPassword',
+ component: () => import('@views/auth/forget-password/index.vue'),
+ meta: { title: 'menus.forgetPassword.title', isHideTab: true }
+ },
+ {
+ path: '/403',
+ name: 'Exception403',
+ component: () => import('@views/exception/403/index.vue'),
+ meta: { title: '403', isHideTab: true }
+ },
+ {
+ path: '/:pathMatch(.*)*',
+ name: 'Exception404',
+ component: () => import('@views/exception/404/index.vue'),
+ meta: { title: '404', isHideTab: true }
+ },
+ {
+ path: '/500',
+ name: 'Exception500',
+ component: () => import('@views/exception/500/index.vue'),
+ meta: { title: '500', isHideTab: true }
+ },
+ {
+ path: '/outside',
+ component: () => import('@views/index/index.vue'),
+ name: 'Outside',
+ meta: { title: 'menus.outside.title' },
+ children: [
+ // iframe 内嵌页面
+ {
+ path: '/outside/iframe/:path',
+ name: 'Iframe',
+ component: () => import('@/views/outside/Iframe.vue'),
+ meta: { title: 'iframe' }
+ }
+ ]
+ }
+]
diff --git a/adminSystem/src/router/routesAlias.ts b/adminSystem/src/router/routesAlias.ts
new file mode 100644
index 0000000..2af1c68
--- /dev/null
+++ b/adminSystem/src/router/routesAlias.ts
@@ -0,0 +1,8 @@
+/**
+ * 公共路由别名
+ # 存放系统级公共路由路径,如布局容器、登录页等
+ */
+export enum RoutesAlias {
+ Layout = '/index/index', // 布局容器
+ Login = '/auth/login' // 登录页
+}
diff --git a/adminSystem/src/store/index.ts b/adminSystem/src/store/index.ts
new file mode 100644
index 0000000..b485999
--- /dev/null
+++ b/adminSystem/src/store/index.ts
@@ -0,0 +1,52 @@
+/**
+ * Pinia Store 配置模块
+ *
+ * 提供全局状态管理的初始化和配置
+ *
+ * ## 主要功能
+ *
+ * - Pinia Store 实例创建
+ * - 持久化插件配置(pinia-plugin-persistedstate)
+ * - 版本化存储键管理
+ * - 自动数据迁移(跨版本)
+ * - LocalStorage 序列化配置
+ * - Store 初始化函数
+ *
+ * ## 持久化策略
+ *
+ * - 使用 StorageKeyManager 生成版本化的存储键
+ * - 格式:sys-v{version}-{storeId}
+ * - 自动迁移旧版本数据到当前版本
+ * - 使用 localStorage 作为存储介质
+ *
+ * @module store/index
+ * @author Art Design Pro Team
+ */
+import type { App } from 'vue'
+import { createPinia } from 'pinia'
+import { createPersistedState } from 'pinia-plugin-persistedstate'
+import { StorageKeyManager } from '@/utils/storage/storage-key-manager'
+
+export const store = createPinia()
+
+// 创建存储键管理器实例
+const storageKeyManager = new StorageKeyManager()
+
+// 配置持久化插件
+store.use(
+ createPersistedState({
+ key: (storeId: string) => storageKeyManager.getStorageKey(storeId),
+ storage: localStorage,
+ serializer: {
+ serialize: JSON.stringify,
+ deserialize: JSON.parse
+ }
+ })
+)
+
+/**
+ * 初始化 Store
+ */
+export function initStore(app: App): void {
+ app.use(store)
+}
diff --git a/adminSystem/src/store/modules/menu.ts b/adminSystem/src/store/modules/menu.ts
new file mode 100644
index 0000000..85d13da
--- /dev/null
+++ b/adminSystem/src/store/modules/menu.ts
@@ -0,0 +1,109 @@
+/**
+ * 菜单状态管理模块
+ *
+ * 提供菜单数据和动态路由的状态管理
+ *
+ * ## 主要功能
+ *
+ * - 菜单列表存储和管理
+ * - 首页路径配置
+ * - 动态路由注册和移除
+ * - 路由移除函数管理
+ * - 菜单宽度配置
+ *
+ * ## 使用场景
+ *
+ * - 动态菜单加载和渲染
+ * - 路由权限控制
+ * - 首页路径动态设置
+ * - 登出时清理动态路由
+ *
+ * ## 工作流程
+ *
+ * 1. 获取菜单数据(前端/后端模式)
+ * 2. 设置菜单列表和首页路径
+ * 3. 注册动态路由并保存移除函数
+ * 4. 登出时调用移除函数清理路由
+ *
+ * @module store/modules/menu
+ * @author Art Design Pro Team
+ */
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { AppRouteRecord } from '@/types/router'
+import { getFirstMenuPath } from '@/utils'
+import { HOME_PAGE_PATH } from '@/router'
+
+/**
+ * 菜单状态管理
+ * 管理应用的菜单列表、首页路径、菜单宽度和动态路由移除函数
+ */
+export const useMenuStore = defineStore('menuStore', () => {
+ /** 首页路径 */
+ const homePath = ref(HOME_PAGE_PATH)
+ /** 菜单列表 */
+ const menuList = ref([])
+ /** 菜单宽度 */
+ const menuWidth = ref('')
+ /** 存储路由移除函数的数组 */
+ const removeRouteFns = ref<(() => void)[]>([])
+
+ /**
+ * 设置菜单列表
+ * @param list 菜单路由记录数组
+ */
+ const setMenuList = (list: AppRouteRecord[]) => {
+ menuList.value = list
+ setHomePath(HOME_PAGE_PATH || getFirstMenuPath(list))
+ }
+
+ /**
+ * 获取首页路径
+ * @returns 首页路径字符串
+ */
+ const getHomePath = () => homePath.value
+
+ /**
+ * 设置主页路径
+ * @param path 主页路径
+ */
+ const setHomePath = (path: string) => {
+ homePath.value = path
+ }
+
+ /**
+ * 添加路由移除函数
+ * @param fns 要添加的路由移除函数数组
+ */
+ const addRemoveRouteFns = (fns: (() => void)[]) => {
+ removeRouteFns.value.push(...fns)
+ }
+
+ /**
+ * 移除所有动态路由
+ * 执行所有存储的路由移除函数并清空数组
+ */
+ const removeAllDynamicRoutes = () => {
+ removeRouteFns.value.forEach((fn) => fn())
+ removeRouteFns.value = []
+ }
+
+ /**
+ * 清空路由移除函数数组
+ */
+ const clearRemoveRouteFns = () => {
+ removeRouteFns.value = []
+ }
+
+ return {
+ menuList,
+ menuWidth,
+ removeRouteFns,
+ setMenuList,
+ getHomePath,
+ setHomePath,
+ addRemoveRouteFns,
+ removeAllDynamicRoutes,
+ clearRemoveRouteFns
+ }
+})
diff --git a/adminSystem/src/store/modules/setting.ts b/adminSystem/src/store/modules/setting.ts
new file mode 100644
index 0000000..2878259
--- /dev/null
+++ b/adminSystem/src/store/modules/setting.ts
@@ -0,0 +1,450 @@
+/**
+ * 系统设置状态管理模块
+ *
+ * 提供完整的系统设置状态管理
+ *
+ * ## 主要功能
+ *
+ * - 菜单布局配置(左侧、顶部、混合、双栏)
+ * - 主题管理(亮色、暗色、自动)
+ * - 菜单主题样式配置
+ * - 界面显示开关(面包屑、标签页、语言切换等)
+ * - 功能开关(手风琴模式、色弱模式、水印等)
+ * - 样式配置(边框、圆角、容器宽度、页面过渡)
+ * - 节日功能配置
+ * - Element Plus 主题色动态设置
+ *
+ * ## 使用场景
+ *
+ * - 设置面板配置管理
+ * - 主题切换和样式定制
+ * - 界面功能开关控制
+ * - 用户偏好设置持久化
+ *
+ * ## 持久化
+ *
+ * - 使用 localStorage 存储
+ * - 存储键:sys-v{version}-setting
+ * - 支持跨版本数据迁移
+ *
+ * @module store/modules/setting
+ * @author Art Design Pro Team
+ */
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import { MenuThemeType } from '@/types/store'
+import AppConfig from '@/config'
+import { SystemThemeEnum, MenuThemeEnum, MenuTypeEnum, ContainerWidthEnum } from '@/enums/appEnum'
+import { setElementThemeColor } from '@/utils/ui'
+import { useCeremony } from '@/hooks/core/useCeremony'
+import { StorageConfig } from '@/utils'
+import { SETTING_DEFAULT_CONFIG } from '@/config/setting'
+
+/**
+ * 系统设置状态管理
+ * 管理应用的菜单、主题、界面显示等各项设置
+ */
+export const useSettingStore = defineStore(
+ 'settingStore',
+ () => {
+ // 菜单相关设置
+ /** 菜单类型 */
+ const menuType = ref(SETTING_DEFAULT_CONFIG.menuType)
+ /** 菜单展开宽度 */
+ const menuOpenWidth = ref(SETTING_DEFAULT_CONFIG.menuOpenWidth)
+ /** 菜单是否展开 */
+ const menuOpen = ref(SETTING_DEFAULT_CONFIG.menuOpen)
+ /** 双菜单是否显示文本 */
+ const dualMenuShowText = ref(SETTING_DEFAULT_CONFIG.dualMenuShowText)
+
+ // 主题相关设置
+ /** 系统主题类型 */
+ const systemThemeType = ref(SETTING_DEFAULT_CONFIG.systemThemeType)
+ /** 系统主题模式 */
+ const systemThemeMode = ref(SETTING_DEFAULT_CONFIG.systemThemeMode)
+ /** 菜单主题类型 */
+ const menuThemeType = ref(SETTING_DEFAULT_CONFIG.menuThemeType)
+ /** 系统主题颜色 */
+ const systemThemeColor = ref(SETTING_DEFAULT_CONFIG.systemThemeColor)
+
+ // 界面显示设置
+ /** 是否显示菜单按钮 */
+ const showMenuButton = ref(SETTING_DEFAULT_CONFIG.showMenuButton)
+ /** 是否显示快速入口 */
+ const showFastEnter = ref(SETTING_DEFAULT_CONFIG.showFastEnter)
+ /** 是否显示刷新按钮 */
+ const showRefreshButton = ref(SETTING_DEFAULT_CONFIG.showRefreshButton)
+ /** 是否显示面包屑 */
+ const showCrumbs = ref(SETTING_DEFAULT_CONFIG.showCrumbs)
+ /** 是否显示工作台标签 */
+ const showWorkTab = ref(SETTING_DEFAULT_CONFIG.showWorkTab)
+ /** 是否显示语言切换 */
+ const showLanguage = ref(SETTING_DEFAULT_CONFIG.showLanguage)
+ /** 是否显示进度条 */
+ const showNprogress = ref(SETTING_DEFAULT_CONFIG.showNprogress)
+ /** 是否显示设置引导 */
+ const showSettingGuide = ref(SETTING_DEFAULT_CONFIG.showSettingGuide)
+ /** 是否显示节日文本 */
+ const showFestivalText = ref(SETTING_DEFAULT_CONFIG.showFestivalText)
+ /** 是否显示水印 */
+ const watermarkVisible = ref(SETTING_DEFAULT_CONFIG.watermarkVisible)
+
+ // 功能设置
+ /** 是否自动关闭 */
+ const autoClose = ref(SETTING_DEFAULT_CONFIG.autoClose)
+ /** 是否唯一展开 */
+ const uniqueOpened = ref(SETTING_DEFAULT_CONFIG.uniqueOpened)
+ /** 是否色弱模式 */
+ const colorWeak = ref(SETTING_DEFAULT_CONFIG.colorWeak)
+ /** 是否刷新 */
+ const refresh = ref(SETTING_DEFAULT_CONFIG.refresh)
+ /** 是否加载节日烟花 */
+ const holidayFireworksLoaded = ref(SETTING_DEFAULT_CONFIG.holidayFireworksLoaded)
+
+ // 样式设置
+ /** 边框模式 */
+ const boxBorderMode = ref(SETTING_DEFAULT_CONFIG.boxBorderMode)
+ /** 页面过渡效果 */
+ const pageTransition = ref(SETTING_DEFAULT_CONFIG.pageTransition)
+ /** 标签页样式 */
+ const tabStyle = ref(SETTING_DEFAULT_CONFIG.tabStyle)
+ /** 自定义圆角 */
+ const customRadius = ref(SETTING_DEFAULT_CONFIG.customRadius)
+ /** 容器宽度 */
+ const containerWidth = ref(SETTING_DEFAULT_CONFIG.containerWidth)
+
+ // 节日相关
+ /** 节日日期 */
+ const festivalDate = ref('')
+
+ /**
+ * 获取菜单主题
+ * 根据当前主题类型和暗色模式返回对应的主题配置
+ */
+ const getMenuTheme = computed((): MenuThemeType => {
+ const list = AppConfig.themeList.filter((item) => item.theme === menuThemeType.value)
+ if (isDark.value) {
+ return AppConfig.darkMenuStyles[0]
+ } else {
+ return list[0]
+ }
+ })
+
+ /**
+ * 判断是否为暗色模式
+ */
+ const isDark = computed((): boolean => {
+ return systemThemeType.value === SystemThemeEnum.DARK
+ })
+
+ /**
+ * 获取菜单展开宽度
+ */
+ const getMenuOpenWidth = computed((): string => {
+ return menuOpenWidth.value + 'px' || SETTING_DEFAULT_CONFIG.menuOpenWidth + 'px'
+ })
+
+ /**
+ * 获取自定义圆角
+ */
+ const getCustomRadius = computed((): string => {
+ return customRadius.value + 'rem' || SETTING_DEFAULT_CONFIG.customRadius + 'rem'
+ })
+
+ /**
+ * 是否显示烟花
+ * 根据当前日期和节日日期判断是否显示烟花效果
+ */
+ const isShowFireworks = computed((): boolean => {
+ return festivalDate.value === useCeremony().currentFestivalData.value?.date ? false : true
+ })
+
+ /**
+ * 切换菜单布局
+ * @param type 菜单类型
+ */
+ const switchMenuLayouts = (type: MenuTypeEnum) => {
+ menuType.value = type
+ }
+
+ /**
+ * 设置菜单展开宽度
+ * @param width 宽度值
+ */
+ const setMenuOpenWidth = (width: number) => {
+ menuOpenWidth.value = width
+ }
+
+ /**
+ * 设置全局主题
+ * @param theme 主题类型
+ * @param themeMode 主题模式
+ */
+ const setGlopTheme = (theme: SystemThemeEnum, themeMode: SystemThemeEnum) => {
+ systemThemeType.value = theme
+ systemThemeMode.value = themeMode
+ localStorage.setItem(StorageConfig.THEME_KEY, theme)
+ }
+
+ /**
+ * 切换菜单样式
+ * @param theme 菜单主题
+ */
+ const switchMenuStyles = (theme: MenuThemeEnum) => {
+ menuThemeType.value = theme
+ }
+
+ /**
+ * 设置Element Plus主题颜色
+ * @param theme 主题颜色
+ */
+ const setElementTheme = (theme: string) => {
+ systemThemeColor.value = theme
+ setElementThemeColor(theme)
+ }
+
+ /**
+ * 切换边框模式
+ */
+ const setBorderMode = () => {
+ boxBorderMode.value = !boxBorderMode.value
+ }
+
+ /**
+ * 设置容器宽度
+ * @param width 容器宽度枚举值
+ */
+ const setContainerWidth = (width: ContainerWidthEnum) => {
+ containerWidth.value = width
+ }
+
+ /**
+ * 切换唯一展开模式
+ */
+ const setUniqueOpened = () => {
+ uniqueOpened.value = !uniqueOpened.value
+ }
+
+ /**
+ * 切换菜单按钮显示
+ */
+ const setButton = () => {
+ showMenuButton.value = !showMenuButton.value
+ }
+
+ /**
+ * 切换快速入口显示
+ */
+ const setFastEnter = () => {
+ showFastEnter.value = !showFastEnter.value
+ }
+
+ /**
+ * 切换自动关闭
+ */
+ const setAutoClose = () => {
+ autoClose.value = !autoClose.value
+ }
+
+ /**
+ * 切换刷新按钮显示
+ */
+ const setShowRefreshButton = () => {
+ showRefreshButton.value = !showRefreshButton.value
+ }
+
+ /**
+ * 切换面包屑显示
+ */
+ const setCrumbs = () => {
+ showCrumbs.value = !showCrumbs.value
+ }
+
+ /**
+ * 设置工作台标签显示
+ * @param show 是否显示
+ */
+ const setWorkTab = (show: boolean) => {
+ showWorkTab.value = show
+ }
+
+ /**
+ * 切换语言切换显示
+ */
+ const setLanguage = () => {
+ showLanguage.value = !showLanguage.value
+ }
+
+ /**
+ * 切换进度条显示
+ */
+ const setNprogress = () => {
+ showNprogress.value = !showNprogress.value
+ }
+
+ /**
+ * 切换色弱模式
+ */
+ const setColorWeak = () => {
+ colorWeak.value = !colorWeak.value
+ }
+
+ /**
+ * 隐藏设置引导
+ */
+ const hideSettingGuide = () => {
+ showSettingGuide.value = false
+ }
+
+ /**
+ * 显示设置引导
+ */
+ const openSettingGuide = () => {
+ showSettingGuide.value = true
+ }
+
+ /**
+ * 设置页面过渡效果
+ * @param transition 过渡效果名称
+ */
+ const setPageTransition = (transition: string) => {
+ pageTransition.value = transition
+ }
+
+ /**
+ * 设置标签页样式
+ * @param style 样式名称
+ */
+ const setTabStyle = (style: string) => {
+ tabStyle.value = style
+ }
+
+ /**
+ * 设置菜单展开状态
+ * @param open 是否展开
+ */
+ const setMenuOpen = (open: boolean) => {
+ menuOpen.value = open
+ }
+
+ /**
+ * 刷新页面
+ */
+ const reload = () => {
+ refresh.value = !refresh.value
+ }
+
+ /**
+ * 设置水印显示
+ * @param visible 是否显示
+ */
+ const setWatermarkVisible = (visible: boolean) => {
+ watermarkVisible.value = visible
+ }
+
+ /**
+ * 设置自定义圆角
+ * @param radius 圆角值
+ */
+ const setCustomRadius = (radius: string) => {
+ customRadius.value = radius
+ document.documentElement.style.setProperty('--custom-radius', `${radius}rem`)
+ }
+
+ /**
+ * 设置节日烟花加载状态
+ * @param isLoad 是否已加载
+ */
+ const setholidayFireworksLoaded = (isLoad: boolean) => {
+ holidayFireworksLoaded.value = isLoad
+ }
+
+ /**
+ * 设置节日文本显示
+ * @param show 是否显示
+ */
+ const setShowFestivalText = (show: boolean) => {
+ showFestivalText.value = show
+ }
+
+ const setFestivalDate = (date: string) => {
+ festivalDate.value = date
+ }
+
+ const setDualMenuShowText = (show: boolean) => {
+ dualMenuShowText.value = show
+ }
+
+ return {
+ menuType,
+ menuOpenWidth,
+ systemThemeType,
+ systemThemeMode,
+ menuThemeType,
+ systemThemeColor,
+ boxBorderMode,
+ uniqueOpened,
+ showMenuButton,
+ showFastEnter,
+ showRefreshButton,
+ showCrumbs,
+ autoClose,
+ showWorkTab,
+ showLanguage,
+ showNprogress,
+ colorWeak,
+ showSettingGuide,
+ pageTransition,
+ tabStyle,
+ menuOpen,
+ refresh,
+ watermarkVisible,
+ customRadius,
+ holidayFireworksLoaded,
+ showFestivalText,
+ festivalDate,
+ dualMenuShowText,
+ containerWidth,
+ getMenuTheme,
+ isDark,
+ getMenuOpenWidth,
+ getCustomRadius,
+ isShowFireworks,
+ switchMenuLayouts,
+ setMenuOpenWidth,
+ setGlopTheme,
+ switchMenuStyles,
+ setElementTheme,
+ setBorderMode,
+ setContainerWidth,
+ setUniqueOpened,
+ setButton,
+ setFastEnter,
+ setAutoClose,
+ setShowRefreshButton,
+ setCrumbs,
+ setWorkTab,
+ setLanguage,
+ setNprogress,
+ setColorWeak,
+ hideSettingGuide,
+ openSettingGuide,
+ setPageTransition,
+ setTabStyle,
+ setMenuOpen,
+ reload,
+ setWatermarkVisible,
+ setCustomRadius,
+ setholidayFireworksLoaded,
+ setShowFestivalText,
+ setFestivalDate,
+ setDualMenuShowText
+ }
+ },
+ {
+ persist: {
+ key: 'setting',
+ storage: localStorage
+ }
+ }
+)
diff --git a/adminSystem/src/store/modules/table.ts b/adminSystem/src/store/modules/table.ts
new file mode 100644
index 0000000..094c310
--- /dev/null
+++ b/adminSystem/src/store/modules/table.ts
@@ -0,0 +1,97 @@
+/**
+ * 表格状态管理模块
+ *
+ * 提供表格显示配置的状态管理
+ *
+ * ## 主要功能
+ *
+ * - 表格尺寸配置(紧凑、默认、宽松)
+ * - 斑马纹显示开关
+ * - 边框显示开关
+ * - 表头背景显示开关
+ * - 全屏模式开关
+ *
+ * ## 使用场景
+ * - 表格组件样式配置
+ * - 用户表格偏好设置
+ * - 表格工具栏功能控制
+ *
+ * ## 持久化
+ *
+ * - 使用 localStorage 存储
+ * - 存储键:sys-v{version}-table
+ * - 用户配置跨页面保持
+ *
+ * @module store/modules/table
+ * @author Art Design Pro Team
+ */
+import { defineStore } from 'pinia'
+import { ref } from 'vue'
+import { TableSizeEnum } from '@/enums/formEnum'
+
+// 表格
+export const useTableStore = defineStore(
+ 'tableStore',
+ () => {
+ // 表格大小
+ const tableSize = ref(TableSizeEnum.DEFAULT)
+ // 斑马纹
+ const isZebra = ref(false)
+ // 边框
+ const isBorder = ref(false)
+ // 表头背景
+ const isHeaderBackground = ref(false)
+
+ // 是否全屏
+ const isFullScreen = ref(false)
+
+ /**
+ * 设置表格大小
+ * @param size 表格大小枚举值
+ */
+ const setTableSize = (size: TableSizeEnum) => (tableSize.value = size)
+
+ /**
+ * 设置斑马纹显示状态
+ * @param value 是否显示斑马纹
+ */
+ const setIsZebra = (value: boolean) => (isZebra.value = value)
+
+ /**
+ * 设置表格边框显示状态
+ * @param value 是否显示边框
+ */
+ const setIsBorder = (value: boolean) => (isBorder.value = value)
+
+ /**
+ * 设置表头背景显示状态
+ * @param value 是否显示表头背景
+ */
+ const setIsHeaderBackground = (value: boolean) => (isHeaderBackground.value = value)
+
+ /**
+ * 设置是否全屏
+ * @param value 是否全屏
+ */
+ const setIsFullScreen = (value: boolean) => (isFullScreen.value = value)
+
+ return {
+ tableSize,
+ isZebra,
+ isBorder,
+ isHeaderBackground,
+ setTableSize,
+ setIsZebra,
+ setIsBorder,
+ setIsHeaderBackground,
+ isFullScreen,
+ setIsFullScreen
+ }
+ },
+ {
+ persist: {
+ key: 'table',
+ storage: localStorage
+ }
+ }
+)
diff --git a/adminSystem/src/store/modules/user.ts b/adminSystem/src/store/modules/user.ts
new file mode 100644
index 0000000..08f7684
--- /dev/null
+++ b/adminSystem/src/store/modules/user.ts
@@ -0,0 +1,235 @@
+/**
+ * 用户状态管理模块
+ *
+ * 提供用户相关的状态管理
+ *
+ * ## 主要功能
+ *
+ * - 用户登录状态管理
+ * - 用户信息存储
+ * - 访问令牌和刷新令牌管理
+ * - 语言设置
+ * - 搜索历史记录
+ * - 锁屏状态和密码管理
+ * - 登出清理逻辑
+ *
+ * ## 使用场景
+ *
+ * - 用户登录和认证
+ * - 权限验证
+ * - 个人信息展示
+ * - 多语言切换
+ * - 锁屏功能
+ * - 搜索历史管理
+ *
+ * ## 持久化
+ *
+ * - 使用 localStorage 存储
+ * - 存储键:sys-v{version}-user
+ * - 登出时自动清理
+ *
+ * @module store/modules/user
+ * @author Art Design Pro Team
+ */
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import { LanguageEnum } from '@/enums/appEnum'
+import { router } from '@/router'
+import { useSettingStore } from './setting'
+import { useWorktabStore } from './worktab'
+import { AppRouteRecord } from '@/types/router'
+import { setPageTitle } from '@/utils/router'
+import { resetRouterState } from '@/router/guards/beforeEach'
+import { useMenuStore } from './menu'
+import { StorageConfig } from '@/utils/storage/storage-config'
+
+/**
+ * 用户状态管理
+ * 管理用户登录状态、个人信息、语言设置、搜索历史、锁屏状态等
+ */
+export const useUserStore = defineStore(
+ 'userStore',
+ () => {
+ // 语言设置
+ const language = ref(LanguageEnum.ZH)
+ // 登录状态
+ const isLogin = ref(false)
+ // 锁屏状态
+ const isLock = ref(false)
+ // 锁屏密码
+ const lockPassword = ref('')
+ // 用户信息
+ const info = ref>({})
+ // 搜索历史记录
+ const searchHistory = ref([])
+ // 访问令牌
+ const accessToken = ref('')
+ // 刷新令牌
+ const refreshToken = ref('')
+
+ // 计算属性:获取用户信息
+ const getUserInfo = computed(() => info.value)
+ // 计算属性:获取设置状态
+ const getSettingState = computed(() => useSettingStore().$state)
+ // 计算属性:获取工作台状态
+ const getWorktabState = computed(() => useWorktabStore().$state)
+
+ /**
+ * 设置用户信息
+ * @param newInfo 新的用户信息
+ */
+ const setUserInfo = (newInfo: Api.Auth.UserInfo) => {
+ info.value = newInfo
+ }
+
+ /**
+ * 设置登录状态
+ * @param status 登录状态
+ */
+ const setLoginStatus = (status: boolean) => {
+ isLogin.value = status
+ }
+
+ /**
+ * 设置语言
+ * @param lang 语言枚举值
+ */
+ const setLanguage = (lang: LanguageEnum) => {
+ setPageTitle(router.currentRoute.value)
+ language.value = lang
+ }
+
+ /**
+ * 设置搜索历史
+ * @param list 搜索历史列表
+ */
+ const setSearchHistory = (list: AppRouteRecord[]) => {
+ searchHistory.value = list
+ }
+
+ /**
+ * 设置锁屏状态
+ * @param status 锁屏状态
+ */
+ const setLockStatus = (status: boolean) => {
+ isLock.value = status
+ }
+
+ /**
+ * 设置锁屏密码
+ * @param password 锁屏密码
+ */
+ const setLockPassword = (password: string) => {
+ lockPassword.value = password
+ }
+
+ /**
+ * 设置令牌
+ * @param newAccessToken 访问令牌
+ * @param newRefreshToken 刷新令牌(可选)
+ */
+ const setToken = (newAccessToken: string, newRefreshToken?: string) => {
+ accessToken.value = newAccessToken
+ if (newRefreshToken) {
+ refreshToken.value = newRefreshToken
+ }
+ }
+
+ /**
+ * 退出登录
+ * 清空所有用户相关状态并跳转到登录页
+ * 如果是同一账号重新登录,保留工作台标签页
+ */
+ const logOut = () => {
+ // 保存当前用户 ID,用于下次登录时判断是否为同一用户
+ const currentUserId = info.value.userId
+ if (currentUserId) {
+ localStorage.setItem(StorageConfig.LAST_USER_ID_KEY, String(currentUserId))
+ }
+
+ // 清空用户信息
+ info.value = {}
+ // 重置登录状态
+ isLogin.value = false
+ // 重置锁屏状态
+ isLock.value = false
+ // 清空锁屏密码
+ lockPassword.value = ''
+ // 清空访问令牌
+ accessToken.value = ''
+ // 清空刷新令牌
+ refreshToken.value = ''
+ // 注意:不清空工作台标签页,等下次登录时根据用户判断
+ // 移除iframe路由缓存
+ sessionStorage.removeItem('iframeRoutes')
+ // 清空主页路径
+ useMenuStore().setHomePath('')
+ // 重置路由状态
+ resetRouterState(500)
+ // 跳转到登录页,携带当前路由作为 redirect 参数
+ const currentRoute = router.currentRoute.value
+ const redirect = currentRoute.path !== '/login' ? currentRoute.fullPath : undefined
+ router.push({
+ name: 'Login',
+ query: redirect ? { redirect } : undefined
+ })
+ }
+
+ /**
+ * 检查并清理工作台标签页
+ * 如果不是同一用户登录,清空工作台标签页
+ * 应在登录成功后调用
+ */
+ const checkAndClearWorktabs = () => {
+ const lastUserId = localStorage.getItem(StorageConfig.LAST_USER_ID_KEY)
+ const currentUserId = info.value.userId
+
+ // 无法获取当前用户 ID,跳过检查
+ if (!currentUserId) return
+
+ // 首次登录或缓存已清除,保留现有标签页
+ if (!lastUserId) {
+ return
+ }
+
+ // 不同用户登录,清空工作台标签页
+ if (String(currentUserId) !== lastUserId) {
+ const worktabStore = useWorktabStore()
+ worktabStore.opened = []
+ worktabStore.keepAliveExclude = []
+ }
+
+ // 清除临时存储
+ localStorage.removeItem(StorageConfig.LAST_USER_ID_KEY)
+ }
+
+ return {
+ language,
+ isLogin,
+ isLock,
+ lockPassword,
+ info,
+ searchHistory,
+ accessToken,
+ refreshToken,
+ getUserInfo,
+ getSettingState,
+ getWorktabState,
+ setUserInfo,
+ setLoginStatus,
+ setLanguage,
+ setSearchHistory,
+ setLockStatus,
+ setLockPassword,
+ setToken,
+ logOut,
+ checkAndClearWorktabs
+ }
+ },
+ {
+ persist: {
+ key: 'user',
+ storage: localStorage
+ }
+ }
+)
diff --git a/adminSystem/src/store/modules/worktab.ts b/adminSystem/src/store/modules/worktab.ts
new file mode 100644
index 0000000..caa0d90
--- /dev/null
+++ b/adminSystem/src/store/modules/worktab.ts
@@ -0,0 +1,568 @@
+/**
+ * 工作标签页状态管理模块
+ *
+ * 提供多标签页功能的完整状态管理
+ *
+ * ## 主要功能
+ *
+ * - 标签页打开和关闭
+ * - 标签页固定和取消固定
+ * - 批量关闭(左侧、右侧、其他、全部)
+ * - 标签页缓存管理(KeepAlive)
+ * - 标签页标题自定义
+ * - 标签页路由验证
+ * - 动态路由参数处理
+ *
+ * ## 使用场景
+ *
+ * - 多标签页导航
+ * - 页面缓存控制
+ * - 标签页右键菜单
+ * - 固定常用页面
+ * - 批量关闭标签
+ *
+ * ## 核心特性
+ *
+ * - 智能标签页复用(同路由名称复用)
+ * - 固定标签页保护(不可关闭)
+ * - KeepAlive 缓存排除管理
+ * - 路由有效性验证
+ * - 首页自动保留
+ *
+ * ## 持久化
+ * - 使用 localStorage 存储
+ * - 存储键:sys-v{version}-worktab
+ * - 刷新页面保持标签状态
+ *
+ * @module store/modules/worktab
+ * @author Art Design Pro Team
+ */
+import { defineStore } from 'pinia'
+import { ref, computed } from 'vue'
+import { router } from '@/router'
+import { LocationQueryRaw, Router } from 'vue-router'
+import { WorkTab } from '@/types'
+import { useCommon } from '@/hooks/core/useCommon'
+
+interface WorktabState {
+ current: Partial
+ opened: WorkTab[]
+ keepAliveExclude: string[]
+}
+
+/**
+ * 工作台标签页管理 Store
+ */
+export const useWorktabStore = defineStore(
+ 'worktabStore',
+ () => {
+ // 状态定义
+ const current = ref>({})
+ const opened = ref([])
+ const keepAliveExclude = ref([])
+
+ // 计算属性
+ const hasOpenedTabs = computed(() => opened.value.length > 0)
+ const hasMultipleTabs = computed(() => opened.value.length > 1)
+ const currentTabIndex = computed(() =>
+ current.value.path ? opened.value.findIndex((tab) => tab.path === current.value.path) : -1
+ )
+
+ /**
+ * 查找标签页索引
+ */
+ const findTabIndex = (path: string): number => {
+ return opened.value.findIndex((tab) => tab.path === path)
+ }
+
+ /**
+ * 获取标签页
+ */
+ const getTab = (path: string): WorkTab | undefined => {
+ return opened.value.find((tab) => tab.path === path)
+ }
+
+ /**
+ * 检查标签页是否可关闭
+ */
+ const isTabClosable = (tab: WorkTab): boolean => {
+ return !tab.fixedTab
+ }
+
+ /**
+ * 安全的路由跳转
+ */
+ const safeRouterPush = (tab: Partial): void => {
+ if (!tab.path) {
+ console.warn('尝试跳转到无效路径的标签页')
+ return
+ }
+
+ try {
+ router.push({
+ path: tab.path,
+ query: tab.query as LocationQueryRaw
+ })
+ } catch (error) {
+ console.error('路由跳转失败:', error)
+ }
+ }
+
+ /**
+ * 打开或激活一个选项卡
+ */
+ const openTab = (tab: WorkTab): void => {
+ if (!tab.path) {
+ console.warn('尝试打开无效的标签页')
+ return
+ }
+
+ // 从 keepAlive 排除列表中移除
+ if (tab.name) {
+ removeKeepAliveExclude(tab.name)
+ }
+
+ // 先根据路由名称查找(应对动态路由参数导致的多开问题),找不到再根据路径查找
+ let existingIndex = -1
+ if (tab.name) {
+ existingIndex = opened.value.findIndex((t) => t.name === tab.name)
+ }
+ if (existingIndex === -1) {
+ existingIndex = findTabIndex(tab.path)
+ }
+
+ if (existingIndex === -1) {
+ // 新增标签页
+ const insertIndex = tab.fixedTab ? findFixedTabInsertIndex() : opened.value.length
+ const newTab = { ...tab }
+
+ if (tab.fixedTab) {
+ opened.value.splice(insertIndex, 0, newTab)
+ } else {
+ opened.value.push(newTab)
+ }
+
+ current.value = newTab
+ } else {
+ // 更新现有标签页(当动态路由参数或查询变更时,复用同一标签)
+ const existingTab = opened.value[existingIndex]
+
+ opened.value[existingIndex] = {
+ ...existingTab,
+ path: tab.path,
+ params: tab.params,
+ query: tab.query,
+ title: tab.title || existingTab.title,
+ fixedTab: tab.fixedTab ?? existingTab.fixedTab,
+ keepAlive: tab.keepAlive ?? existingTab.keepAlive,
+ name: tab.name || existingTab.name,
+ icon: tab.icon || existingTab.icon
+ }
+
+ current.value = opened.value[existingIndex]
+ }
+ }
+
+ /**
+ * 查找固定标签页的插入位置
+ */
+ const findFixedTabInsertIndex = (): number => {
+ let insertIndex = 0
+ for (let i = 0; i < opened.value.length; i++) {
+ if (opened.value[i].fixedTab) {
+ insertIndex = i + 1
+ } else {
+ break
+ }
+ }
+ return insertIndex
+ }
+
+ /**
+ * 关闭指定的选项卡
+ */
+ const removeTab = (path: string): void => {
+ const targetTab = getTab(path)
+ const targetIndex = findTabIndex(path)
+
+ if (targetIndex === -1) {
+ console.warn(`尝试关闭不存在的标签页: ${path}`)
+ return
+ }
+
+ if (targetTab && !isTabClosable(targetTab)) {
+ console.warn(`尝试关闭固定标签页: ${path}`)
+ return
+ }
+
+ // 从标签页列表中移除
+ opened.value.splice(targetIndex, 1)
+
+ // 处理缓存排除
+ if (targetTab?.name) {
+ addKeepAliveExclude(targetTab)
+ }
+
+ const { homePath } = useCommon()
+
+ // 如果关闭后无标签页,跳转首页
+ if (!hasOpenedTabs.value) {
+ if (path !== homePath.value) {
+ current.value = {}
+ safeRouterPush({ path: homePath.value })
+ }
+ return
+ }
+
+ // 如果关闭的是当前激活标签,需要激活其他标签
+ if (current.value.path === path) {
+ const newIndex = targetIndex >= opened.value.length ? opened.value.length - 1 : targetIndex
+ current.value = opened.value[newIndex]
+ safeRouterPush(current.value)
+ }
+ }
+
+ /**
+ * 关闭左侧选项卡
+ */
+ const removeLeft = (path: string): void => {
+ const targetIndex = findTabIndex(path)
+
+ if (targetIndex === -1) {
+ console.warn(`尝试关闭左侧标签页,但目标标签页不存在: ${path}`)
+ return
+ }
+
+ // 获取左侧可关闭的标签页
+ const leftTabs = opened.value.slice(0, targetIndex)
+ const closableLeftTabs = leftTabs.filter(isTabClosable)
+
+ if (closableLeftTabs.length === 0) {
+ console.warn('左侧没有可关闭的标签页')
+ return
+ }
+
+ // 标记为缓存排除
+ markTabsToRemove(closableLeftTabs)
+
+ // 移除左侧可关闭的标签页
+ opened.value = opened.value.filter(
+ (tab, index) => index >= targetIndex || !isTabClosable(tab)
+ )
+
+ // 确保当前标签是激活状态
+ const targetTab = getTab(path)
+ if (targetTab) {
+ current.value = targetTab
+ }
+ }
+
+ /**
+ * 关闭右侧选项卡
+ */
+ const removeRight = (path: string): void => {
+ const targetIndex = findTabIndex(path)
+
+ if (targetIndex === -1) {
+ console.warn(`尝试关闭右侧标签页,但目标标签页不存在: ${path}`)
+ return
+ }
+
+ // 获取右侧可关闭的标签页
+ const rightTabs = opened.value.slice(targetIndex + 1)
+ const closableRightTabs = rightTabs.filter(isTabClosable)
+
+ if (closableRightTabs.length === 0) {
+ console.warn('右侧没有可关闭的标签页')
+ return
+ }
+
+ // 标记为缓存排除
+ markTabsToRemove(closableRightTabs)
+
+ // 移除右侧可关闭的标签页
+ opened.value = opened.value.filter(
+ (tab, index) => index <= targetIndex || !isTabClosable(tab)
+ )
+
+ // 确保当前标签是激活状态
+ const targetTab = getTab(path)
+ if (targetTab) {
+ current.value = targetTab
+ }
+ }
+
+ /**
+ * 关闭其他选项卡
+ */
+ const removeOthers = (path: string): void => {
+ const targetTab = getTab(path)
+
+ if (!targetTab) {
+ console.warn(`尝试关闭其他标签页,但目标标签页不存在: ${path}`)
+ return
+ }
+
+ // 获取其他可关闭的标签页
+ const otherTabs = opened.value.filter((tab) => tab.path !== path)
+ const closableTabs = otherTabs.filter(isTabClosable)
+
+ if (closableTabs.length === 0) {
+ console.warn('没有其他可关闭的标签页')
+ return
+ }
+
+ // 标记为缓存排除
+ markTabsToRemove(closableTabs)
+
+ // 只保留当前标签和固定标签
+ opened.value = opened.value.filter((tab) => tab.path === path || !isTabClosable(tab))
+
+ // 确保当前标签是激活状态
+ current.value = targetTab
+ }
+
+ /**
+ * 关闭所有可关闭的标签页
+ */
+ const removeAll = (): void => {
+ const { homePath } = useCommon()
+ const hasFixedTabs = opened.value.some((tab) => tab.fixedTab)
+
+ // 获取可关闭的标签页
+ const closableTabs = opened.value.filter((tab) => {
+ if (!isTabClosable(tab)) return false
+ // 如果有固定标签,则所有可关闭的都可以关闭;否则保留首页
+ return hasFixedTabs || tab.path !== homePath.value
+ })
+
+ if (closableTabs.length === 0) {
+ console.warn('没有可关闭的标签页')
+ return
+ }
+
+ // 标记为缓存排除
+ markTabsToRemove(closableTabs)
+
+ // 保留不可关闭的标签页和首页(当没有固定标签时)
+ opened.value = opened.value.filter((tab) => {
+ return !isTabClosable(tab) || (!hasFixedTabs && tab.path === homePath.value)
+ })
+
+ // 处理激活状态
+ if (!hasOpenedTabs.value) {
+ current.value = {}
+ safeRouterPush({ path: homePath.value })
+ return
+ }
+
+ // 选择激活的标签页:优先首页,其次第一个可用标签
+ const homeTab = opened.value.find((tab) => tab.path === homePath.value)
+ const targetTab = homeTab || opened.value[0]
+
+ current.value = targetTab
+ safeRouterPush(targetTab)
+ }
+
+ /**
+ * 将指定选项卡添加到 keepAlive 排除列表中
+ */
+ const addKeepAliveExclude = (tab: WorkTab): void => {
+ if (!tab.keepAlive || !tab.name) return
+
+ if (!keepAliveExclude.value.includes(tab.name)) {
+ keepAliveExclude.value.push(tab.name)
+ }
+ }
+
+ /**
+ * 从 keepAlive 排除列表中移除指定组件名称
+ */
+ const removeKeepAliveExclude = (name: string): void => {
+ if (!name) return
+
+ keepAliveExclude.value = keepAliveExclude.value.filter((item) => item !== name)
+ }
+
+ /**
+ * 将传入的一组选项卡的组件名称标记为排除缓存
+ */
+ const markTabsToRemove = (tabs: WorkTab[]): void => {
+ tabs.forEach((tab) => {
+ if (tab.name) {
+ addKeepAliveExclude(tab)
+ }
+ })
+ }
+
+ /**
+ * 切换指定标签页的固定状态
+ */
+ const toggleFixedTab = (path: string): void => {
+ const targetIndex = findTabIndex(path)
+
+ if (targetIndex === -1) {
+ console.warn(`尝试切换不存在标签页的固定状态: ${path}`)
+ return
+ }
+
+ const tab = { ...opened.value[targetIndex] }
+ tab.fixedTab = !tab.fixedTab
+
+ // 移除原位置
+ opened.value.splice(targetIndex, 1)
+
+ if (tab.fixedTab) {
+ // 固定标签插入到所有固定标签的末尾
+ const firstNonFixedIndex = opened.value.findIndex((t) => !t.fixedTab)
+ const insertIndex = firstNonFixedIndex === -1 ? opened.value.length : firstNonFixedIndex
+ opened.value.splice(insertIndex, 0, tab)
+ } else {
+ // 非固定标签插入到所有固定标签后
+ const fixedCount = opened.value.filter((t) => t.fixedTab).length
+ opened.value.splice(fixedCount, 0, tab)
+ }
+
+ // 更新当前标签引用
+ if (current.value.path === path) {
+ current.value = tab
+ }
+ }
+
+ /**
+ * 验证工作台标签页的路由有效性
+ */
+ const validateWorktabs = (routerInstance: Router): void => {
+ try {
+ // 动态路由校验:优先使用路由 name 判断有效性;否则用 resolve 匹配参数化路径
+ const isTabRouteValid = (tab: Partial): boolean => {
+ try {
+ if (tab.name) {
+ const routes = routerInstance.getRoutes()
+ if (routes.some((r) => r.name === tab.name)) return true
+ }
+ if (tab.path) {
+ const resolved = routerInstance.resolve({
+ path: tab.path,
+ query: (tab.query as LocationQueryRaw) || undefined
+ })
+ return resolved.matched.length > 0
+ }
+ return false
+ } catch {
+ return false
+ }
+ }
+
+ // 过滤出有效的标签页
+ const validTabs = opened.value.filter((tab) => isTabRouteValid(tab))
+
+ if (validTabs.length !== opened.value.length) {
+ console.warn('发现无效的标签页路由,已自动清理')
+ opened.value = validTabs
+ }
+
+ // 验证当前激活标签的有效性
+ const isCurrentValid = current.value && isTabRouteValid(current.value)
+
+ if (!isCurrentValid && validTabs.length > 0) {
+ console.warn('当前激活标签无效,已自动切换')
+ current.value = validTabs[0]
+ } else if (!isCurrentValid) {
+ current.value = {}
+ }
+ } catch (error) {
+ console.error('验证工作台标签页失败:', error)
+ }
+ }
+
+ /**
+ * 清空所有状态(用于登出等场景)
+ */
+ const clearAll = (): void => {
+ current.value = {}
+ opened.value = []
+ keepAliveExclude.value = []
+ }
+
+ /**
+ * 获取状态快照(用于持久化存储)
+ */
+ const getStateSnapshot = (): WorktabState => {
+ return {
+ current: { ...current.value },
+ opened: [...opened.value],
+ keepAliveExclude: [...keepAliveExclude.value]
+ }
+ }
+
+ /**
+ * 获取标签页标题
+ */
+ const getTabTitle = (path: string): WorkTab | undefined => {
+ const tab = getTab(path)
+ return tab
+ }
+
+ /**
+ * 更新标签页标题
+ */
+ const updateTabTitle = (path: string, title: string): void => {
+ const tab = getTab(path)
+ if (tab) {
+ tab.customTitle = title
+ }
+ }
+
+ /**
+ * 重置标签页标题
+ */
+ const resetTabTitle = (path: string): void => {
+ const tab = getTab(path)
+ if (tab) {
+ tab.customTitle = ''
+ }
+ }
+
+ return {
+ // 状态
+ current,
+ opened,
+ keepAliveExclude,
+
+ // 计算属性
+ hasOpenedTabs,
+ hasMultipleTabs,
+ currentTabIndex,
+
+ // 方法
+ openTab,
+ removeTab,
+ removeLeft,
+ removeRight,
+ removeOthers,
+ removeAll,
+ toggleFixedTab,
+ validateWorktabs,
+ clearAll,
+ getStateSnapshot,
+
+ // 工具方法
+ findTabIndex,
+ getTab,
+ isTabClosable,
+ addKeepAliveExclude,
+ removeKeepAliveExclude,
+ markTabsToRemove,
+ getTabTitle,
+ updateTabTitle,
+ resetTabTitle
+ }
+ },
+ {
+ persist: {
+ key: 'worktab',
+ storage: localStorage
+ }
+ }
+)
diff --git a/adminSystem/src/types/api/api.d.ts b/adminSystem/src/types/api/api.d.ts
new file mode 100644
index 0000000..fd3abbb
--- /dev/null
+++ b/adminSystem/src/types/api/api.d.ts
@@ -0,0 +1,135 @@
+/**
+ * API 接口类型定义模块
+ *
+ * 提供所有后端接口的类型定义
+ *
+ * ## 主要功能
+ *
+ * - 通用类型(分页参数、响应结构等)
+ * - 认证类型(登录、用户信息等)
+ * - 系统管理类型(用户、角色等)
+ * - 全局命名空间声明
+ *
+ * ## 使用场景
+ *
+ * - API 请求参数类型约束
+ * - API 响应数据类型定义
+ * - 接口文档类型同步
+ *
+ * ## 注意事项
+ *
+ * - 在 .vue 文件使用需要在 eslint.config.mjs 中配置 globals: { Api: 'readonly' }
+ * - 使用全局命名空间,无需导入即可使用
+ *
+ * ## 使用方式
+ *
+ * ```typescript
+ * const params: Api.Auth.LoginParams = { userName: 'admin', password: '123456' }
+ * const response: Api.Auth.UserInfo = await fetchUserInfo()
+ * ```
+ *
+ * @module types/api/api
+ * @author Art Design Pro Team
+ */
+
+declare namespace Api {
+ /** 通用类型 */
+ namespace Common {
+ /** 分页参数 */
+ interface PaginationParams {
+ /** 当前页码 */
+ current: number
+ /** 每页条数 */
+ size: number
+ /** 总条数 */
+ total: number
+ }
+
+ /** 通用搜索参数 */
+ type CommonSearchParams = Pick
+
+ /** 分页响应基础结构 */
+ interface PaginatedResponse {
+ records: T[]
+ current: number
+ size: number
+ total: number
+ }
+
+ /** 启用状态 */
+ type EnableStatus = '1' | '2'
+ }
+
+ /** 认证类型 */
+ namespace Auth {
+ /** 登录参数 */
+ interface LoginParams {
+ userName: string
+ password: string
+ }
+
+ /** 登录响应 */
+ interface LoginResponse {
+ token: string
+ refreshToken: string
+ }
+
+ /** 用户信息 */
+ interface UserInfo {
+ buttons: string[]
+ roles: string[]
+ userId: number
+ userName: string
+ email: string
+ avatar?: string
+ }
+ }
+
+ /** 系统管理类型 */
+ namespace SystemManage {
+ /** 用户列表 */
+ type UserList = Api.Common.PaginatedResponse
+
+ /** 用户列表项 */
+ interface UserListItem {
+ id: number
+ avatar: string
+ status: string
+ userName: string
+ userGender: string
+ nickName: string
+ userPhone: string
+ userEmail: string
+ userRoles: string[]
+ createBy: string
+ createTime: string
+ updateBy: string
+ updateTime: string
+ }
+
+ /** 用户搜索参数 */
+ type UserSearchParams = Partial<
+ Pick &
+ Api.Common.CommonSearchParams
+ >
+
+ /** 角色列表 */
+ type RoleList = Api.Common.PaginatedResponse
+
+ /** 角色列表项 */
+ interface RoleListItem {
+ roleId: number
+ roleName: string
+ roleCode: string
+ description: string
+ enabled: boolean
+ createTime: string
+ }
+
+ /** 角色搜索参数 */
+ type RoleSearchParams = Partial<
+ Pick &
+ Api.Common.CommonSearchParams
+ >
+ }
+}
diff --git a/adminSystem/src/types/common/index.ts b/adminSystem/src/types/common/index.ts
new file mode 100644
index 0000000..7e751d1
--- /dev/null
+++ b/adminSystem/src/types/common/index.ts
@@ -0,0 +1,95 @@
+/**
+ * 通用类型定义模块
+ *
+ * 提供项目中常用的通用类型定义
+ *
+ * ## 主要功能
+ *
+ * - 状态类型(启用/禁用)
+ * - 性别类型
+ * - 排序方向类型
+ * - 操作类型(增删改查)
+ * - 记录类型(键值对)
+ * - 时间范围类型
+ * - 文件信息类型
+ * - 坐标和尺寸类型
+ * - 响应式断点类型
+ * - 主题和语言类型
+ * - 环境和弹窗类型
+ *
+ * ## 使用场景
+ *
+ * - 通用数据结构定义
+ * - 类型约束和提示
+ * - 减少重复类型定义
+ *
+ * @module types/common/index
+ * @author Art Design Pro Team
+ */
+
+// 导出响应类型
+export * from './response'
+
+// 状态类型
+export type Status = 0 | 1 // 0: 禁用, 1: 启用
+
+// 性别类型
+export type Gender = 'male' | 'female' | 'unknown'
+
+// 排序方向
+export type SortOrder = 'ascending' | 'descending'
+
+// 操作类型
+export type ActionType = 'create' | 'update' | 'delete' | 'view'
+
+// 可选的记录类型
+export type Recordable = Record
+
+// 键值对类型
+export type KeyValue = {
+ key: string
+ value: T
+ label?: string
+}
+
+// 时间范围类型
+export interface TimeRange {
+ startTime: string
+ endTime: string
+}
+
+// 文件类型
+export interface FileInfo {
+ name: string
+ url: string
+ size: number
+ type: string
+ lastModified?: number
+}
+
+// 坐标类型
+export interface Position {
+ x: number
+ y: number
+}
+
+// 尺寸类型
+export interface Size {
+ width: number
+ height: number
+}
+
+// 响应式断点类型
+export type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+
+// 主题类型
+export type ThemeMode = 'light' | 'dark' | 'auto'
+
+// 语言类型
+export type Language = 'zh-CN' | 'en-US'
+
+// 环境类型
+export type Environment = 'development' | 'production' | 'test'
+
+// 弹窗类型
+export type DialogType = 'add' | 'edit'
diff --git a/adminSystem/src/types/common/response.ts b/adminSystem/src/types/common/response.ts
new file mode 100644
index 0000000..4a5fdab
--- /dev/null
+++ b/adminSystem/src/types/common/response.ts
@@ -0,0 +1,30 @@
+/**
+ * API 响应类型定义模块
+ *
+ * 提供统一的 API 响应结构类型定义
+ *
+ * ## 主要功能
+ *
+ * - 基础响应结构定义
+ * - 泛型支持(适配不同数据类型)
+ * - 统一的响应格式约束
+ *
+ * ## 使用场景
+ *
+ * - API 请求响应类型约束
+ * - 接口数据类型定义
+ * - 响应数据解析
+ *
+ * @module types/common/response
+ * @author Art Design Pro Team
+ */
+
+/** 基础 API 响应结构 */
+export interface BaseResponse {
+ /** 状态码 */
+ code: number
+ /** 消息 */
+ msg: string
+ /** 数据 */
+ data: T
+}
diff --git a/adminSystem/src/types/component/chart.ts b/adminSystem/src/types/component/chart.ts
new file mode 100644
index 0000000..c3225c9
--- /dev/null
+++ b/adminSystem/src/types/component/chart.ts
@@ -0,0 +1,324 @@
+/**
+ * 图表组件类型定义模块
+ *
+ * 提供 ECharts 图表组件的完整类型定义
+ *
+ * ## 主要功能
+ *
+ * - 基础图表配置类型
+ * - 柱状图类型定义
+ * - 折线图类型定义
+ * - 饼图/环形图类型定义
+ * - 雷达图类型定义
+ * - K线图类型定义
+ * - 散点图类型定义
+ * - 地图图表类型定义
+ * - 双向堆叠柱状图类型定义
+ * - 图表主题配置类型
+ * - 图表事件回调类型
+ *
+ * ## 使用场景
+ *
+ * - 图表组件 Props 类型约束
+ * - 图表配置类型定义
+ * - 图表数据结构定义
+ * - 图表事件处理
+ *
+ * @module types/component/chart
+ * @author Art Design Pro Team
+ */
+import type { EChartsOption } from '@/plugins/echarts'
+
+// 图例位置类型
+export type LegendPosition = 'bottom' | 'top' | 'left' | 'right'
+
+export type SymbolType =
+ | 'circle'
+ | 'rect'
+ | 'roundRect'
+ | 'triangle'
+ | 'diamond'
+ | 'pin'
+ | 'arrow'
+ | 'none'
+
+// 图表主题配置
+export interface ChartThemeConfig {
+ /** 图表高度 */
+ chartHeight: string
+ /** 字体大小 */
+ fontSize: number
+ /** 字体颜色 */
+ fontColor: string
+ /** 主题颜色 */
+ themeColor: string
+ /** 颜色组 */
+ colors: string[]
+}
+
+// 图表初始化选项
+export interface UseChartOptions {
+ /** 初始化选项 */
+ initOptions?: EChartsOption
+ /** 延迟初始化时间(ms) */
+ initDelay?: number
+ /** IntersectionObserver阈值 */
+ threshold?: number
+ /** 是否自动响应主题变化 */
+ autoTheme?: boolean
+}
+
+// 基础图表 Props 接口 - 统一所有图表的基础属性
+export interface BaseChartProps {
+ /** 图表高度 */
+ height?: string
+ /** 是否加载中 */
+ loading?: boolean
+ isEmpty?: boolean
+ /** 颜色配置 */
+ colors?: string[]
+}
+
+// 轴线显示控制接口 - 统一轴线相关配置
+export interface AxisDisplayProps {
+ /** 是否显示坐标轴标签 */
+ showAxisLabel?: boolean
+ /** 是否显示坐标轴线 */
+ showAxisLine?: boolean
+ /** 是否显示分割线 */
+ showSplitLine?: boolean
+}
+
+// 交互显示控制接口 - 统一交互相关配置
+export interface InteractionProps {
+ /** 是否显示提示框 */
+ showTooltip?: boolean
+ /** 是否显示图例 */
+ showLegend?: boolean
+ /** 图例位置 */
+ legendPosition?: LegendPosition
+}
+
+// 柱状图数据项接口
+export interface BarDataItem {
+ /** 系列名称 */
+ name: string
+ /** 数据值 */
+ data: number[]
+ /** 柱状图宽度 */
+ barWidth?: string | number
+ /** 堆叠分组名称 */
+ stack?: string
+}
+
+// 柱状图 Props 接口 - 统一柱状图配置
+export interface BarChartProps extends BaseChartProps, AxisDisplayProps, InteractionProps {
+ /** 图表数据 - 支持单组数据或多组数据 */
+ data: number[] | BarDataItem[]
+ /** X轴标签数据 */
+ xAxisData?: string[]
+ /** 柱状图宽度 */
+ barWidth?: string | number
+ /** 是否堆叠显示 */
+ stack?: boolean
+ /** 圆角 */
+ borderRadius?: number | number[]
+}
+
+// 折线图数据项接口
+export interface LineDataItem {
+ /** 系列名称 */
+ name: string
+ /** 数据值 */
+ data: number[]
+ /** 线条宽度 */
+ lineWidth?: number
+ /** 是否显示区域填充 */
+ showAreaColor?: boolean
+ /** 区域样式配置 */
+ areaStyle?: {
+ /** 渐变开始透明度 */
+ startOpacity?: number
+ /** 渐变结束透明度 */
+ endOpacity?: number
+ /** 自定义 ECharts areaStyle 配置 */
+ custom?: any
+ }
+ /** 是否平滑曲线 */
+ smooth?: boolean
+ /** 数据点符号 */
+ symbol?: SymbolType
+ /** 数据点大小 */
+ symbolSize?: number
+}
+
+// 折线图 Props 接口 - 统一折线图配置
+export interface LineChartProps extends BaseChartProps, AxisDisplayProps, InteractionProps {
+ /** 图表数据 - 支持单组数据或多组数据 */
+ data: number[] | LineDataItem[]
+ /** X轴标签数据 */
+ xAxisData?: string[]
+ /** 线条宽度 */
+ lineWidth?: number
+ /** 是否显示区域填充 */
+ showAreaColor?: boolean
+ /** 是否平滑曲线 */
+ smooth?: boolean
+ /** 数据点符号 */
+ symbol?: SymbolType
+ /** 数据点大小 */
+ symbolSize?: number
+ /** 多数据动画延迟间隔(毫秒) */
+ animationDelay?: number
+}
+
+// 雷达图数据项接口
+export interface RadarDataItem {
+ /** 系列名称 */
+ name: string
+ /** 数据值 */
+ value: number[]
+}
+
+// 雷达图 Props 接口 - 统一雷达图配置
+export interface RadarChartProps extends BaseChartProps, InteractionProps {
+ /** 雷达图指标配置 */
+ indicator?: Array<{ name: string; max: number }>
+ /** 图表数据 */
+ data?: RadarDataItem[]
+}
+
+// 饼图/环形图数据项接口
+export interface PieDataItem {
+ /** 数据值 */
+ value: number
+ /** 数据名称 */
+ name: string
+}
+
+// 环形图 Props 接口 - 统一环形图配置
+export interface RingChartProps extends BaseChartProps, InteractionProps {
+ /** 图表数据 */
+ data: PieDataItem[]
+ /** 内外半径 */
+ radius?: string[]
+ /** 边框圆角 */
+ borderRadius?: number
+ /** 中心文本 */
+ centerText?: string
+ /** 是否显示标签 */
+ showLabel?: boolean
+}
+
+// K线图数据项接口
+export interface KLineDataItem {
+ /** 时间标签 */
+ time: string
+ /** 开盘价 */
+ open: number
+ /** 收盘价 */
+ close: number
+ /** 最高价 */
+ high: number
+ /** 最低价 */
+ low: number
+}
+
+// K线图 Props 接口 - 统一K线图配置
+export interface KLineChartProps extends BaseChartProps {
+ /** 图表数据 */
+ data?: KLineDataItem[]
+ /** 是否显示数据缩放控件 */
+ showDataZoom?: boolean
+ /** 数据缩放初始开始位置 */
+ dataZoomStart?: number
+ /** 数据缩放初始结束位置 */
+ dataZoomEnd?: number
+}
+
+// 散点图数据项接口
+export interface ScatterDataItem {
+ /** 坐标值 [x, y] */
+ value: number[]
+}
+
+// 散点图 Props 接口 - 统一散点图配置
+export interface ScatterChartProps extends BaseChartProps, AxisDisplayProps, InteractionProps {
+ /** 图表数据 */
+ data?: ScatterDataItem[]
+ /** 散点大小 */
+ symbolSize?: number
+}
+
+// 双柱对比图 Props 接口 - 统一双柱对比图配置
+export interface DualBarCompareChartProps extends BaseChartProps {
+ /** 上方数据 */
+ topData: number[]
+ /** 下方数据 */
+ bottomData: number[]
+ /** X轴标签数据 */
+ xAxisData: string[]
+ /** 上方柱子颜色 */
+ topColor?: string
+ /** 下方柱子颜色 */
+ bottomColor?: string
+ /** 柱状图宽度 */
+ barWidth?: number
+}
+
+// 地图图表 Props 接口 - 统一地图图表配置
+export interface MapChartProps extends BaseChartProps {
+ /** 地图数据 */
+ mapData?: any[]
+ /** 选中区域 */
+ selectedRegion?: string
+ /** 是否显示标签 */
+ showLabels?: boolean
+ /** 是否显示散点 */
+ showScatter?: boolean
+}
+
+// 双向堆叠柱状图 Props 接口(人口金字塔样式)
+export interface BidirectionalBarChartProps
+ extends BaseChartProps,
+ AxisDisplayProps,
+ InteractionProps {
+ /** 正向数据(向上显示) */
+ positiveData: number[]
+ /** 负向数据(向下显示) */
+ negativeData: number[]
+ /** X轴标签数据 */
+ xAxisData?: string[]
+ /** 正向数据名称 */
+ positiveName?: string
+ /** 负向数据名称 */
+ negativeName?: string
+ /** 柱状图宽度 */
+ barWidth?: string | number
+ /** Y轴最小值 */
+ yAxisMin?: number
+ /** Y轴最大值 */
+ yAxisMax?: number
+ /** 是否显示数据标签 */
+ showDataLabel?: boolean
+ /** 正向数据圆角配置 */
+ positiveBorderRadius?: number | number[]
+ /** 负向数据圆角配置 */
+ negativeBorderRadius?: number | number[]
+}
+
+// 图表配置生成器函数类型
+export type ChartOptionGenerator = () => EChartsOption
+
+// 图表事件回调类型
+export type ChartEventCallback = (params: any) => void
+
+// 图表错误信息接口
+export interface ChartError {
+ /** 错误码 */
+ code: string
+ /** 错误信息 */
+ message: string
+ /** 错误详情 */
+ details?: any
+}
diff --git a/adminSystem/src/types/component/index.ts b/adminSystem/src/types/component/index.ts
new file mode 100644
index 0000000..cd89bce
--- /dev/null
+++ b/adminSystem/src/types/component/index.ts
@@ -0,0 +1,145 @@
+/**
+ * 组件类型定义模块
+ *
+ * 提供项目组件的类型定义
+ *
+ * ## 主要功能
+ *
+ * - 搜索组件类型定义
+ * - 表格列配置类型
+ * - 分页配置类型
+ * - 表单规则类型
+ * - 对话框配置类型
+ *
+ * ## 使用场景
+ *
+ * - 组件 Props 类型约束
+ * - 组件配置类型定义
+ * - 组件事件参数类型
+ *
+ * @module types/component/index
+ * @author Art Design Pro Team
+ */
+
+// 搜索组件类型
+export type SearchComponentType =
+ | 'input'
+ | 'select'
+ | 'radio'
+ | 'checkbox'
+ | 'date'
+ | 'datetime'
+ | 'daterange'
+ | 'datetimerange'
+ | 'month'
+ | 'monthrange'
+ | 'year'
+ | 'yearrange'
+ | 'week'
+ | 'time'
+ | 'timerange'
+
+// 搜索框值变化参数
+export interface SearchChangeParams {
+ prop: string
+ val: unknown
+}
+
+// 表格列配置接口
+export interface ColumnOption {
+ // 列类型
+ type?: 'selection' | 'expand' | 'index' | 'globalIndex'
+ // 列属性名
+ prop?: string
+ // 列标题
+ label?: string
+ // 列宽度
+ width?: string | number
+ // 最小列宽度
+ minWidth?: string | number
+ // 固定列
+ fixed?: boolean | 'left' | 'right'
+ // 是否可排序
+ sortable?: boolean
+ // 过滤器选项
+ filters?: any[]
+ // 过滤方法
+ filterMethod?: (value: any, row: any) => boolean
+ // 过滤器位置
+ filterPlacement?: string
+ // 是否禁用
+ disabled?: boolean
+ // 是否显示列
+ visible?: boolean
+ // 是否选中显示
+ checked?: boolean
+ // 自定义渲染函数
+ formatter?: (row: T) => any
+ // 插槽相关配置
+ // 是否使用插槽渲染内容
+ useSlot?: boolean
+ // 插槽名称(默认为 prop 值)
+ slotName?: string
+ // 是否使用表头插槽
+ useHeaderSlot?: boolean
+ // 表头插槽名称(默认为 `${prop}-header`)
+ headerSlotName?: string
+ // 其他属性
+ [key: string]: any
+}
+
+// 分页配置
+export interface PaginationConfig {
+ // 当前页
+ currentPage: number
+ // 每页条数
+ pageSize: number
+ // 总条数
+ total: number
+ // 每页显示个数选择器的选项
+ pageSizes?: number[]
+ // 组件布局
+ layout?: string
+ // 是否为小型分页
+ small?: boolean
+}
+
+// 表单规则
+export interface FormRule {
+ // 是否必填
+ required?: boolean
+ // 错误提示信息
+ message?: string
+ // 触发方式
+ trigger?: string | string[]
+ // 最小长度
+ min?: number
+ // 最大长度
+ max?: number
+ // 正则表达式
+ pattern?: RegExp
+ // 自定义验证函数
+ validator?: (rule: any, value: any, callback: any) => void
+}
+
+// 对话框配置
+export interface DialogConfig {
+ // 标题
+ title: string
+ // 是否显示
+ visible: boolean
+ // 宽度
+ width?: string | number
+ // 是否可以通过点击 modal 关闭
+ closeOnClickModal?: boolean
+ // 是否可以通过按下 ESC 关闭
+ closeOnPressEscape?: boolean
+ // 是否显示关闭按钮
+ showClose?: boolean
+ // 是否在 Dialog 出现时将 body 滚动锁定
+ lockScroll?: boolean
+ // 是否显示遮罩层
+ modal?: boolean
+ // 自定义类名
+ customClass?: string
+}
diff --git a/adminSystem/src/types/config/index.ts b/adminSystem/src/types/config/index.ts
new file mode 100644
index 0000000..dd144de
--- /dev/null
+++ b/adminSystem/src/types/config/index.ts
@@ -0,0 +1,211 @@
+/**
+ * 配置类型定义模块
+ *
+ * 提供系统配置相关的类型定义
+ *
+ * ## 主要功能
+ *
+ * - 主题设置类型
+ * - 菜单布局类型
+ * - 节日配置类型
+ * - 系统基础配置类型
+ * - 快速入口配置类型
+ * - 顶部栏功能配置类型
+ * - 环境配置类型
+ * - 应用配置类型
+ *
+ * ## 使用场景
+ *
+ * - 系统配置文件类型约束
+ * - 配置项类型定义
+ * - 配置数据验证
+ *
+ * @module types/config/index
+ * @author Art Design Pro Team
+ */
+
+import { MenuTypeEnum, SystemThemeEnum } from '@/enums/appEnum'
+import { MenuThemeType, SystemThemeTypes } from '@/types/store'
+
+// 主题设置
+export interface ThemeSetting {
+ /** 主题名称 */
+ name: string
+ /** 系统主题类型 */
+ theme: SystemThemeEnum
+ /** 主题颜色数组 */
+ color: string[]
+ /** 左侧线条颜色 */
+ leftLineColor: string
+ /** 右侧线条颜色 */
+ rightLineColor: string
+ /** 主题图片 */
+ img: string
+}
+
+// 菜单布局
+export interface MenuLayout {
+ /** 布局名称 */
+ name: string
+ /** 菜单类型值 */
+ value: MenuTypeEnum
+ /** 布局预览图 */
+ img: string
+ /** 布局描述 */
+ description?: string
+}
+
+// 节日配置
+export interface FestivalConfig {
+ /** 节日日期(单日)或开始日期(日期范围) */
+ date: string
+ /** 节日结束日期(可选,用于跨日期节日) */
+ endDate?: string
+ /** 节日名称 */
+ name: string
+ /** 烟花图片 */
+ image: string
+ /** 滚动文本 */
+ scrollText: string
+ /** 是否激活 */
+ isActive?: boolean
+ /** 烟花播放次数(可选,默认为 3 次) */
+ count?: number
+}
+
+// 系统基础配置
+export interface SystemBasicConfig {
+ // 系统名称
+ name: string
+ // 系统描述
+ description?: string
+ // 系统logo
+ logo?: string
+ // 系统favicon
+ favicon?: string
+ // 版权信息
+ copyright?: string
+}
+
+// 快速入口基础项
+export interface FastEnterBaseItem {
+ /** 名称 */
+ name: string
+ /** 是否启用 */
+ enabled?: boolean
+ /** 排序权重 */
+ order?: number
+ /** 路由名称 */
+ routeName?: string
+ /** 外部链接 */
+ link?: string
+}
+
+// 快速入口应用项
+export interface FastEnterApplication extends FastEnterBaseItem {
+ /** 应用描述 */
+ description: string
+ /** 图标代码 */
+ icon: string
+ /** 图标颜色 */
+ iconColor: string
+}
+
+// 快速链接项
+export type FastEnterQuickLink = FastEnterBaseItem
+
+// 快速入口配置
+export interface FastEnterConfig {
+ /** 应用列表 */
+ applications: FastEnterApplication[]
+ /** 快速链接 */
+ quickLinks: FastEnterQuickLink[]
+ /** 显示条件(屏幕宽度) */
+ minWidth?: number
+}
+
+// 系统配置
+export interface SystemConfig {
+ // 系统基础信息
+ systemInfo: SystemBasicConfig
+ // 系统主题样式
+ systemThemeStyles: SystemThemeTypes
+ // 设置主题列表
+ settingThemeList: ThemeSetting[]
+ // 菜单布局列表
+ menuLayoutList: MenuLayout[]
+ // 主题列表
+ themeList: MenuThemeType[]
+ // 暗色菜单样式
+ darkMenuStyles: MenuThemeType[]
+ // 系统主色调
+ systemMainColor: readonly string[]
+ // 快速入口配置
+ fastEnter?: FastEnterConfig
+ // 顶部栏功能配置
+ headerBar?: HeaderBarFeatureConfig
+}
+
+// 环境配置
+export interface EnvConfig {
+ // 环境名称
+ NODE_ENV: string
+ // 应用版本
+ VITE_VERSION: string
+ // 应用端口
+ VITE_PORT: string
+ // 应用基础路径
+ VITE_BASE_URL: string
+ // API 地址
+ VITE_API_URL: string
+ // 是否开启 Mock
+ VITE_USE_MOCK?: string
+ // 是否开启压缩
+ VITE_USE_GZIP?: string
+ // 是否开启 CDN
+ VITE_USE_CDN?: string
+}
+
+// 应用配置
+export interface AppConfig extends SystemConfig {
+ // 环境配置
+ env: EnvConfig
+ // 开发模式
+ isDev: boolean
+ // 生产模式
+ isProd: boolean
+ // 测试模式
+ isTest: boolean
+}
+
+// 功能配置项基础接口
+export interface FeatureConfigItem {
+ enabled: boolean
+ description: string
+}
+
+// 顶部栏功能配置接口
+export interface HeaderBarFeatureConfig {
+ /** 菜单按钮 */
+ menuButton: FeatureConfigItem
+ /** 刷新按钮 */
+ refreshButton: FeatureConfigItem
+ /** 快速入口 */
+ fastEnter: FeatureConfigItem
+ /** 面包屑导航 */
+ breadcrumb: FeatureConfigItem
+ /** 全局搜索 */
+ globalSearch: FeatureConfigItem
+ /** 全屏功能 */
+ fullscreen: FeatureConfigItem
+ /** 通知功能 */
+ notification: FeatureConfigItem
+ /** 聊天功能 */
+ chat: FeatureConfigItem
+ /** 多语言切换 */
+ language: FeatureConfigItem
+ /** 设置面板 */
+ settings: FeatureConfigItem
+ /** 主题切换 */
+ themeToggle: FeatureConfigItem
+}
diff --git a/adminSystem/src/types/index.ts b/adminSystem/src/types/index.ts
new file mode 100644
index 0000000..9032fd2
--- /dev/null
+++ b/adminSystem/src/types/index.ts
@@ -0,0 +1,22 @@
+/**
+ * 类型定义统一导出模块
+ * 提供全局类型定义的统一导出入口
+ *
+ * @module types/index
+ * @author Art Design Pro Team
+ */
+
+/** 通用类型定义(基础类型、工具类型等) */
+export * from './common'
+
+/** 组件相关类型定义 */
+export * from './component'
+
+/** 状态管理相关类型定义 */
+export * from './store'
+
+/** 路由相关类型定义 */
+export * from './router'
+
+/** 配置相关类型定义 */
+export * from './config'
diff --git a/adminSystem/src/types/router/index.ts b/adminSystem/src/types/router/index.ts
new file mode 100644
index 0000000..d9ef012
--- /dev/null
+++ b/adminSystem/src/types/router/index.ts
@@ -0,0 +1,80 @@
+/**
+ * 路由类型定义模块
+ *
+ * 提供路由相关的类型定义
+ *
+ * ## 主要功能
+ *
+ * - 路由元数据类型(标题、图标、权限等)
+ * - 应用路由记录类型
+ * - 路由配置扩展
+ *
+ * ## 使用场景
+ *
+ * - 路由配置类型约束
+ * - 路由元数据定义
+ * - 菜单生成
+ * - 权限控制
+ *
+ * @module types/router/index
+ * @author Art Design Pro Team
+ */
+
+import { RouteRecordRaw } from 'vue-router'
+
+/**
+ * 路由元数据接口
+ * 定义路由的各种配置属性
+ */
+export interface RouteMeta extends Record {
+ /** 路由标题 */
+ title: string
+ /** 路由图标 */
+ icon?: string
+ /** 是否显示徽章 */
+ showBadge?: boolean
+ /** 文本徽章 */
+ showTextBadge?: string
+ /** 是否在菜单中隐藏 */
+ isHide?: boolean
+ /** 是否在标签页中隐藏 */
+ isHideTab?: boolean
+ /** 外部链接 */
+ link?: string
+ /** 是否为iframe */
+ isIframe?: boolean
+ /** 是否缓存 */
+ keepAlive?: boolean
+ /** 操作权限 */
+ authList?: Array<{
+ title: string
+ authMark: string
+ }>
+ /** 是否为一级菜单 */
+ isFirstLevel?: boolean
+ /** 角色权限 */
+ roles?: string[]
+ /** 是否固定标签页 */
+ fixedTab?: boolean
+ /** 激活菜单路径 */
+ activePath?: string
+ /** 是否为全屏页面 */
+ isFullPage?: boolean
+ /** 是否为权限按钮行 */
+ isAuthButton?: boolean
+ /** 权限标识 */
+ authMark?: string
+ /** 父级路径 */
+ parentPath?: string
+}
+
+/**
+ * 应用路由记录接口
+ * 扩展 Vue Router 的路由记录类型
+ */
+export interface AppRouteRecord extends Omit {
+ id?: number
+ meta: RouteMeta
+ children?: AppRouteRecord[]
+ component?: string | (() => Promise)
+}
diff --git a/adminSystem/src/types/store/index.ts b/adminSystem/src/types/store/index.ts
new file mode 100644
index 0000000..019801e
--- /dev/null
+++ b/adminSystem/src/types/store/index.ts
@@ -0,0 +1,157 @@
+/**
+ * Store 状态类型定义模块
+ *
+ * 提供 Pinia Store 的状态类型定义
+ *
+ * ## 主要功能
+ *
+ * - 系统主题类型
+ * - 菜单主题类型
+ * - 设置状态类型
+ * - 工作标签页类型
+ * - 用户状态类型
+ * - 菜单状态类型
+ * - 根状态类型
+ *
+ * ## 使用场景
+ *
+ * - Store 状态类型约束
+ * - 状态数据结构定义
+ * - 类型提示和自动补全
+ *
+ * @module types/store/index
+ * @author Art Design Pro Team
+ */
+
+import { MenuThemeEnum, SystemThemeEnum } from '@/enums/appEnum'
+import { LocationQueryRaw } from 'vue-router'
+
+// 系统主题样式(light | dark)
+export interface SystemThemeType {
+ /** 主题类名 */
+ className: string
+}
+
+// 定义包含多个主题的类型
+export type SystemThemeTypes = {
+ [key in Exclude]: SystemThemeType
+}
+
+// 菜单主题样式
+export interface MenuThemeType {
+ /** 主题类型 */
+ theme: MenuThemeEnum
+ /** 背景颜色 */
+ background: string
+ /** 系统名称颜色 */
+ systemNameColor: string
+ /** 文本颜色 */
+ textColor: string
+ /** 图标颜色 */
+ iconColor: string
+ /** 背景图片 */
+ img?: string
+}
+
+// 设置中心
+export interface SettingState {
+ /** 主题 */
+ theme: string
+ /** 是否只保持一个子菜单的展开 */
+ uniqueOpened: boolean
+ /** 是否显示菜单按钮 */
+ menuButton: boolean
+ /** 是否显示刷新按钮 */
+ showRefreshButton: boolean
+ /** 是否显示面包屑 */
+ showCrumbs: boolean
+ /** 是否自动关闭 */
+ autoClose: boolean
+ /** 是否显示工作标签页 */
+ showWorkTab: boolean
+ /** 是否显示语言切换 */
+ showLanguage: boolean
+ /** 是否显示进度条 */
+ showNprogress: boolean
+ /** 主题模式 */
+ themeModel: string
+}
+
+// 多标签
+export interface WorkTab {
+ /** 标签标题 */
+ title: string
+ /** 自定义标题 */
+ customTitle?: string
+ /** 路由路径 */
+ path: string
+ /** 路由名称 */
+ name: string
+ /** 是否缓存 */
+ keepAlive: boolean
+ /** 是否固定标签 */
+ fixedTab?: boolean
+ /** 路由参数 */
+ params?: object
+ /** 路由查询参数 */
+ query?: LocationQueryRaw
+ /** 图标 */
+ icon?: string
+ /** 是否激活 */
+ isActive?: boolean
+}
+
+// 用户Store状态
+export interface UserState {
+ /** 用户信息 */
+ userInfo: Api.Auth.UserInfo | null
+ /** 认证令牌 */
+ token: string | null
+ /** 用户角色列表 */
+ roles: string[]
+ /** 用户权限列表 */
+ permissions: string[]
+}
+
+// 设置Store状态
+export interface SettingStoreState extends SettingState {
+ // 额外的设置状态
+ /** 菜单是否折叠 */
+ collapsed: boolean
+ /** 设备类型 */
+ device: 'desktop' | 'mobile'
+ /** 当前语言 */
+ language: string
+}
+
+// 工作标签页Store状态
+export interface WorkTabState {
+ /** 标签页列表 */
+ tabs: WorkTab[]
+ /** 当前激活的标签页 */
+ activeTab: string
+ /** 缓存的标签页列表 */
+ cachedTabs: string[]
+}
+
+// 菜单Store状态
+export interface MenuState {
+ /** 菜单列表 */
+ menuList: any[]
+ /** 菜单是否已加载 */
+ isLoaded: boolean
+ /** 菜单是否折叠 */
+ collapsed: boolean
+}
+
+// 根Store状态类型
+export interface RootState {
+ /** 用户状态 */
+ user: UserState
+ /** 设置状态 */
+ setting: SettingStoreState
+ /** 工作标签页状态 */
+ workTab: WorkTabState
+ /** 菜单状态 */
+ menu: MenuState
+}
diff --git a/adminSystem/src/utils/constants/index.ts b/adminSystem/src/utils/constants/index.ts
new file mode 100644
index 0000000..831be29
--- /dev/null
+++ b/adminSystem/src/utils/constants/index.ts
@@ -0,0 +1,8 @@
+/**
+ * 常量定义相关工具函数统一导出
+ *
+ * @module utils/constants/index
+ * @author Art Design Pro Team
+ */
+
+export * from './links'
diff --git a/adminSystem/src/utils/constants/links.ts b/adminSystem/src/utils/constants/links.ts
new file mode 100644
index 0000000..06d297e
--- /dev/null
+++ b/adminSystem/src/utils/constants/links.ts
@@ -0,0 +1,35 @@
+/**
+ * 网站链接常量配置
+ * 集中管理便于维护和更新链接地址
+ *
+ * @module utils/constants/links
+ * @author Art Design Pro Team
+ */
+export const WEB_LINKS = {
+ // Github 主页
+ GITHUB_HOME: 'https://github.com/Daymychen/art-design-pro',
+
+ // 项目 Github 主页
+ GITHUB: 'https://github.com/Daymychen/art-design-pro',
+
+ // 个人博客
+ BLOG: 'https://www.artd.pro',
+
+ // 项目文档
+ DOCS: 'https://www.artd.pro/docs/zh/',
+
+ // 精简版本
+ LiteVersion: 'https://www.artd.pro/docs/zh/guide/lite-version.html',
+
+ // v2.6.1版本
+ OldVersion: 'https://www.artd.pro/v2/',
+
+ // 项目社区
+ COMMUNITY: 'https://www.artd.pro/docs/zh/community/communicate.html',
+
+ // 个人 Bilibili 主页
+ BILIBILI: 'https://space.bilibili.com/425500936?spm_id_from=333.1007.0.0',
+
+ // 项目介绍
+ INTRODUCE: 'https://www.artd.pro/docs/zh/guide/introduce.html'
+}
diff --git a/adminSystem/src/utils/form/index.ts b/adminSystem/src/utils/form/index.ts
new file mode 100644
index 0000000..ed23a46
--- /dev/null
+++ b/adminSystem/src/utils/form/index.ts
@@ -0,0 +1,12 @@
+/**
+ * 表单工具函数统一导出
+ *
+ * @module utils/form
+ * @author Art Design Pro Team
+ */
+
+// 表单验证器
+export * from './validator'
+
+// 响应式布局
+export * from './responsive'
diff --git a/adminSystem/src/utils/form/responsive.ts b/adminSystem/src/utils/form/responsive.ts
new file mode 100644
index 0000000..c11df92
--- /dev/null
+++ b/adminSystem/src/utils/form/responsive.ts
@@ -0,0 +1,122 @@
+/**
+ * 表单响应式布局工具模块
+ *
+ * 提供表单项在不同屏幕尺寸下的智能布局计算
+ *
+ * ## 主要功能
+ *
+ * - 响应式断点管理(xs/sm/md/lg/xl)
+ * - 表单列宽自动降级(避免小屏幕压缩)
+ * - 基于阈值的智能 span 计算
+ * - 响应式计算器工厂函数
+ * - 可配置的断点规则
+ *
+ * ## 使用场景
+ *
+ * - 表单组件响应式布局
+ * - 搜索表单自适应
+ * - 移动端表单优化
+ * - 多列表单布局
+ *
+ * ## 断点说明(基于 Element Plus Grid 24 栅格系统):
+ * - xs (手机): < 768px,小于 12 时降级为 24(满宽)
+ * - sm (平板): ≥ 768px,小于 12 时降级为 12(半宽)
+ * - md (中等屏幕): ≥ 992px,小于 8 时降级为 8(三分之一宽)
+ * - lg (大屏幕): ≥ 1200px,直接使用设置的 span
+ * - xl (超大屏幕): ≥ 1920px,直接使用设置的 span
+ *
+ * ## 核心功能
+ *
+ * - calculateResponsiveSpan: 计算响应式列宽
+ * - createResponsiveSpanCalculator: 创建 span 计算器(柯里化)
+ *
+ * @module utils/form/responsive
+ * @author Art Design Pro Team
+ */
+
+/**
+ * 响应式断点类型
+ */
+export type ResponsiveBreakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl'
+
+/**
+ * 断点配置映射
+ */
+interface BreakpointConfig {
+ /** 最小 span 阈值 */
+ threshold: number
+ /** 降级后的 span 值 */
+ fallback: number
+}
+
+/**
+ * 响应式断点配置
+ */
+const BREAKPOINT_CONFIG: Record = {
+ xs: { threshold: 12, fallback: 24 }, // 手机:小于 12 时使用满宽
+ sm: { threshold: 12, fallback: 12 }, // 平板:小于 12 时使用半宽
+ md: { threshold: 8, fallback: 8 }, // 中等屏幕:小于 8 时使用三分之一宽
+ lg: null, // 大屏幕:直接使用设置的 span
+ xl: null // 超大屏幕:直接使用设置的 span
+}
+
+/**
+ * 计算响应式列宽
+ *
+ * 根据屏幕尺寸智能降级,避免小屏幕上表单项被压缩过小
+ *
+ * @param itemSpan 表单项自定义的 span 值
+ * @param defaultSpan 默认的 span 值
+ * @param breakpoint 当前断点
+ * @returns 计算后的 span 值
+ *
+ * @example
+ * ```ts
+ * // 在 xs 断点下,span 为 6 会降级为 24(满宽)
+ * calculateResponsiveSpan(6, 6, 'xs') // 24
+ *
+ * // 在 md 断点下,span 为 6 会降级为 8(三分之一宽)
+ * calculateResponsiveSpan(6, 6, 'md') // 8
+ *
+ * // 在 lg 断点下,直接使用原始 span
+ * calculateResponsiveSpan(6, 6, 'lg') // 6
+ * ```
+ */
+export function calculateResponsiveSpan(
+ itemSpan: number | undefined,
+ defaultSpan: number,
+ breakpoint: ResponsiveBreakpoint
+): number {
+ const finalSpan = itemSpan ?? defaultSpan
+ const config = BREAKPOINT_CONFIG[breakpoint]
+
+ // 如果没有配置(lg/xl),直接返回原始 span
+ if (!config) {
+ return finalSpan
+ }
+
+ // 如果 span 小于阈值,使用降级值
+ return finalSpan >= config.threshold ? finalSpan : config.fallback
+}
+
+/**
+ * 创建响应式 span 计算器
+ *
+ * 返回一个函数,用于计算指定断点下的 span 值
+ *
+ * @param defaultSpan 默认的 span 值
+ * @returns span 计算函数
+ *
+ * @example
+ * ```ts
+ * const getColSpan = createResponsiveSpanCalculator(6)
+ * getColSpan(undefined, 'xs') // 24
+ * getColSpan(8, 'md') // 8
+ * getColSpan(12, 'lg') // 12
+ * ```
+ */
+export function createResponsiveSpanCalculator(defaultSpan: number) {
+ return (itemSpan: number | undefined, breakpoint: ResponsiveBreakpoint): number => {
+ return calculateResponsiveSpan(itemSpan, defaultSpan, breakpoint)
+ }
+}
diff --git a/adminSystem/src/utils/form/validator.ts b/adminSystem/src/utils/form/validator.ts
new file mode 100644
index 0000000..3670763
--- /dev/null
+++ b/adminSystem/src/utils/form/validator.ts
@@ -0,0 +1,316 @@
+/**
+ * 表单验证工具模块
+ *
+ * 提供全面的表单字段验证功能
+ *
+ * ## 主要功能
+ *
+ * - 手机号码验证(中国大陆格式)
+ * - 固定电话验证(支持区号格式)
+ * - 用户账号验证(字母开头,支持数字和下划线)
+ * - 密码强度验证(普通密码、强密码)
+ * - 密码强度评估(弱、中、强)
+ * - IPv4 地址验证
+ * - 邮箱地址验证(RFC 5322 标准)
+ * - URL 地址验证
+ * - 身份证号码验证(18位,含校验码验证)
+ * - 银行卡号验证(Luhn 算法)
+ * - 字符串空格处理
+ *
+ * ## 验证规则
+ *
+ * - 手机号:1开头,第二位3-9,共11位
+ * - 账号:字母开头,5-20位,支持字母数字下划线
+ * - 普通密码:6-20位,必须包含字母和数字
+ * - 强密码:8-20位,必须包含大小写字母、数字和特殊字符
+ * - 身份证:18位,含出生日期和校验码验证
+ * - 银行卡:13-19位,通过 Luhn 算法验证
+ *
+ * @module utils/validation/formValidator
+ * @author Art Design Pro Team
+ */
+
+/**
+ * 密码强度级别枚举
+ */
+export enum PasswordStrength {
+ WEAK = '弱',
+ MEDIUM = '中',
+ STRONG = '强'
+}
+
+/**
+ * 去除字符串首尾空格
+ * @param value 待处理的字符串
+ * @returns 返回去除首尾空格后的字符串
+ */
+export function trimSpaces(value: string): string {
+ if (typeof value !== 'string') {
+ return ''
+ }
+ return value.trim()
+}
+
+/**
+ * 验证手机号码(中国大陆)
+ * @param value 手机号码字符串
+ * @returns 返回验证结果,true表示格式正确
+ */
+export function validatePhone(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ // 中国大陆手机号码:1开头,第二位为3-9,共11位数字
+ const phoneRegex = /^1[3-9]\d{9}$/
+ return phoneRegex.test(value.trim())
+}
+
+/**
+ * 验证固定电话号码(中国大陆)
+ * @param value 电话号码字符串
+ * @returns 返回验证结果,true表示格式正确
+ */
+export function validateTelPhone(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ // 支持格式:区号-号码,如:010-12345678、0755-1234567
+ const telRegex = /^0\d{2,3}-?\d{7,8}$/
+ return telRegex.test(value.trim().replace(/\s+/g, ''))
+}
+
+/**
+ * 验证用户账号
+ * @param value 账号字符串
+ * @returns 返回验证结果,true表示格式正确
+ * @description 规则:字母开头,5-20位,支持字母、数字、下划线
+ */
+export function validateAccount(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ // 字母开头,5-20位,支持字母、数字、下划线
+ const accountRegex = /^[a-zA-Z][a-zA-Z0-9_]{4,19}$/
+ return accountRegex.test(value.trim())
+}
+
+/**
+ * 验证密码
+ * @param value 密码字符串
+ * @returns 返回验证结果,true表示格式正确
+ * @description 规则:6-20位,必须包含字母和数字
+ */
+export function validatePassword(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ const trimmedValue = value.trim()
+
+ // 长度检查
+ if (trimmedValue.length < 6 || trimmedValue.length > 20) {
+ return false
+ }
+
+ // 必须包含字母和数字
+ const hasLetter = /[a-zA-Z]/.test(trimmedValue)
+ const hasNumber = /\d/.test(trimmedValue)
+
+ return hasLetter && hasNumber
+}
+
+/**
+ * 验证强密码
+ * @param value 密码字符串
+ * @returns 返回验证结果,true表示格式正确
+ * @description 规则:8-20位,必须包含大写字母、小写字母、数字和特殊字符
+ */
+export function validateStrongPassword(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ const trimmedValue = value.trim()
+
+ // 长度检查
+ if (trimmedValue.length < 8 || trimmedValue.length > 20) {
+ return false
+ }
+
+ // 必须包含:大写字母、小写字母、数字、特殊字符
+ const hasUpperCase = /[A-Z]/.test(trimmedValue)
+ const hasLowerCase = /[a-z]/.test(trimmedValue)
+ const hasNumber = /\d/.test(trimmedValue)
+ const hasSpecialChar = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(trimmedValue)
+
+ return hasUpperCase && hasLowerCase && hasNumber && hasSpecialChar
+}
+
+/**
+ * 获取密码强度
+ * @param value 密码字符串
+ * @returns 返回密码强度:弱、中、强
+ * @description 弱:纯数字/纯字母/纯特殊字符;中:两种组合;强:三种或以上组合
+ */
+export function getPasswordStrength(value: string): PasswordStrength {
+ if (!value || typeof value !== 'string') {
+ return PasswordStrength.WEAK
+ }
+
+ const trimmedValue = value.trim()
+
+ if (trimmedValue.length < 6) {
+ return PasswordStrength.WEAK
+ }
+
+ const hasUpperCase = /[A-Z]/.test(trimmedValue)
+ const hasLowerCase = /[a-z]/.test(trimmedValue)
+ const hasNumber = /\d/.test(trimmedValue)
+ const hasSpecialChar = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(trimmedValue)
+
+ const typeCount = [hasUpperCase, hasLowerCase, hasNumber, hasSpecialChar].filter(Boolean).length
+
+ if (typeCount >= 3) {
+ return PasswordStrength.STRONG
+ } else if (typeCount >= 2) {
+ return PasswordStrength.MEDIUM
+ } else {
+ return PasswordStrength.WEAK
+ }
+}
+
+/**
+ * 验证IPv4地址
+ * @param value IP地址字符串
+ * @returns 返回验证结果,true表示格式正确
+ */
+export function validateIPv4Address(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ const trimmedValue = value.trim()
+ const ipRegex = /^((25[0-5]|2[0-4]\d|[01]?\d{1,2})\.){3}(25[0-5]|2[0-4]\d|[01]?\d{1,2})$/
+
+ if (!ipRegex.test(trimmedValue)) {
+ return false
+ }
+
+ // 额外检查每个段是否在有效范围内
+ const segments = trimmedValue.split('.')
+ return segments.every((segment) => {
+ const num = parseInt(segment, 10)
+ return num >= 0 && num <= 255
+ })
+}
+
+/**
+ * 验证邮箱地址
+ * @param value 邮箱地址字符串
+ * @returns 返回验证结果,true表示格式正确
+ */
+export function validateEmail(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ const trimmedValue = value.trim()
+
+ // RFC 5322 标准的简化版邮箱正则
+ const emailRegex =
+ /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
+
+ return emailRegex.test(trimmedValue) && trimmedValue.length <= 254
+}
+
+/**
+ * 验证URL地址
+ * @param value URL字符串
+ * @returns 返回验证结果,true表示格式正确
+ */
+export function validateURL(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ try {
+ new URL(value.trim())
+ return true
+ } catch {
+ return false
+ }
+}
+
+/**
+ * 验证身份证号码(中国大陆)
+ * @param value 身份证号码字符串
+ * @returns 返回验证结果,true表示格式正确
+ */
+export function validateChineseIDCard(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ const trimmedValue = value.trim()
+
+ // 18位身份证号码正则
+ const idCardRegex =
+ /^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/
+
+ if (!idCardRegex.test(trimmedValue)) {
+ return false
+ }
+
+ // 验证校验码
+ const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
+ const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
+
+ let sum = 0
+ for (let i = 0; i < 17; i++) {
+ sum += parseInt(trimmedValue[i]) * weights[i]
+ }
+
+ const checkCode = checkCodes[sum % 11]
+ return trimmedValue[17].toUpperCase() === checkCode
+}
+
+/**
+ * 验证银行卡号
+ * @param value 银行卡号字符串
+ * @returns 返回验证结果,true表示格式正确
+ */
+export function validateBankCard(value: string): boolean {
+ if (!value || typeof value !== 'string') {
+ return false
+ }
+
+ const trimmedValue = value.trim().replace(/\s+/g, '')
+
+ // 银行卡号通常为13-19位数字
+ if (!/^\d{13,19}$/.test(trimmedValue)) {
+ return false
+ }
+
+ // Luhn算法验证
+ let sum = 0
+ let shouldDouble = false
+
+ for (let i = trimmedValue.length - 1; i >= 0; i--) {
+ let digit = parseInt(trimmedValue[i])
+
+ if (shouldDouble) {
+ digit *= 2
+ if (digit > 9) {
+ digit = (digit % 10) + 1
+ }
+ }
+
+ sum += digit
+ shouldDouble = !shouldDouble
+ }
+
+ return sum % 10 === 0
+}
diff --git a/adminSystem/src/utils/http/error.ts b/adminSystem/src/utils/http/error.ts
new file mode 100644
index 0000000..0f2c1ae
--- /dev/null
+++ b/adminSystem/src/utils/http/error.ts
@@ -0,0 +1,182 @@
+/**
+ * HTTP 错误处理模块
+ *
+ * 提供统一的 HTTP 请求错误处理机制
+ *
+ * ## 主要功能
+ *
+ * - 自定义 HttpError 错误类,封装错误信息、状态码、时间戳等
+ * - 错误拦截和转换,将 Axios 错误转换为标准的 HttpError
+ * - 错误消息国际化处理,根据状态码返回对应的多语言错误提示
+ * - 错误日志记录,便于问题追踪和调试
+ * - 错误和成功消息的统一展示
+ * - 类型守卫函数,用于判断错误类型
+ *
+ * ## 使用场景
+ *
+ * - HTTP 请求拦截器中统一处理错误
+ * - 业务代码中捕获和处理特定错误
+ * - 错误日志收集和上报
+ *
+ * @module utils/http/error
+ * @author Art Design Pro Team
+ */
+import { AxiosError } from 'axios'
+import { ApiStatus } from './status'
+import { $t } from '@/locales'
+
+// 错误响应接口
+export interface ErrorResponse {
+ /** 错误状态码 */
+ code: number
+ /** 错误消息 */
+ msg: string
+ /** 错误附加数据 */
+ data?: unknown
+}
+
+// 错误日志数据接口
+export interface ErrorLogData {
+ /** 错误状态码 */
+ code: number
+ /** 错误消息 */
+ message: string
+ /** 错误附加数据 */
+ data?: unknown
+ /** 错误发生时间戳 */
+ timestamp: string
+ /** 请求 URL */
+ url?: string
+ /** 请求方法 */
+ method?: string
+ /** 错误堆栈信息 */
+ stack?: string
+}
+
+// 自定义 HttpError 类
+export class HttpError extends Error {
+ public readonly code: number
+ public readonly data?: unknown
+ public readonly timestamp: string
+ public readonly url?: string
+ public readonly method?: string
+
+ constructor(
+ message: string,
+ code: number,
+ options?: {
+ data?: unknown
+ url?: string
+ method?: string
+ }
+ ) {
+ super(message)
+ this.name = 'HttpError'
+ this.code = code
+ this.data = options?.data
+ this.timestamp = new Date().toISOString()
+ this.url = options?.url
+ this.method = options?.method
+ }
+
+ public toLogData(): ErrorLogData {
+ return {
+ code: this.code,
+ message: this.message,
+ data: this.data,
+ timestamp: this.timestamp,
+ url: this.url,
+ method: this.method,
+ stack: this.stack
+ }
+ }
+}
+
+/**
+ * 获取错误消息
+ * @param status 错误状态码
+ * @returns 错误消息
+ */
+const getErrorMessage = (status: number): string => {
+ const errorMap: Record = {
+ [ApiStatus.unauthorized]: 'httpMsg.unauthorized',
+ [ApiStatus.forbidden]: 'httpMsg.forbidden',
+ [ApiStatus.notFound]: 'httpMsg.notFound',
+ [ApiStatus.methodNotAllowed]: 'httpMsg.methodNotAllowed',
+ [ApiStatus.requestTimeout]: 'httpMsg.requestTimeout',
+ [ApiStatus.internalServerError]: 'httpMsg.internalServerError',
+ [ApiStatus.badGateway]: 'httpMsg.badGateway',
+ [ApiStatus.serviceUnavailable]: 'httpMsg.serviceUnavailable',
+ [ApiStatus.gatewayTimeout]: 'httpMsg.gatewayTimeout'
+ }
+
+ return $t(errorMap[status] || 'httpMsg.internalServerError')
+}
+
+/**
+ * 处理错误
+ * @param error 错误对象
+ * @returns 错误对象
+ */
+export function handleError(error: AxiosError): never {
+ // 处理取消的请求
+ if (error.code === 'ERR_CANCELED') {
+ console.warn('Request cancelled:', error.message)
+ throw new HttpError($t('httpMsg.requestCancelled'), ApiStatus.error)
+ }
+
+ const statusCode = error.response?.status
+ const errorMessage = error.response?.data?.msg || error.message
+ const requestConfig = error.config
+
+ // 处理网络错误
+ if (!error.response) {
+ throw new HttpError($t('httpMsg.networkError'), ApiStatus.error, {
+ url: requestConfig?.url,
+ method: requestConfig?.method?.toUpperCase()
+ })
+ }
+
+ // 处理 HTTP 状态码错误
+ const message = statusCode
+ ? getErrorMessage(statusCode)
+ : errorMessage || $t('httpMsg.requestFailed')
+ throw new HttpError(message, statusCode || ApiStatus.error, {
+ data: error.response.data,
+ url: requestConfig?.url,
+ method: requestConfig?.method?.toUpperCase()
+ })
+}
+
+/**
+ * 显示错误消息
+ * @param error 错误对象
+ * @param showMessage 是否显示错误消息
+ */
+export function showError(error: HttpError, showMessage: boolean = true): void {
+ if (showMessage) {
+ ElMessage.error(error.message)
+ }
+ // 记录错误日志
+ console.error('[HTTP Error]', error.toLogData())
+}
+
+/**
+ * 显示成功消息
+ * @param message 成功消息
+ * @param showMessage 是否显示消息
+ */
+export function showSuccess(message: string, showMessage: boolean = true): void {
+ if (showMessage) {
+ ElMessage.success(message)
+ }
+}
+
+/**
+ * 判断是否为 HttpError 类型
+ * @param error 错误对象
+ * @returns 是否为 HttpError 类型
+ */
+export const isHttpError = (error: unknown): error is HttpError => {
+ return error instanceof HttpError
+}
diff --git a/adminSystem/src/utils/http/index.ts b/adminSystem/src/utils/http/index.ts
new file mode 100644
index 0000000..0e0002a
--- /dev/null
+++ b/adminSystem/src/utils/http/index.ts
@@ -0,0 +1,214 @@
+/**
+ * HTTP 请求封装模块
+ * 基于 Axios 封装的 HTTP 请求工具,提供统一的请求/响应处理
+ *
+ * ## 主要功能
+ *
+ * - 请求/响应拦截器(自动添加 Token、统一错误处理)
+ * - 401 未授权自动登出(带防抖机制)
+ * - 请求失败自动重试(可配置)
+ * - 统一的成功/错误消息提示
+ * - 支持 GET/POST/PUT/DELETE 等常用方法
+ *
+ * @module utils/http
+ * @author Art Design Pro Team
+ */
+
+import axios, { AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
+import { useUserStore } from '@/store/modules/user'
+import { ApiStatus } from './status'
+import { HttpError, handleError, showError, showSuccess } from './error'
+import { $t } from '@/locales'
+import { BaseResponse } from '@/types'
+
+/** 请求配置常量 */
+const REQUEST_TIMEOUT = 15000
+const LOGOUT_DELAY = 500
+const MAX_RETRIES = 0
+const RETRY_DELAY = 1000
+const UNAUTHORIZED_DEBOUNCE_TIME = 3000
+
+/** 401防抖状态 */
+let isUnauthorizedErrorShown = false
+let unauthorizedTimer: NodeJS.Timeout | null = null
+
+/** 扩展 AxiosRequestConfig */
+interface ExtendedAxiosRequestConfig extends AxiosRequestConfig {
+ showErrorMessage?: boolean
+ showSuccessMessage?: boolean
+}
+
+const { VITE_API_URL, VITE_WITH_CREDENTIALS } = import.meta.env
+
+/** Axios实例 */
+const axiosInstance = axios.create({
+ timeout: REQUEST_TIMEOUT,
+ baseURL: VITE_API_URL,
+ withCredentials: VITE_WITH_CREDENTIALS === 'true',
+ validateStatus: (status) => status >= 200 && status < 300,
+ transformResponse: [
+ (data, headers) => {
+ const contentType = headers['content-type']
+ if (contentType?.includes('application/json')) {
+ try {
+ return JSON.parse(data)
+ } catch {
+ return data
+ }
+ }
+ return data
+ }
+ ]
+})
+
+/** 请求拦截器 */
+axiosInstance.interceptors.request.use(
+ (request: InternalAxiosRequestConfig) => {
+ const { accessToken } = useUserStore()
+ if (accessToken) request.headers.set('Authorization', accessToken)
+
+ if (request.data && !(request.data instanceof FormData) && !request.headers['Content-Type']) {
+ request.headers.set('Content-Type', 'application/json')
+ request.data = JSON.stringify(request.data)
+ }
+
+ return request
+ },
+ (error) => {
+ showError(createHttpError($t('httpMsg.requestConfigError'), ApiStatus.error))
+ return Promise.reject(error)
+ }
+)
+
+/** 响应拦截器 */
+axiosInstance.interceptors.response.use(
+ (response: AxiosResponse) => {
+ const { code, msg } = response.data
+ if (code === ApiStatus.success) return response
+ if (code === ApiStatus.unauthorized) handleUnauthorizedError(msg)
+ throw createHttpError(msg || $t('httpMsg.requestFailed'), code)
+ },
+ (error) => {
+ if (error.response?.status === ApiStatus.unauthorized) handleUnauthorizedError()
+ return Promise.reject(handleError(error))
+ }
+)
+
+/** 统一创建HttpError */
+function createHttpError(message: string, code: number) {
+ return new HttpError(message, code)
+}
+
+/** 处理401错误(带防抖) */
+function handleUnauthorizedError(message?: string): never {
+ const error = createHttpError(message || $t('httpMsg.unauthorized'), ApiStatus.unauthorized)
+
+ if (!isUnauthorizedErrorShown) {
+ isUnauthorizedErrorShown = true
+ logOut()
+
+ unauthorizedTimer = setTimeout(resetUnauthorizedError, UNAUTHORIZED_DEBOUNCE_TIME)
+
+ showError(error, true)
+ throw error
+ }
+
+ throw error
+}
+
+/** 重置401防抖状态 */
+function resetUnauthorizedError() {
+ isUnauthorizedErrorShown = false
+ if (unauthorizedTimer) clearTimeout(unauthorizedTimer)
+ unauthorizedTimer = null
+}
+
+/** 退出登录函数 */
+function logOut() {
+ setTimeout(() => {
+ useUserStore().logOut()
+ }, LOGOUT_DELAY)
+}
+
+/** 是否需要重试 */
+function shouldRetry(statusCode: number) {
+ return [
+ ApiStatus.requestTimeout,
+ ApiStatus.internalServerError,
+ ApiStatus.badGateway,
+ ApiStatus.serviceUnavailable,
+ ApiStatus.gatewayTimeout
+ ].includes(statusCode)
+}
+
+/** 请求重试逻辑 */
+async function retryRequest(
+ config: ExtendedAxiosRequestConfig,
+ retries: number = MAX_RETRIES
+): Promise {
+ try {
+ return await request(config)
+ } catch (error) {
+ if (retries > 0 && error instanceof HttpError && shouldRetry(error.code)) {
+ await delay(RETRY_DELAY)
+ return retryRequest(config, retries - 1)
+ }
+ throw error
+ }
+}
+
+/** 延迟函数 */
+function delay(ms: number) {
+ return new Promise((resolve) => setTimeout(resolve, ms))
+}
+
+/** 请求函数 */
+async function request(config: ExtendedAxiosRequestConfig): Promise {
+ // POST | PUT 参数自动填充
+ if (
+ ['POST', 'PUT'].includes(config.method?.toUpperCase() || '') &&
+ config.params &&
+ !config.data
+ ) {
+ config.data = config.params
+ config.params = undefined
+ }
+
+ try {
+ const res = await axiosInstance.request>(config)
+
+ // 显示成功消息
+ if (config.showSuccessMessage && res.data.msg) {
+ showSuccess(res.data.msg)
+ }
+
+ return res.data.data as T
+ } catch (error) {
+ if (error instanceof HttpError && error.code !== ApiStatus.unauthorized) {
+ const showMsg = config.showErrorMessage !== false
+ showError(error, showMsg)
+ }
+ return Promise.reject(error)
+ }
+}
+
+/** API方法集合 */
+const api = {
+ get(config: ExtendedAxiosRequestConfig) {
+ return retryRequest({ ...config, method: 'GET' })
+ },
+ post(config: ExtendedAxiosRequestConfig) {
+ return retryRequest({ ...config, method: 'POST' })
+ },
+ put(config: ExtendedAxiosRequestConfig) {
+ return retryRequest({ ...config, method: 'PUT' })
+ },
+ del(config: ExtendedAxiosRequestConfig) {
+ return retryRequest({ ...config, method: 'DELETE' })
+ },
+ request(config: ExtendedAxiosRequestConfig) {
+ return retryRequest(config)
+ }
+}
+
+export default api
diff --git a/adminSystem/src/utils/http/status.ts b/adminSystem/src/utils/http/status.ts
new file mode 100644
index 0000000..989bb37
--- /dev/null
+++ b/adminSystem/src/utils/http/status.ts
@@ -0,0 +1,18 @@
+/**
+ * 接口状态码
+ */
+export enum ApiStatus {
+ success = 200, // 成功
+ error = 400, // 错误
+ unauthorized = 401, // 未授权
+ forbidden = 403, // 禁止访问
+ notFound = 404, // 未找到
+ methodNotAllowed = 405, // 方法不允许
+ requestTimeout = 408, // 请求超时
+ internalServerError = 500, // 服务器错误
+ notImplemented = 501, // 未实现
+ badGateway = 502, // 网关错误
+ serviceUnavailable = 503, // 服务不可用
+ gatewayTimeout = 504, // 网关超时
+ httpVersionNotSupported = 505 // HTTP版本不支持
+}
diff --git a/adminSystem/src/utils/index.ts b/adminSystem/src/utils/index.ts
new file mode 100644
index 0000000..f1e1b77
--- /dev/null
+++ b/adminSystem/src/utils/index.ts
@@ -0,0 +1,34 @@
+/**
+ * Utils 工具函数统一导出
+ * 提供向后兼容性和便捷导入
+ *
+ * @module utils/index
+ * @author Art Design Pro Team
+ */
+
+// UI 相关
+export * from './ui'
+
+// 路由相关
+export * from './router'
+
+// 路由导航相关
+export * from './navigation'
+
+// 系统管理相关
+export * from './sys'
+
+// 常量定义相关
+export * from './constants'
+
+// 存储相关
+export * from './storage'
+
+// HTTP 相关
+export * from './http'
+
+// 表单相关
+export * from './form'
+
+// socket 相关
+export * from './socket'
diff --git a/adminSystem/src/utils/navigation/index.ts b/adminSystem/src/utils/navigation/index.ts
new file mode 100644
index 0000000..0b84e78
--- /dev/null
+++ b/adminSystem/src/utils/navigation/index.ts
@@ -0,0 +1,10 @@
+/**
+ * 路由和导航相关工具函数统一导出
+ *
+ * @module utils/navigation/index
+ * @author Art Design Pro Team
+ */
+
+export * from './jump'
+export * from './worktab'
+export * from './route'
diff --git a/adminSystem/src/utils/navigation/jump.ts b/adminSystem/src/utils/navigation/jump.ts
new file mode 100644
index 0000000..4fde1e8
--- /dev/null
+++ b/adminSystem/src/utils/navigation/jump.ts
@@ -0,0 +1,62 @@
+/**
+ * 导航跳转工具模块
+ *
+ * 提供统一的页面跳转和导航功能
+ *
+ * ## 主要功能
+ *
+ * - 外部链接打开(新窗口)
+ * - 菜单项跳转处理(支持内部路由和外部链接)
+ * - iframe 页面跳转支持
+ * - 递归查找并跳转到第一个可见的子菜单
+ * - 智能判断跳转目标类型(外部链接/内部路由)
+ *
+ * @module utils/navigation/jump
+ * @author Art Design Pro Team
+ */
+import { AppRouteRecord } from '@/types/router'
+import { router } from '@/router'
+
+// 打开外部链接
+export const openExternalLink = (link: string) => {
+ window.open(link, '_blank')
+}
+
+/**
+ * 菜单跳转
+ * @param item 菜单项
+ * @param jumpToFirst 是否跳转到第一个子菜单
+ * @returns
+ */
+export const handleMenuJump = (item: AppRouteRecord, jumpToFirst: boolean = false) => {
+ // 处理外部链接
+ const { link, isIframe } = item.meta
+ if (link && !isIframe) {
+ return openExternalLink(link)
+ }
+
+ // 如果不需要跳转到第一个子菜单,或者没有子菜单,直接跳转当前路径
+ if (!jumpToFirst || !item.children?.length) {
+ return router.push(item.path)
+ }
+
+ // 递归查找第一个可见的叶子节点菜单
+ const findFirstLeafMenu = (items: AppRouteRecord[]): AppRouteRecord => {
+ for (const child of items) {
+ if (!child.meta.isHide) {
+ return child.children?.length ? findFirstLeafMenu(child.children) : child
+ }
+ }
+ return items[0]
+ }
+
+ const firstChild = findFirstLeafMenu(item.children)
+
+ // 如果第一个子菜单是外部链接则打开新窗口
+ if (firstChild.meta?.link) {
+ return openExternalLink(firstChild.meta.link)
+ }
+
+ // 跳转到子菜单路径
+ router.push(firstChild.path)
+}
diff --git a/adminSystem/src/utils/navigation/route.ts b/adminSystem/src/utils/navigation/route.ts
new file mode 100644
index 0000000..9ca4f29
--- /dev/null
+++ b/adminSystem/src/utils/navigation/route.ts
@@ -0,0 +1,78 @@
+/**
+ * 路由工具模块
+ *
+ * 提供路由处理和菜单路径相关的工具函数
+ *
+ * ## 主要功能
+ *
+ * - iframe 路由检测,判断是否为外部嵌入页面
+ * - 菜单项有效性验证,过滤隐藏和无效菜单
+ * - 路径标准化处理,统一路径格式
+ * - 递归查找菜单树中第一个有效路径
+ * - 支持多级嵌套菜单的路径解析
+ *
+ * ## 使用场景
+ *
+ * - 系统初始化时获取默认跳转路径
+ * - 菜单权限过滤后获取首个可访问页面
+ * - 路由重定向逻辑处理
+ * - iframe 页面特殊处理
+ *
+ * @module utils/navigation/route
+ * @author Art Design Pro Team
+ */
+
+import { AppRouteRecord } from '@/types'
+
+// 检查是否为 iframe 路由
+export function isIframe(url: string): boolean {
+ return url.startsWith('/outside/iframe/')
+}
+
+/**
+ * 验证菜单项是否有效
+ * @param menuItem 菜单项
+ * @returns 是否为有效菜单项
+ */
+const isValidMenuItem = (menuItem: AppRouteRecord): boolean => {
+ return !!(menuItem.path && menuItem.path.trim() && !menuItem.meta?.isHide)
+}
+
+/**
+ * 标准化路径格式
+ * @param path 路径
+ * @returns 标准化后的路径
+ */
+const normalizePath = (path: string): string => {
+ return path.startsWith('/') ? path : `/${path}`
+}
+
+/**
+ * 递归获取菜单的第一个有效路径
+ * @param menuList 菜单列表
+ * @returns 第一个有效路径,如果没有找到则返回空字符串
+ */
+export const getFirstMenuPath = (menuList: AppRouteRecord[]): string => {
+ if (!Array.isArray(menuList) || menuList.length === 0) {
+ return ''
+ }
+
+ for (const menuItem of menuList) {
+ if (!isValidMenuItem(menuItem)) {
+ continue
+ }
+
+ // 如果有子菜单,优先查找子菜单
+ if (menuItem.children?.length) {
+ const childPath = getFirstMenuPath(menuItem.children)
+ if (childPath) {
+ return childPath
+ }
+ }
+
+ // 返回当前菜单项的标准化路径
+ return normalizePath(menuItem.path!)
+ }
+
+ return ''
+}
diff --git a/adminSystem/src/utils/navigation/worktab.ts b/adminSystem/src/utils/navigation/worktab.ts
new file mode 100644
index 0000000..6db6a77
--- /dev/null
+++ b/adminSystem/src/utils/navigation/worktab.ts
@@ -0,0 +1,67 @@
+/**
+ * 工作标签页管理模块
+ *
+ * 提供工作标签页(Worktab)的自动管理功能
+ *
+ * ## 主要功能
+ *
+ * - 根据路由导航自动创建和更新工作标签页
+ * - iframe 页面标签页特殊处理
+ * - 标签页信息提取(标题、路径、缓存状态等)
+ * - 固定标签页支持
+ * - 根据系统设置控制标签页显示
+ * - 首页标签页特殊处理
+ *
+ * ## 使用场景
+ *
+ * - 路由守卫中自动创建标签页
+ * - 页面切换时更新标签页状态
+ * - 多标签页导航系统
+ *
+ * @module utils/navigation/worktab
+ * @author Art Design Pro Team
+ */
+import { useWorktabStore } from '@/store/modules/worktab'
+import { RouteLocationNormalized } from 'vue-router'
+import { isIframe } from './route'
+import { useSettingStore } from '@/store/modules/setting'
+import { IframeRouteManager } from '@/router/core'
+import { useCommon } from '@/hooks/core/useCommon'
+
+/**
+ * 根据当前路由信息设置工作标签页(worktab)
+ * @param to 当前路由对象
+ */
+export const setWorktab = (to: RouteLocationNormalized): void => {
+ const worktabStore = useWorktabStore()
+ const { meta, path, name, params, query } = to
+ if (!meta.isHideTab) {
+ // 如果是 iframe 页面,则特殊处理工作标签页
+ if (isIframe(path)) {
+ const iframeRoute = IframeRouteManager.getInstance().findByPath(to.path)
+
+ if (iframeRoute?.meta) {
+ worktabStore.openTab({
+ title: iframeRoute.meta.title,
+ icon: meta.icon as string,
+ path,
+ name: name as string,
+ keepAlive: meta.keepAlive as boolean,
+ params,
+ query
+ })
+ }
+ } else if (useSettingStore().showWorkTab || path === useCommon().homePath.value) {
+ worktabStore.openTab({
+ title: meta.title as string,
+ icon: meta.icon as string,
+ path,
+ name: name as string,
+ keepAlive: meta.keepAlive as boolean,
+ params,
+ query,
+ fixedTab: meta.fixedTab as boolean
+ })
+ }
+ }
+}
diff --git a/adminSystem/src/utils/router.ts b/adminSystem/src/utils/router.ts
new file mode 100644
index 0000000..8c838ff
--- /dev/null
+++ b/adminSystem/src/utils/router.ts
@@ -0,0 +1,61 @@
+/**
+ * 路由工具函数
+ *
+ * 提供路由相关的工具函数
+ *
+ * @module utils/router
+ */
+import { RouteLocationNormalized, RouteRecordRaw } from 'vue-router'
+import AppConfig from '@/config'
+import NProgress from 'nprogress'
+import 'nprogress/nprogress.css'
+import i18n, { $t } from '@/locales'
+
+/** 扩展的路由配置类型 */
+export type AppRouteRecordRaw = RouteRecordRaw & {
+ hidden?: boolean
+}
+
+/** 顶部进度条配置 */
+export const configureNProgress = () => {
+ NProgress.configure({
+ easing: 'ease',
+ speed: 600,
+ showSpinner: false,
+ parent: 'body'
+ })
+}
+
+/**
+ * 设置页面标题,根据路由元信息和系统信息拼接标题
+ * @param to 当前路由对象
+ */
+export const setPageTitle = (to: RouteLocationNormalized): void => {
+ const { title } = to.meta
+ if (title) {
+ setTimeout(() => {
+ document.title = `${formatMenuTitle(String(title))} - ${AppConfig.systemInfo.name}`
+ }, 150)
+ }
+}
+
+/**
+ * 格式化菜单标题
+ * @param title 菜单标题,可以是 i18n 的 key,也可以是字符串
+ * @returns 格式化后的菜单标题
+ */
+export const formatMenuTitle = (title: string): string => {
+ if (title) {
+ if (title.startsWith('menus.')) {
+ // 使用 te() 方法检查翻译键值是否存在,避免控制台警告
+ if (i18n.global.te(title)) {
+ return $t(title)
+ } else {
+ // 如果翻译不存在,返回键值的最后部分作为fallback
+ return title.split('.').pop() || title
+ }
+ }
+ return title
+ }
+ return ''
+}
diff --git a/adminSystem/src/utils/socket/index.ts b/adminSystem/src/utils/socket/index.ts
new file mode 100644
index 0000000..77d46ad
--- /dev/null
+++ b/adminSystem/src/utils/socket/index.ts
@@ -0,0 +1,388 @@
+interface WebSocketOptions {
+ url?: string
+ messageHandler: (event: MessageEvent) => void
+ reconnectInterval?: number // 重连间隔(ms)
+ heartbeatInterval?: number // 心跳检测间隔(ms)
+ pingInterval?: number // 发送ping间隔(ms)
+ reconnectTimeout?: number // 重连超时时间(ms)
+ maxReconnectAttempts?: number // 最大重连次数
+ connectionTimeout?: number // 连接建立超时时间(ms)
+}
+
+export default class WebSocketClient {
+ private static instance: WebSocketClient | null = null
+ private ws: WebSocket | null = null
+ private url: string
+ private messageHandler: (event: MessageEvent) => void
+ private reconnectInterval: number
+ private heartbeatInterval: number
+ private pingInterval: number
+ private reconnectTimeout: number
+ private maxReconnectAttempts: number
+ private connectionTimeout: number
+ private reconnectAttempts: number = 0 // 当前重连次数
+
+ // 消息队列 - 缓存连接建立前的消息
+ private messageQueue: Array = []
+
+ // 定时器
+ private detectionTimer: NodeJS.Timeout | null = null
+ private timeoutTimer: NodeJS.Timeout | null = null
+ private reconnectTimer: NodeJS.Timeout | null = null
+ private pingTimer: NodeJS.Timeout | null = null
+ private connectionTimer: NodeJS.Timeout | null = null // 连接超时定时器
+
+ // 状态标识
+ private isConnected: boolean = false
+ private isConnecting: boolean = false // 是否正在连接中
+ private stopReconnect: boolean = false
+
+ private constructor(options: WebSocketOptions) {
+ this.url = options.url || (process.env.VUE_APP_LOGIN_WEBSOCKET as string)
+ this.messageHandler = options.messageHandler
+ this.reconnectInterval = options.reconnectInterval || 20 * 1000 // 默认20秒
+ this.heartbeatInterval = options.heartbeatInterval || 5 * 1000 // 默认5秒
+ this.pingInterval = options.pingInterval || 10 * 1000 // 默认10秒
+ this.reconnectTimeout = options.reconnectTimeout || 30 * 1000 // 默认30秒
+ this.maxReconnectAttempts = options.maxReconnectAttempts || 10 // 默认最多重连10次
+ this.connectionTimeout = options.connectionTimeout || 10 * 1000 // 连接超时10秒
+ }
+
+ // 单例模式获取实例
+ static getInstance(options: WebSocketOptions): WebSocketClient {
+ if (!WebSocketClient.instance) {
+ WebSocketClient.instance = new WebSocketClient(options)
+ } else {
+ // 更新消息处理器
+ WebSocketClient.instance.messageHandler = options.messageHandler
+ // 如果提供了新的URL,则更新并重新连接
+ if (options.url && WebSocketClient.instance.url !== options.url) {
+ WebSocketClient.instance.url = options.url
+ WebSocketClient.instance.reconnectAttempts = 0
+ WebSocketClient.instance.init()
+ }
+ }
+ return WebSocketClient.instance
+ }
+
+ // 初始化连接
+ init(): void {
+ // 如果正在连接中,不重复连接
+ if (this.isConnecting) {
+ console.log('正在建立WebSocket连接中...')
+ return
+ }
+
+ // 如果已连接,不重复连接
+ if (this.ws?.readyState === WebSocket.OPEN) {
+ console.warn('WebSocket连接已存在')
+ this.flushMessageQueue() // 确保队列中的消息被发送
+ return
+ }
+
+ try {
+ this.isConnecting = true
+ this.reconnectAttempts = 0 // 重置重连次数
+ this.ws = new WebSocket(this.url)
+
+ // 设置连接超时检测
+ this.clearTimer('connectionTimer')
+ this.connectionTimer = setTimeout(() => {
+ console.error(`WebSocket连接超时 (${this.connectionTimeout}ms):${this.url}`)
+ this.handleConnectionTimeout()
+ }, this.connectionTimeout)
+
+ this.ws.onopen = (event) => this.handleOpen(event)
+ this.ws.onmessage = (event) => this.handleMessage(event)
+ this.ws.onclose = (event) => this.handleClose(event)
+ this.ws.onerror = (event) => this.handleError(event)
+ } catch (error) {
+ console.error('WebSocket初始化失败:', error)
+ this.isConnecting = false
+ this.reconnect()
+ }
+ }
+
+ // 处理连接超时
+ private handleConnectionTimeout(): void {
+ if (this.ws?.readyState !== WebSocket.OPEN) {
+ console.error('WebSocket连接超时,强制关闭连接')
+ this.ws?.close(1000, 'Connection timeout')
+ this.isConnecting = false
+ this.reconnect()
+ }
+ }
+
+ // 关闭连接
+ close(force?: boolean): void {
+ this.clearAllTimers()
+ this.stopReconnect = true
+ this.isConnecting = false
+
+ if (this.ws) {
+ // 1000 表示正常关闭
+ this.ws.close(force ? 1001 : 1000, force ? 'Force closed' : 'Normal close')
+ this.ws = null
+ }
+
+ this.isConnected = false
+ }
+
+ // 发送消息 - 增加消息队列
+ send(data: string | ArrayBufferLike | Blob | ArrayBufferView, immediate: boolean = false): void {
+ // 如果要求立即发送且未连接,则直接报错
+ if (immediate && (!this.ws || this.ws.readyState !== WebSocket.OPEN)) {
+ console.error('WebSocket未连接,无法立即发送消息')
+ return
+ }
+
+ // 如果未连接且不要求立即发送,则加入消息队列
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
+ console.log('WebSocket未连接,消息已加入队列等待发送')
+ this.messageQueue.push(data)
+ // 如果未在重连中,则尝试重连
+ if (!this.isConnecting && !this.stopReconnect) {
+ this.init()
+ }
+ return
+ }
+
+ try {
+ this.ws.send(data)
+ } catch (error) {
+ console.error('WebSocket发送消息失败:', error)
+ // 发送失败时将消息加入队列,等待重连后重试
+ this.messageQueue.push(data)
+ this.reconnect()
+ }
+ }
+
+ // 发送队列中的消息
+ private flushMessageQueue(): void {
+ if (this.messageQueue.length > 0 && this.ws?.readyState === WebSocket.OPEN) {
+ console.log(`发送队列中的${this.messageQueue.length}条消息`)
+ while (this.messageQueue.length > 0) {
+ const data = this.messageQueue.shift()
+ if (data) {
+ try {
+ this.ws?.send(data)
+ } catch (error) {
+ console.error('发送队列消息失败:', error)
+ // 如果发送失败,将消息放回队列头部
+ if (data) this.messageQueue.unshift(data)
+ break
+ }
+ }
+ }
+ }
+ }
+
+ // 处理连接打开
+ private handleOpen(event: Event): void {
+ console.log('WebSocket连接成功', event)
+ this.clearTimer('connectionTimer') // 清除连接超时定时器
+ this.isConnected = true
+ this.isConnecting = false
+ this.stopReconnect = false
+ this.reconnectAttempts = 0 // 重置重连次数
+ this.startHeartbeat()
+ this.startPing()
+ this.flushMessageQueue() // 发送队列中的消息
+ }
+
+ // 处理收到的消息
+ private handleMessage(event: MessageEvent): void {
+ console.log('收到WebSocket消息:', event)
+ this.resetHeartbeat()
+ this.messageHandler(event)
+ }
+
+ // 处理连接关闭
+ private handleClose(event: CloseEvent): void {
+ console.log(
+ `WebSocket断开: 代码=${event.code}, 原因=${event.reason}, 干净关闭=${event.wasClean}`
+ )
+
+ // 1000 是正常关闭代码
+ const isNormalClose = event.code === 1000
+
+ this.isConnected = false
+ this.isConnecting = false
+ this.clearAllTimers()
+
+ if (!this.stopReconnect && !isNormalClose) {
+ this.reconnect()
+ }
+ }
+
+ // 处理错误 - 增加详细错误信息
+ private handleError(event: Event): void {
+ console.error('WebSocket连接错误:')
+ console.error('错误事件:', event)
+ console.error(
+ '当前连接状态:',
+ this.ws?.readyState ? this.getReadyStateText(this.ws.readyState) : '未初始化'
+ )
+
+ this.isConnected = false
+ this.isConnecting = false
+
+ // 只有在未停止重连的情况下才尝试重连
+ if (!this.stopReconnect) {
+ this.reconnect()
+ }
+ }
+
+ // 转换连接状态为文本描述
+ private getReadyStateText(state: number): string {
+ switch (state) {
+ case WebSocket.CONNECTING:
+ return 'CONNECTING (0) - 正在连接'
+ case WebSocket.OPEN:
+ return 'OPEN (1) - 已连接'
+ case WebSocket.CLOSING:
+ return 'CLOSING (2) - 正在关闭'
+ case WebSocket.CLOSED:
+ return 'CLOSED (3) - 已关闭'
+ default:
+ return `未知状态 (${state})`
+ }
+ }
+
+ // 开始心跳检测
+ private startHeartbeat(): void {
+ this.clearTimer('detectionTimer')
+ this.clearTimer('timeoutTimer')
+
+ this.detectionTimer = setTimeout(() => {
+ this.isConnected = this.ws?.readyState === WebSocket.OPEN
+
+ if (!this.isConnected) {
+ console.warn('WebSocket心跳检测失败,尝试重连')
+ this.reconnect()
+
+ this.timeoutTimer = setTimeout(() => {
+ console.warn('WebSocket重连超时')
+ this.close()
+ }, this.reconnectTimeout)
+ }
+ }, this.heartbeatInterval)
+ }
+
+ // 重置心跳检测
+ private resetHeartbeat(): void {
+ this.clearTimer('detectionTimer')
+ this.clearTimer('timeoutTimer')
+ this.startHeartbeat()
+ }
+
+ // 开始发送ping消息
+ private startPing(): void {
+ this.clearTimer('pingTimer')
+
+ this.pingTimer = setInterval(() => {
+ if (this.ws?.readyState !== WebSocket.OPEN) {
+ console.warn('WebSocket未连接,停止发送ping')
+ this.clearTimer('pingTimer')
+ this.reconnect()
+ return
+ }
+
+ try {
+ this.ws.send('ping')
+ console.log('发送ping消息')
+ } catch (error) {
+ console.error('发送ping消息失败:', error)
+ this.clearTimer('pingTimer')
+ this.reconnect()
+ }
+ }, this.pingInterval)
+ }
+
+ // 重连 - 增加重连次数限制
+ private reconnect(): void {
+ if (this.stopReconnect || this.isConnecting) {
+ return
+ }
+
+ // 检查是否超过最大重连次数
+ if (this.reconnectAttempts >= this.maxReconnectAttempts) {
+ console.error(`已达到最大重连次数(${this.maxReconnectAttempts}),停止重连`)
+ this.close(true)
+ return
+ }
+
+ this.reconnectAttempts++
+ this.stopReconnect = true
+ this.close(true)
+
+ const delay = this.calculateReconnectDelay()
+ console.log(
+ `将在${delay / 1000}秒后尝试重新连接(第${this.reconnectAttempts}/${this.maxReconnectAttempts}次)`
+ )
+
+ this.clearTimer('reconnectTimer')
+ this.reconnectTimer = setTimeout(() => {
+ console.log(`尝试重新连接WebSocket(第${this.reconnectAttempts}次)`)
+ this.init()
+ this.stopReconnect = false
+ }, delay)
+ }
+
+ // 计算重连延迟 - 指数退避策略
+ private calculateReconnectDelay(): number {
+ // 基础延迟 + 随机值,避免多个客户端同时重连
+ const jitter = Math.random() * 1000 // 0-1秒的随机延迟
+ const baseDelay = Math.min(
+ this.reconnectInterval * Math.pow(1.5, this.reconnectAttempts - 1),
+ this.reconnectInterval * 5
+ )
+ return baseDelay + jitter
+ }
+
+ // 清除指定定时器
+ private clearTimer(
+ timerName:
+ | 'detectionTimer'
+ | 'timeoutTimer'
+ | 'reconnectTimer'
+ | 'pingTimer'
+ | 'connectionTimer'
+ ): void {
+ if (this[timerName]) {
+ clearTimeout(this[timerName] as NodeJS.Timeout)
+ this[timerName] = null
+ }
+ }
+
+ // 清除所有定时器
+ private clearAllTimers(): void {
+ this.clearTimer('detectionTimer')
+ this.clearTimer('timeoutTimer')
+ this.clearTimer('reconnectTimer')
+ this.clearTimer('pingTimer')
+ this.clearTimer('connectionTimer')
+ }
+
+ // 获取当前连接状态
+ get isWebSocketConnected(): boolean {
+ return this.isConnected
+ }
+
+ // 获取当前连接状态文本
+ get connectionStatusText(): string {
+ if (this.isConnecting) return '正在连接'
+ if (this.isConnected) return '已连接'
+ if (this.reconnectAttempts > 0 && !this.stopReconnect)
+ return `重连中(${this.reconnectAttempts}/${this.maxReconnectAttempts})`
+ return '已断开'
+ }
+
+ // 销毁实例
+ static destroyInstance(): void {
+ if (WebSocketClient.instance) {
+ WebSocketClient.instance.close()
+ WebSocketClient.instance = null
+ }
+ }
+}
diff --git a/adminSystem/src/utils/storage/index.ts b/adminSystem/src/utils/storage/index.ts
new file mode 100644
index 0000000..a4366f0
--- /dev/null
+++ b/adminSystem/src/utils/storage/index.ts
@@ -0,0 +1,7 @@
+/**
+ * 存储相关工具函数统一导出
+ */
+
+export * from './storage'
+export * from './storage-config'
+export * from './storage-key-manager'
diff --git a/adminSystem/src/utils/storage/storage-config.ts b/adminSystem/src/utils/storage/storage-config.ts
new file mode 100644
index 0000000..c41b60b
--- /dev/null
+++ b/adminSystem/src/utils/storage/storage-config.ts
@@ -0,0 +1,122 @@
+/**
+ * 存储配置管理模块
+ *
+ * 提供统一的本地存储配置和工具方法
+ *
+ * ## 主要功能
+ *
+ * - 版本化存储键管理,支持多版本数据隔离
+ * - 存储键名生成和解析(带版本前缀)
+ * - 版本号提取和验证
+ * - 存储键匹配的正则表达式生成
+ * - 旧版本存储键兼容处理
+ * - 升级和登出延迟配置
+ * - 主题存储键配置
+ *
+ * ## 使用场景
+ *
+ * - Pinia Store 持久化存储
+ * - 应用版本升级时的数据迁移
+ * - 多版本数据清理
+ * - 存储键的统一管理和规范
+ *
+ * 存储键格式:sys-v{version}-{storeId}
+ * 例如:sys-v1.0.0-user, sys-v1.0.0-setting
+ *
+ * @module utils/storage/storage-config
+ * @author Art Design Pro Team
+ */
+export class StorageConfig {
+ /** 当前应用版本 */
+ static readonly CURRENT_VERSION = __APP_VERSION__
+
+ /** 存储键前缀 */
+ static readonly STORAGE_PREFIX = 'sys-v'
+
+ /** 版本键名 */
+ static readonly VERSION_KEY = 'sys-version'
+
+ /** 主题键名(index.html中使用了,如果修改,需要同步修改) */
+ static readonly THEME_KEY = 'sys-theme'
+
+ /** 上次登录用户ID键名(用于判断是否为同一用户登录) */
+ static readonly LAST_USER_ID_KEY = 'sys-last-user-id'
+
+ /** 跳过升级检查的版本 */
+ static readonly SKIP_UPGRADE_VERSION = '1.0.0'
+
+ /** 升级处理延迟时间(毫秒) */
+ static readonly UPGRADE_DELAY = 1000
+
+ /** 登出延迟时间(毫秒) */
+ static readonly LOGOUT_DELAY = 1000
+
+ /**
+ * 生成版本化的存储键名
+ * @param storeId 存储ID
+ * @param version 版本号,默认使用当前版本
+ */
+ static generateStorageKey(storeId: string, version: string = this.CURRENT_VERSION): string {
+ return `${this.STORAGE_PREFIX}${version}-${storeId}`
+ }
+
+ /**
+ * 生成旧版本的存储键名(不带分隔符)
+ * @param version 版本号,默认使用当前版本
+ */
+ static generateLegacyKey(version: string = this.CURRENT_VERSION): string {
+ return `${this.STORAGE_PREFIX}${version}`
+ }
+
+ /**
+ * 创建存储键匹配的正则表达式
+ * @param storeId 存储ID
+ */
+ static createKeyPattern(storeId: string): RegExp {
+ return new RegExp(`^${this.STORAGE_PREFIX}[^-]+-${storeId}$`)
+ }
+
+ /**
+ * 创建当前版本存储键匹配的正则表达式
+ */
+ static createCurrentVersionPattern(): RegExp {
+ return new RegExp(`^${this.STORAGE_PREFIX}${this.CURRENT_VERSION}-`)
+ }
+
+ /**
+ * 创建任意版本存储键匹配的正则表达式
+ */
+ static createVersionPattern(): RegExp {
+ return new RegExp(`^${this.STORAGE_PREFIX}`)
+ }
+
+ /**
+ * 检查是否为当前版本的键
+ */
+ static isCurrentVersionKey(key: string): boolean {
+ return key.startsWith(`${this.STORAGE_PREFIX}${this.CURRENT_VERSION}`)
+ }
+
+ /**
+ * 检查是否为版本化的键
+ */
+ static isVersionedKey(key: string): boolean {
+ return key.startsWith(this.STORAGE_PREFIX)
+ }
+
+ /**
+ * 从存储键中提取版本号
+ */
+ static extractVersionFromKey(key: string): string | null {
+ const match = key.match(new RegExp(`^${this.STORAGE_PREFIX}([^-]+)`))
+ return match ? match[1] : null
+ }
+
+ /**
+ * 从存储键中提取存储ID
+ */
+ static extractStoreIdFromKey(key: string): string | null {
+ const match = key.match(new RegExp(`^${this.STORAGE_PREFIX}[^-]+-(.+)$`))
+ return match ? match[1] : null
+ }
+}
diff --git a/adminSystem/src/utils/storage/storage-key-manager.ts b/adminSystem/src/utils/storage/storage-key-manager.ts
new file mode 100644
index 0000000..ba14f65
--- /dev/null
+++ b/adminSystem/src/utils/storage/storage-key-manager.ts
@@ -0,0 +1,97 @@
+/**
+ * 存储键名管理器模块
+ *
+ * 提供智能的版本化存储键管理和数据迁移功能
+ *
+ * ## 主要功能
+ *
+ * - 自动生成当前版本的存储键名
+ * - 检测当前版本数据是否存在
+ * - 查找其他版本的同名存储数据
+ * - 自动将旧版本数据迁移到当前版本
+ * - 数据迁移日志记录
+ * - 迁移失败的错误处理
+ *
+ * ## 使用场景
+ *
+ * - Pinia Store 持久化插件中获取存储键
+ * - 应用版本升级时自动迁移用户数据
+ * - 避免版本升级导致的数据丢失
+ * - 实现平滑的版本过渡
+ *
+ * ## 工作流程
+ *
+ * 1. 优先使用当前版本的存储键
+ * 2. 如果当前版本无数据,查找其他版本的同名数据
+ * 3. 找到旧版本数据后自动迁移到当前版本
+ * 4. 返回当前版本的存储键供使用
+ *
+ * @module utils/storage/storage-key-manager
+ * @author Art Design Pro Team
+ */
+import { StorageConfig } from '@/utils/storage'
+
+/**
+ * 存储键名管理器
+ * 负责处理版本化的存储键名生成和数据迁移
+ */
+export class StorageKeyManager {
+ /**
+ * 获取当前版本的存储键名
+ */
+ private getCurrentVersionKey(storeId: string): string {
+ return StorageConfig.generateStorageKey(storeId)
+ }
+
+ /**
+ * 检查当前版本的数据是否存在
+ */
+ private hasCurrentVersionData(key: string): boolean {
+ return localStorage.getItem(key) !== null
+ }
+
+ /**
+ * 查找其他版本的同名存储键
+ */
+ private findExistingKey(storeId: string): string | null {
+ const storageKeys = Object.keys(localStorage)
+ const pattern = StorageConfig.createKeyPattern(storeId)
+
+ return storageKeys.find((key) => pattern.test(key) && localStorage.getItem(key)) || null
+ }
+
+ /**
+ * 将数据从旧版本迁移到当前版本
+ */
+ private migrateData(fromKey: string, toKey: string): void {
+ try {
+ const existingData = localStorage.getItem(fromKey)
+ if (existingData) {
+ localStorage.setItem(toKey, existingData)
+ console.info(`[Storage] 已迁移数据: ${fromKey} → ${toKey}`)
+ }
+ } catch (error) {
+ console.warn(`[Storage] 数据迁移失败: ${fromKey}`, error)
+ }
+ }
+
+ /**
+ * 获取持久化存储的键名(支持自动数据迁移)
+ */
+ getStorageKey(storeId: string): string {
+ const currentKey = this.getCurrentVersionKey(storeId)
+
+ // 优先使用当前版本的数据
+ if (this.hasCurrentVersionData(currentKey)) {
+ return currentKey
+ }
+
+ // 查找并迁移其他版本的数据
+ const existingKey = this.findExistingKey(storeId)
+ if (existingKey) {
+ this.migrateData(existingKey, currentKey)
+ }
+
+ return currentKey
+ }
+}
diff --git a/adminSystem/src/utils/storage/storage.ts b/adminSystem/src/utils/storage/storage.ts
new file mode 100644
index 0000000..67b9e9e
--- /dev/null
+++ b/adminSystem/src/utils/storage/storage.ts
@@ -0,0 +1,250 @@
+/**
+ * 存储兼容性管理模块
+ *
+ * 提供完整的本地存储兼容性检查和数据验证功能
+ *
+ * 主要功能
+ *
+ * - 多版本存储数据检测和验证
+ * - 新旧存储格式兼容处理
+ * - 存储数据完整性校验
+ * - 存储异常自动恢复(清理+登出)
+ * - 登录状态验证
+ * - 存储为空检测
+ * - 版本号管理
+ *
+ * ## 使用场景
+ *
+ * - 应用启动时检查存储数据有效性
+ * - 路由守卫中验证登录状态
+ * - 版本升级时的数据兼容性检查
+ * - 存储异常时的自动恢复
+ * - 防止因存储数据损坏导致的系统异常
+ *
+ * ## 工作流程
+ *
+ * 1. 优先检查当前版本的存储数据
+ * 2. 检查其他版本的存储数据
+ * 3. 兼容旧格式的存储数据
+ * 4. 验证数据完整性
+ * 5. 异常时提示用户并执行登出
+ *
+ * @module utils/storage/storage
+ * @author Art Design Pro Team
+ */
+import { router } from '@/router'
+import { useUserStore } from '@/store/modules/user'
+import { StorageConfig } from '@/utils/storage/storage-config'
+
+/**
+ * 存储兼容性管理器
+ * 负责处理不同版本间的存储兼容性检查和数据验证
+ */
+class StorageCompatibilityManager {
+ /**
+ * 获取系统版本号
+ */
+ getSystemVersion(): string | null {
+ return localStorage.getItem(StorageConfig.VERSION_KEY)
+ }
+
+ /**
+ * 获取系统存储数据(兼容旧格式)
+ */
+ getSystemStorage(): any {
+ const version = this.getSystemVersion() || StorageConfig.CURRENT_VERSION
+ const legacyKey = StorageConfig.generateLegacyKey(version)
+ const data = localStorage.getItem(legacyKey)
+ return data ? JSON.parse(data) : null
+ }
+
+ /**
+ * 检查当前版本是否有存储数据
+ */
+ private hasCurrentVersionStorage(): boolean {
+ const storageKeys = Object.keys(localStorage)
+ const currentVersionPattern = StorageConfig.createCurrentVersionPattern()
+
+ return storageKeys.some(
+ (key) => currentVersionPattern.test(key) && localStorage.getItem(key) !== null
+ )
+ }
+
+ /**
+ * 检查是否存在任何版本的存储数据
+ */
+ private hasAnyVersionStorage(): boolean {
+ const storageKeys = Object.keys(localStorage)
+ const versionPattern = StorageConfig.createVersionPattern()
+
+ return storageKeys.some((key) => versionPattern.test(key) && localStorage.getItem(key) !== null)
+ }
+
+ /**
+ * 获取旧格式的本地存储数据
+ */
+ private getLegacyStorageData(): Record {
+ try {
+ const systemStorage = this.getSystemStorage()
+ return systemStorage || {}
+ } catch (error) {
+ console.warn('[Storage] 解析旧格式存储数据失败:', error)
+ return {}
+ }
+ }
+
+ /**
+ * 显示存储错误消息
+ */
+ private showStorageError(): void {
+ ElMessage({
+ type: 'error',
+ offset: 40,
+ duration: 5000,
+ message: '系统检测到本地数据异常,请重新登录系统恢复使用!'
+ })
+ }
+
+ /**
+ * 执行系统登出
+ */
+ private performSystemLogout(): void {
+ setTimeout(() => {
+ try {
+ localStorage.clear()
+ useUserStore().logOut()
+ router.push({ name: 'Login' })
+ console.info('[Storage] 已执行系统登出')
+ } catch (error) {
+ console.error('[Storage] 系统登出失败:', error)
+ }
+ }, StorageConfig.LOGOUT_DELAY)
+ }
+
+ /**
+ * 处理存储异常
+ */
+ private handleStorageError(): void {
+ this.showStorageError()
+ this.performSystemLogout()
+ }
+
+ /**
+ * 验证存储数据完整性
+ * @param requireAuth 是否需要验证登录状态(默认 false)
+ */
+ validateStorageData(requireAuth: boolean = false): boolean {
+ try {
+ // 优先检查新版本存储结构
+ if (this.hasCurrentVersionStorage()) {
+ // console.debug('[Storage] 发现当前版本存储数据')
+ return true
+ }
+
+ // 检查是否有任何版本的存储数据
+ if (this.hasAnyVersionStorage()) {
+ // console.debug('[Storage] 发现其他版本存储数据,可能需要迁移')
+ return true
+ }
+
+ // 检查旧版本存储结构
+ const legacyData = this.getLegacyStorageData()
+ if (Object.keys(legacyData).length === 0) {
+ // 只有在需要验证登录状态时才执行登出操作
+ if (requireAuth) {
+ console.warn('[Storage] 未发现任何存储数据,需要重新登录')
+ this.performSystemLogout()
+ return false
+ }
+ // 首次访问或访问静态路由,不需要登出
+ // console.debug('[Storage] 未发现存储数据,首次访问或访问静态路由')
+ return true
+ }
+
+ console.debug('[Storage] 发现旧版本存储数据')
+ return true
+ } catch (error) {
+ console.error('[Storage] 存储数据验证失败:', error)
+ // 只有在需要验证登录状态时才处理错误
+ if (requireAuth) {
+ this.handleStorageError()
+ return false
+ }
+ return true
+ }
+ }
+
+ /**
+ * 检查存储是否为空
+ */
+ isStorageEmpty(): boolean {
+ // 检查新版本存储结构
+ if (this.hasCurrentVersionStorage()) {
+ return false
+ }
+
+ // 检查是否有任何版本的存储数据
+ if (this.hasAnyVersionStorage()) {
+ return false
+ }
+
+ // 检查旧版本存储结构
+ const legacyData = this.getLegacyStorageData()
+ return Object.keys(legacyData).length === 0
+ }
+
+ /**
+ * 检查存储兼容性
+ * @param requireAuth 是否需要验证登录状态(默认 false)
+ */
+ checkCompatibility(requireAuth: boolean = false): boolean {
+ try {
+ const isValid = this.validateStorageData(requireAuth)
+ const isEmpty = this.isStorageEmpty()
+
+ if (isValid || isEmpty) {
+ // console.debug('[Storage] 存储兼容性检查通过')
+ return true
+ }
+
+ console.warn('[Storage] 存储兼容性检查失败')
+ return false
+ } catch (error) {
+ console.error('[Storage] 兼容性检查异常:', error)
+ return false
+ }
+ }
+}
+
+// 创建存储兼容性管理器实例
+const storageManager = new StorageCompatibilityManager()
+
+/**
+ * 获取系统存储数据
+ */
+export function getSystemStorage(): any {
+ return storageManager.getSystemStorage()
+}
+
+/**
+ * 获取系统版本号
+ */
+export function getSysVersion(): string | null {
+ return storageManager.getSystemVersion()
+}
+
+/**
+ * 验证本地存储数据
+ * @param requireAuth 是否需要验证登录状态(默认 false)
+ */
+export function validateStorageData(requireAuth: boolean = false): boolean {
+ return storageManager.validateStorageData(requireAuth)
+}
+
+/**
+ * 检查存储兼容性
+ * @param requireAuth 是否需要验证登录状态(默认 false)
+ */
+export function checkStorageCompatibility(requireAuth: boolean = false): boolean {
+ return storageManager.checkCompatibility(requireAuth)
+}
diff --git a/adminSystem/src/utils/sys/console.ts b/adminSystem/src/utils/sys/console.ts
new file mode 100644
index 0000000..1f89058
--- /dev/null
+++ b/adminSystem/src/utils/sys/console.ts
@@ -0,0 +1,13 @@
+// ANSI 转义码生成网站 https://patorjk.com/software/taag/#p=display&f=Big&t=ABB%0A
+const asciiArt = `
+\x1b[32m欢迎使用 Art Design Pro!
+\x1b[0m
+\x1b[36m哇!你居然在用我的项目~ 好用的话别忘了去 GitHub 点个 ★Star 呀,你的支持就是我更新的超强动力!祝使用体验满分💯
+\x1b[0m
+\x1b[33mGitHub: https://github.com/Daymychen/art-design-pro
+\x1b[0m
+\x1b[31m技术支持(QQ群): 821834289,和开发者一起交流~ 群里有小伙伴实时答疑,遇到问题不用慌!
+\x1b[0m
+`
+
+console.log(asciiArt)
diff --git a/adminSystem/src/utils/sys/error-handle.ts b/adminSystem/src/utils/sys/error-handle.ts
new file mode 100644
index 0000000..22109c2
--- /dev/null
+++ b/adminSystem/src/utils/sys/error-handle.ts
@@ -0,0 +1,102 @@
+/**
+ * 全局错误处理模块
+ *
+ * 提供统一的错误捕获和处理机制
+ *
+ * ## 主要功能
+ *
+ * - Vue 运行时错误捕获(组件错误、生命周期错误等)
+ * - 全局脚本错误捕获(语法错误、运行时错误等)
+ * - Promise 未捕获错误处理(unhandledrejection)
+ * - 静态资源加载错误监控(图片、脚本、样式等)
+ * - 错误日志记录和上报
+ * - 统一的错误处理入口
+ *
+ * ## 使用场景
+ * - 应用启动时安装全局错误处理器
+ * - 捕获和记录所有类型的错误
+ * - 错误上报到监控平台
+ * - 提升应用稳定性和可维护性
+ * - 问题排查和调试
+ *
+ * ## 错误类型
+ *
+ * - VueError: Vue 组件相关错误
+ * - ScriptError: JavaScript 脚本错误
+ * - PromiseError: Promise 未捕获的 rejection
+ * - ResourceError: 静态资源加载失败
+ *
+ * @module utils/sys/error-handle
+ * @author Art Design Pro Team
+ */
+import type { App } from 'vue'
+
+/**
+ * Vue 运行时错误处理
+ */
+export function vueErrorHandler(err: unknown, instance: any, info: string) {
+ console.error('[VueError]', err, info, instance)
+ // 这里可以上报到服务端,比如:
+ // reportError({ type: 'vue', err, info })
+}
+
+/**
+ * 全局脚本错误处理
+ */
+export function scriptErrorHandler(
+ message: Event | string,
+ source?: string,
+ lineno?: number,
+ colno?: number,
+ error?: Error
+): boolean {
+ console.error('[ScriptError]', { message, source, lineno, colno, error })
+ // reportError({ type: 'script', message, source, lineno, colno, error })
+ return true // 阻止默认控制台报错,可根据需求改
+}
+
+/**
+ * Promise 未捕获错误处理
+ */
+export function registerPromiseErrorHandler() {
+ window.addEventListener('unhandledrejection', (event) => {
+ console.error('[PromiseError]', event.reason)
+ // reportError({ type: 'promise', reason: event.reason })
+ })
+}
+
+/**
+ * 资源加载错误处理 (img, script, css...)
+ */
+export function registerResourceErrorHandler() {
+ window.addEventListener(
+ 'error',
+ (event: Event) => {
+ const target = event.target as HTMLElement
+ if (
+ target &&
+ (target.tagName === 'IMG' || target.tagName === 'SCRIPT' || target.tagName === 'LINK')
+ ) {
+ console.error('[ResourceError]', {
+ tagName: target.tagName,
+ src:
+ (target as HTMLImageElement).src ||
+ (target as HTMLScriptElement).src ||
+ (target as HTMLLinkElement).href
+ })
+ // reportError({ type: 'resource', target })
+ }
+ },
+ true // 捕获阶段才能监听到资源错误
+ )
+}
+
+/**
+ * 安装统一错误处理
+ */
+export function setupErrorHandle(app: App) {
+ app.config.errorHandler = vueErrorHandler
+ window.onerror = scriptErrorHandler
+ registerPromiseErrorHandler()
+ registerResourceErrorHandler()
+}
diff --git a/adminSystem/src/utils/sys/index.ts b/adminSystem/src/utils/sys/index.ts
new file mode 100644
index 0000000..a2e0729
--- /dev/null
+++ b/adminSystem/src/utils/sys/index.ts
@@ -0,0 +1,6 @@
+/**
+ * 系统管理相关工具函数统一导出
+ */
+
+export * from './upgrade'
+export { default as mittBus } from './mittBus'
diff --git a/adminSystem/src/utils/sys/mittBus.ts b/adminSystem/src/utils/sys/mittBus.ts
new file mode 100644
index 0000000..22f0108
--- /dev/null
+++ b/adminSystem/src/utils/sys/mittBus.ts
@@ -0,0 +1,63 @@
+/**
+ * 全局事件总线模块
+ *
+ * 基于 mitt 库实现的类型安全的事件总线
+ *
+ * ## 主要功能
+ *
+ * - 跨组件通信(发布/订阅模式)
+ * - 类型安全的事件定义和调用
+ * - 全局事件管理(烟花效果、设置面板、搜索对话框等)
+ * - 解耦组件间的直接依赖
+ *
+ * ## 使用场景
+ *
+ * - 跨层级组件通信
+ * - 全局功能触发(设置、搜索、聊天、锁屏等)
+ * - 特效触发(烟花效果)
+ * - 避免 props 层层传递
+ *
+ * ## 用法示例
+ *
+ * ```typescript
+ * // 订阅事件
+ * mittBus.on('openSetting', () => { ... })
+ *
+ * // 发布事件
+ * mittBus.emit('openSetting')
+ *
+ * // 带参数的事件
+ * mittBus.emit('triggerFireworks', 'image-url')
+ * ```
+ *
+ * ## 已定义的事件
+ *
+ * - triggerFireworks: 触发烟花效果(可选图片URL)
+ * - openSetting: 打开设置面板
+ * - openSearchDialog: 打开搜索对话框
+ * - openChat: 打开聊天窗口
+ * - openLockScreen: 打开锁屏
+ *
+ * @module utils/sys/mittBus
+ * @author Art Design Pro Team
+ */
+import mitt, { type Emitter } from 'mitt'
+
+// 定义事件类型映射
+type Events = {
+ // 烟花效果事件 - 可选的图片URL参数
+ triggerFireworks: string | undefined
+ // 打开设置面板事件 - 无参数
+ openSetting: void
+ // 打开搜索对话框事件 - 无参数
+ openSearchDialog: void
+ // 打开聊天窗口事件 - 无参数
+ openChat: void
+ // 打开锁屏事件 - 无参数
+ openLockScreen: void
+}
+
+// 创建类型安全的事件总线实例
+const mittBus: Emitter = mitt()
+
+export default mittBus
diff --git a/adminSystem/src/utils/sys/upgrade.ts b/adminSystem/src/utils/sys/upgrade.ts
new file mode 100644
index 0000000..53d3465
--- /dev/null
+++ b/adminSystem/src/utils/sys/upgrade.ts
@@ -0,0 +1,277 @@
+/**
+ * 系统版本升级管理模块
+ *
+ * 提供完整的应用版本升级检测和处理功能
+ *
+ * ## 主要功能
+ *
+ * - 版本号比较和升级检测
+ * - 首次访问识别和处理
+ * - 旧版本数据自动清理
+ * - 升级日志展示和通知
+ * - 强制重新登录控制(根据升级日志配置)
+ * - 版本号规范化处理
+ * - 旧存储结构迁移和清理
+ * - 升级流程延迟执行(确保应用完全加载)
+ *
+ * ## 使用场景
+ *
+ * - 应用启动时自动检测版本升级
+ * - 版本更新后清理旧数据
+ * - 向用户展示版本更新内容
+ * - 重大更新时要求用户重新登录
+ * - 防止旧版本数据污染新版本
+ *
+ * ## 工作流程
+ *
+ * 1. 检查本地存储的版本号
+ * 2. 与当前应用版本对比
+ * 3. 查找并清理旧版本数据
+ * 4. 展示升级通知(包含更新日志)
+ * 5. 根据配置决定是否强制重新登录
+ * 6. 更新本地版本号
+ *
+ * @module utils/sys/upgrade
+ * @author Art Design Pro Team
+ */
+import { upgradeLogList } from '@/mock/upgrade/changeLog'
+import { ElNotification } from 'element-plus'
+import { useUserStore } from '@/store/modules/user'
+import { StorageConfig } from '@/utils/storage/storage-config'
+
+/**
+ * 版本管理器
+ * 负责处理版本比较、升级检测和数据清理
+ */
+class VersionManager {
+ /**
+ * 规范化版本号字符串,移除前缀 'v'
+ */
+ private normalizeVersion(version: string): string {
+ return version.replace(/^v/, '')
+ }
+
+ /**
+ * 获取存储的版本号
+ */
+ private getStoredVersion(): string | null {
+ return localStorage.getItem(StorageConfig.VERSION_KEY)
+ }
+
+ /**
+ * 设置版本号到存储
+ */
+ private setStoredVersion(version: string): void {
+ localStorage.setItem(StorageConfig.VERSION_KEY, version)
+ }
+
+ /**
+ * 检查是否应该跳过升级处理
+ */
+ private shouldSkipUpgrade(): boolean {
+ return StorageConfig.CURRENT_VERSION === StorageConfig.SKIP_UPGRADE_VERSION
+ }
+
+ /**
+ * 检查是否为首次访问
+ */
+ private isFirstVisit(storedVersion: string | null): boolean {
+ return !storedVersion
+ }
+
+ /**
+ * 检查版本是否相同
+ */
+ private isSameVersion(storedVersion: string): boolean {
+ return storedVersion === StorageConfig.CURRENT_VERSION
+ }
+
+ /**
+ * 查找旧的存储结构
+ */
+ private findLegacyStorage(): { oldSysKey: string | null; oldVersionKeys: string[] } {
+ const storageKeys = Object.keys(localStorage)
+ const currentVersionPrefix = StorageConfig.generateStorageKey('').slice(0, -1) // 移除末尾的 '-'
+
+ // 查找旧的单一存储结构
+ const oldSysKey =
+ storageKeys.find(
+ (key) =>
+ StorageConfig.isVersionedKey(key) && key !== currentVersionPrefix && !key.includes('-')
+ ) || null
+
+ // 查找旧版本的分离存储键
+ const oldVersionKeys = storageKeys.filter(
+ (key) =>
+ StorageConfig.isVersionedKey(key) &&
+ !StorageConfig.isCurrentVersionKey(key) &&
+ key.includes('-')
+ )
+
+ return { oldSysKey, oldVersionKeys }
+ }
+
+ /**
+ * 检查是否需要重新登录
+ */
+ private shouldRequireReLogin(storedVersion: string): boolean {
+ const normalizedCurrent = this.normalizeVersion(StorageConfig.CURRENT_VERSION)
+ const normalizedStored = this.normalizeVersion(storedVersion)
+
+ return upgradeLogList.value.some((item) => {
+ const itemVersion = this.normalizeVersion(item.version)
+ return (
+ item.requireReLogin && itemVersion > normalizedStored && itemVersion <= normalizedCurrent
+ )
+ })
+ }
+
+ /**
+ * 构建升级通知消息
+ */
+ private buildUpgradeMessage(requireReLogin: boolean): string {
+ const { title: content } = upgradeLogList.value[0]
+
+ const messageParts = [
+ ``,
+ `系统已升级到 ${StorageConfig.CURRENT_VERSION} 版本,此次更新带来了以下改进:`,
+ `
`,
+ content
+ ]
+
+ if (requireReLogin) {
+ messageParts.push(
+ `升级完成,请重新登录后继续使用。
`
+ )
+ }
+
+ return messageParts.join('')
+ }
+
+ /**
+ * 显示升级通知
+ */
+ private showUpgradeNotification(message: string): void {
+ ElNotification({
+ title: '系统升级公告',
+ message,
+ duration: 0,
+ type: 'success',
+ dangerouslyUseHTMLString: true
+ })
+ }
+
+ /**
+ * 清理旧版本数据
+ */
+ private cleanupLegacyData(oldSysKey: string | null, oldVersionKeys: string[]): void {
+ // 清理旧的单一存储结构
+ if (oldSysKey) {
+ localStorage.removeItem(oldSysKey)
+ console.info(`[Upgrade] 已清理旧存储: ${oldSysKey}`)
+ }
+
+ // 清理旧版本的分离存储
+ oldVersionKeys.forEach((key) => {
+ localStorage.removeItem(key)
+ console.info(`[Upgrade] 已清理旧存储: ${key}`)
+ })
+ }
+
+ /**
+ * 执行升级后的登出操作
+ */
+ private performLogout(): void {
+ try {
+ useUserStore().logOut()
+ console.info('[Upgrade] 已执行升级后登出')
+ } catch (error) {
+ console.error('[Upgrade] 升级后登出失败:', error)
+ }
+ }
+
+ /**
+ * 执行升级流程
+ */
+ private async executeUpgrade(
+ storedVersion: string,
+ legacyStorage: ReturnType
+ ): Promise {
+ try {
+ if (!upgradeLogList.value.length) {
+ console.warn('[Upgrade] 升级日志列表为空')
+ return
+ }
+
+ const requireReLogin = this.shouldRequireReLogin(storedVersion)
+ const message = this.buildUpgradeMessage(requireReLogin)
+
+ // 显示升级通知
+ this.showUpgradeNotification(message)
+
+ // 更新版本号
+ this.setStoredVersion(StorageConfig.CURRENT_VERSION)
+
+ // 清理旧数据
+ this.cleanupLegacyData(legacyStorage.oldSysKey, legacyStorage.oldVersionKeys)
+
+ // 执行登出(如果需要)
+ if (requireReLogin) {
+ this.performLogout()
+ }
+
+ console.info(`[Upgrade] 升级完成: ${storedVersion} → ${StorageConfig.CURRENT_VERSION}`)
+ } catch (error) {
+ console.error('[Upgrade] 系统升级处理失败:', error)
+ }
+ }
+
+ /**
+ * 系统升级处理主流程
+ */
+ async processUpgrade(): Promise {
+ // 跳过特定版本
+ if (this.shouldSkipUpgrade()) {
+ console.debug('[Upgrade] 跳过版本升级检查')
+ return
+ }
+
+ const storedVersion = this.getStoredVersion()
+
+ // 首次访问处理
+ if (this.isFirstVisit(storedVersion)) {
+ this.setStoredVersion(StorageConfig.CURRENT_VERSION)
+ // console.info('[Upgrade] 首次访问,已设置当前版本')
+ return
+ }
+
+ // 版本相同,无需升级
+ if (this.isSameVersion(storedVersion!)) {
+ // console.debug('[Upgrade] 版本相同,无需升级')
+ return
+ }
+
+ // 检查是否有需要升级的旧数据
+ const legacyStorage = this.findLegacyStorage()
+ if (!legacyStorage.oldSysKey && legacyStorage.oldVersionKeys.length === 0) {
+ this.setStoredVersion(StorageConfig.CURRENT_VERSION)
+ console.info('[Upgrade] 无旧数据,已更新版本号')
+ return
+ }
+
+ // 延迟执行升级流程,确保应用已完全加载
+ setTimeout(() => {
+ this.executeUpgrade(storedVersion!, legacyStorage)
+ }, StorageConfig.UPGRADE_DELAY)
+ }
+}
+
+// 创建版本管理器实例
+const versionManager = new VersionManager()
+
+/**
+ * 系统升级处理入口函数
+ */
+export async function systemUpgrade(): Promise {
+ await versionManager.processUpgrade()
+}
diff --git a/adminSystem/src/utils/table/tableCache.ts b/adminSystem/src/utils/table/tableCache.ts
new file mode 100644
index 0000000..045a7ce
--- /dev/null
+++ b/adminSystem/src/utils/table/tableCache.ts
@@ -0,0 +1,266 @@
+/**
+ * 表格缓存管理模块
+ *
+ * 提供高性能的表格数据缓存机制
+ *
+ * ## 主要功能
+ *
+ * - 基于参数的智能缓存键生成(使用 ohash)
+ * - LRU(最近最少使用)缓存淘汰策略
+ * - 缓存过期时间管理
+ * - 缓存大小限制和自动清理
+ * - 基于标签的缓存分组管理
+ * - 多种缓存失效策略(清空所有、清空当前、清空分页等)
+ * - 缓存访问统计和命中率分析
+ * - 缓存大小估算
+ *
+ * ## 使用场景
+ *
+ * - 表格数据的分页缓存
+ * - 减少重复的 API 请求
+ * - 提升表格切换和返回的响应速度
+ * - 搜索条件变化时的智能缓存管理
+ * - 数据更新后的缓存失效处理
+ *
+ * ## 缓存策略
+ *
+ * - CLEAR_ALL: 清空所有缓存(适用于全局数据更新)
+ * - CLEAR_CURRENT: 仅清空当前查询条件的缓存(适用于单条数据更新)
+ * - CLEAR_PAGINATION: 清空所有分页缓存但保留不同搜索条件(适用于批量操作)
+ * - KEEP_ALL: 不清除缓存(适用于只读操作)
+ *
+ * @module utils/table/tableCache
+ * @author Art Design Pro Team
+ */
+import { hash } from 'ohash'
+
+// 缓存失效策略枚举
+export enum CacheInvalidationStrategy {
+ /** 清空所有缓存 */
+ CLEAR_ALL = 'clear_all',
+ /** 仅清空当前查询条件的缓存 */
+ CLEAR_CURRENT = 'clear_current',
+ /** 清空所有分页缓存(保留不同搜索条件的缓存) */
+ CLEAR_PAGINATION = 'clear_pagination',
+ /** 不清除缓存 */
+ KEEP_ALL = 'keep_all'
+}
+
+// 通用 API 响应接口(兼容不同的后端响应格式)
+export interface ApiResponse {
+ records?: T[]
+ data?: T[]
+ total?: number
+ current?: number
+ size?: number
+ [key: string]: unknown
+}
+
+// 缓存存储接口
+export interface CacheItem {
+ data: T[]
+ response: ApiResponse
+ timestamp: number
+ params: string
+ // 缓存标签,用于分组管理
+ tags: Set
+ // 访问次数(用于 LRU 算法)
+ accessCount: number
+ // 最后访问时间
+ lastAccessTime: number
+}
+
+// 增强的缓存管理类
+export class TableCache {
+ private cache = new Map>()
+ private cacheTime: number
+ private maxSize: number
+ private enableLog: boolean
+
+ constructor(cacheTime = 5 * 60 * 1000, maxSize = 50, enableLog = false) {
+ // 默认5分钟,最多50条缓存
+ this.cacheTime = cacheTime
+ this.maxSize = maxSize
+ this.enableLog = enableLog
+ }
+
+ // 内部日志工具
+ private log(message: string, ...args: any[]) {
+ if (this.enableLog) {
+ console.log(`[TableCache] ${message}`, ...args)
+ }
+ }
+
+ // 生成稳定的缓存键
+ private generateKey(params: unknown): string {
+ return hash(params)
+ }
+
+ // 🔧 优化:增强类型安全性
+ private generateTags(params: Record): Set {
+ const tags = new Set()
+
+ // 添加搜索条件标签
+ const searchKeys = Object.keys(params).filter(
+ (key) =>
+ !['current', 'size', 'total'].includes(key) &&
+ params[key] !== undefined &&
+ params[key] !== '' &&
+ params[key] !== null
+ )
+
+ if (searchKeys.length > 0) {
+ const searchTag = searchKeys.map((key) => `${key}:${String(params[key])}`).join('|')
+ tags.add(`search:${searchTag}`)
+ } else {
+ tags.add('search:default')
+ }
+
+ // 添加分页标签
+ tags.add(`pagination:${params.size || 10}`)
+ // 添加通用分页标签,用于清理所有分页缓存
+ tags.add('pagination')
+
+ return tags
+ }
+
+ // 🔧 优化:LRU 缓存清理
+ private evictLRU(): void {
+ if (this.cache.size <= this.maxSize) return
+
+ // 找到最少使用的缓存项
+ let lruKey = ''
+ let minAccessCount = Infinity
+ let oldestTime = Infinity
+
+ for (const [key, item] of this.cache.entries()) {
+ if (
+ item.accessCount < minAccessCount ||
+ (item.accessCount === minAccessCount && item.lastAccessTime < oldestTime)
+ ) {
+ lruKey = key
+ minAccessCount = item.accessCount
+ oldestTime = item.lastAccessTime
+ }
+ }
+
+ if (lruKey) {
+ this.cache.delete(lruKey)
+ this.log(`LRU 清理缓存: ${lruKey}`)
+ }
+ }
+
+ // 设置缓存
+ set(params: unknown, data: T[], response: ApiResponse): void {
+ const key = this.generateKey(params)
+ const tags = this.generateTags(params as Record)
+ const now = Date.now()
+
+ // 检查是否需要清理
+ this.evictLRU()
+
+ this.cache.set(key, {
+ data,
+ response,
+ timestamp: now,
+ params: key,
+ tags,
+ accessCount: 1,
+ lastAccessTime: now
+ })
+ }
+
+ // 获取缓存
+ get(params: unknown): CacheItem | null {
+ const key = this.generateKey(params)
+ const item = this.cache.get(key)
+
+ if (!item) return null
+
+ // 检查是否过期
+ if (Date.now() - item.timestamp > this.cacheTime) {
+ this.cache.delete(key)
+ return null
+ }
+
+ // 更新访问统计
+ item.accessCount++
+ item.lastAccessTime = Date.now()
+
+ return item
+ }
+
+ // 根据标签清除缓存
+ clearByTags(tags: string[]): number {
+ let clearedCount = 0
+
+ for (const [key, item] of this.cache.entries()) {
+ // 检查是否包含任意一个标签
+ const hasMatchingTag = tags.some((tag) =>
+ Array.from(item.tags).some((itemTag) => itemTag.includes(tag))
+ )
+
+ if (hasMatchingTag) {
+ this.cache.delete(key)
+ clearedCount++
+ }
+ }
+
+ return clearedCount
+ }
+
+ // 清除当前搜索条件的缓存
+ clearCurrentSearch(params: unknown): number {
+ const key = this.generateKey(params)
+ const deleted = this.cache.delete(key)
+ return deleted ? 1 : 0
+ }
+
+ // 清除分页缓存
+ clearPagination(): number {
+ return this.clearByTags(['pagination'])
+ }
+
+ // 清空所有缓存
+ clear(): void {
+ this.cache.clear()
+ }
+
+ // 获取缓存统计信息
+ getStats(): { total: number; size: string; hitRate: string } {
+ const total = this.cache.size
+ let totalSize = 0
+ let totalAccess = 0
+
+ for (const item of this.cache.values()) {
+ // 粗略估算大小(JSON字符串长度)
+ totalSize += JSON.stringify(item.data).length
+ totalAccess += item.accessCount
+ }
+
+ // 转换为人类可读的大小
+ const sizeInKB = (totalSize / 1024).toFixed(2)
+ const avgHits = total > 0 ? (totalAccess / total).toFixed(1) : '0'
+
+ return {
+ total,
+ size: `${sizeInKB}KB`,
+ hitRate: `${avgHits} avg hits`
+ }
+ }
+
+ // 清理过期缓存
+ cleanupExpired(): number {
+ let cleanedCount = 0
+ const now = Date.now()
+
+ for (const [key, item] of this.cache.entries()) {
+ if (now - item.timestamp > this.cacheTime) {
+ this.cache.delete(key)
+ cleanedCount++
+ }
+ }
+
+ return cleanedCount
+ }
+}
diff --git a/adminSystem/src/utils/table/tableConfig.ts b/adminSystem/src/utils/table/tableConfig.ts
new file mode 100644
index 0000000..e464c89
--- /dev/null
+++ b/adminSystem/src/utils/table/tableConfig.ts
@@ -0,0 +1,55 @@
+/**
+ * 表格全局配置模块
+ *
+ * 提供表格与后端接口的字段映射配置
+ *
+ * ## 主要功能
+ *
+ * - 响应数据字段自动识别和映射
+ * - 支持多种常见的后端响应格式
+ * - 请求参数字段映射配置
+ * - 可扩展的字段配置机制
+ *
+ * ## 使用场景
+ *
+ * - 适配不同后端的分页接口格式
+ * - 统一前端表格组件的数据处理
+ * - 减少重复的数据转换代码
+ * - 支持多个后端服务的接口对接
+ *
+ * ## 配置说明
+ *
+ * - recordFields: 列表数据字段名(按优先级顺序查找)
+ * - totalFields: 总条数字段名
+ * - currentFields: 当前页码字段名
+ * - sizeFields: 每页大小字段名
+ * - paginationKey: 前端发送请求时使用的分页参数名
+ *
+ * ## 扩展方式
+ *
+ * 如果后端使用其他字段名,可以在对应数组中添加新的字段名
+ * 例如:recordFields: ['list', 'data', 'records', 'items', 'yourCustomField']
+ *
+ * @module utils/table/tableConfig
+ * @author Art Design Pro Team
+ */
+export const tableConfig = {
+ // 响应数据字段映射配置,系统会从接口返回数据中按顺序查找这些字段
+ // 列表数据
+ recordFields: ['list', 'data', 'records', 'items', 'result', 'rows'],
+ // 总条数
+ totalFields: ['total', 'count'],
+ // 当前页码
+ currentFields: ['current', 'page', 'pageNum'],
+ // 每页大小
+ sizeFields: ['size', 'pageSize', 'limit'],
+
+ // 请求参数映射配置,前端发送请求时使用的分页参数名
+ // useTable 组合式函数传递分页参数的时候 用 current 跟 size
+ paginationKey: {
+ // 当前页码
+ current: 'current',
+ // 每页大小
+ size: 'size'
+ }
+}
diff --git a/adminSystem/src/utils/table/tableUtils.ts b/adminSystem/src/utils/table/tableUtils.ts
new file mode 100644
index 0000000..3ca9db1
--- /dev/null
+++ b/adminSystem/src/utils/table/tableUtils.ts
@@ -0,0 +1,297 @@
+/**
+ * 表格工具函数模块
+ *
+ * 提供表格数据处理和请求管理的核心工具函数
+ *
+ * ## 主要功能
+ *
+ * - 多格式 API 响应自动适配和标准化
+ * - 表格数据提取和转换
+ * - 分页信息自动更新和校验
+ * - 智能防抖函数(支持取消和立即执行)
+ * - 统一的错误处理机制
+ * - 嵌套数据结构解析
+ *
+ * ## 使用场景
+ *
+ * - useTable 组合式函数的底层工具
+ * - 适配各种后端接口响应格式
+ * - 表格数据的标准化处理
+ * - 请求防抖和性能优化
+ * - 错误统一处理和日志记录
+ *
+ * ## 支持的响应格式
+ *
+ * 1. 直接数组: [item1, item2, ...]
+ * 2. 标准对象: { records: [], total: 100 }
+ * 3. 嵌套data: { data: { list: [], total: 100 } }
+ * 4. 多种字段名: list/data/records/items/result/rows
+ *
+ * ## 核心功能
+ *
+ * - defaultResponseAdapter: 智能识别和转换响应格式
+ * - extractTableData: 提取表格数据数组
+ * - updatePaginationFromResponse: 更新分页信息
+ * - createSmartDebounce: 创建可控的防抖函数
+ * - createErrorHandler: 生成错误处理器
+ *
+ * @module utils/table/tableUtils
+ * @author Art Design Pro Team
+ */
+
+import type { ApiResponse } from './tableCache'
+import { tableConfig } from './tableConfig'
+
+// 请求参数基础接口,扩展分页参数
+export interface BaseRequestParams extends Api.Common.PaginationParams {
+ [key: string]: unknown
+}
+
+// 错误处理接口
+export interface TableError {
+ code: string
+ message: string
+ details?: unknown
+}
+
+// 辅助函数:从对象中提取记录数组
+function extractRecords(obj: Record, fields: string[]): T[] {
+ for (const field of fields) {
+ if (field in obj && Array.isArray(obj[field])) {
+ return obj[field] as T[]
+ }
+ }
+ return []
+}
+
+// 辅助函数:从对象中提取总数
+function extractTotal(obj: Record, records: unknown[], fields: string[]): number {
+ for (const field of fields) {
+ if (field in obj && typeof obj[field] === 'number') {
+ return obj[field] as number
+ }
+ }
+ return records.length
+}
+
+// 辅助函数:提取分页参数
+function extractPagination(
+ obj: Record,
+ data?: Record
+): Pick, 'current' | 'size'> | undefined {
+ const result: Partial, 'current' | 'size'>> = {}
+ const sources = [obj, data ?? {}]
+
+ const currentFields = tableConfig.currentFields
+ for (const src of sources) {
+ for (const field of currentFields) {
+ if (field in src && typeof src[field] === 'number') {
+ result.current = src[field] as number
+ break
+ }
+ }
+ if (result.current !== undefined) break
+ }
+
+ const sizeFields = tableConfig.sizeFields
+ for (const src of sources) {
+ for (const field of sizeFields) {
+ if (field in src && typeof src[field] === 'number') {
+ result.size = src[field] as number
+ break
+ }
+ }
+ if (result.size !== undefined) break
+ }
+
+ if (result.current === undefined && result.size === undefined) return undefined
+ return result
+}
+
+/**
+ * 默认响应适配器 - 支持多种常见的API响应格式
+ */
+export const defaultResponseAdapter = (response: unknown): ApiResponse => {
+ // 定义支持的字段
+ const recordFields = tableConfig.recordFields
+
+ if (!response) {
+ return { records: [], total: 0 }
+ }
+
+ if (Array.isArray(response)) {
+ return { records: response, total: response.length }
+ }
+
+ if (typeof response !== 'object') {
+ console.warn(
+ '[tableUtils] 无法识别的响应格式,支持的格式包括: 数组、包含' +
+ recordFields.join('/') +
+ '字段的对象、嵌套data对象。当前格式:',
+ response
+ )
+ return { records: [], total: 0 }
+ }
+
+ const res = response as Record
+ let records: T[] = []
+ let total = 0
+ let pagination: Pick, 'current' | 'size'> | undefined
+
+ // 处理标准格式或直接列表
+ records = extractRecords(res, recordFields)
+ total = extractTotal(res, records, tableConfig.totalFields)
+ pagination = extractPagination(res)
+
+ // 如果没有找到,检查嵌套data
+ if (records.length === 0 && 'data' in res && typeof res.data === 'object') {
+ const data = res.data as Record
+ records = extractRecords(data, ['list', 'records', 'items'])
+ total = extractTotal(data, records, tableConfig.totalFields)
+ pagination = extractPagination(res, data)
+
+ if (Array.isArray(res.data)) {
+ records = res.data as T[]
+ total = records.length
+ }
+ }
+
+ if (!recordFields.some((field) => field in res) && records.length === 0) {
+ console.warn('[tableUtils] 无法识别的响应格式')
+ console.warn('支持的字段包括: ' + recordFields.join('、'), response)
+ console.warn('扩展字段请到 utils/table/tableConfig 文件配置')
+ }
+
+ const result: ApiResponse = { records, total }
+ if (pagination) {
+ Object.assign(result, pagination)
+ }
+ return result
+}
+
+/**
+ * 从标准化的API响应中提取表格数据
+ */
+export const extractTableData = (response: ApiResponse): T[] => {
+ const data = response.records || response.data || []
+ return Array.isArray(data) ? data : []
+}
+
+/**
+ * 根据API响应更新分页信息
+ */
+export const updatePaginationFromResponse = (
+ pagination: Api.Common.PaginationParams,
+ response: ApiResponse
+): void => {
+ pagination.total = response.total ?? pagination.total ?? 0
+
+ if (response.current !== undefined) {
+ pagination.current = response.current
+ }
+
+ const maxPage = Math.max(1, Math.ceil(pagination.total / (pagination.size || 1)))
+ if (pagination.current > maxPage) {
+ pagination.current = maxPage
+ }
+}
+
+/**
+ * 创建智能防抖函数 - 支持取消和立即执行
+ */
+export const createSmartDebounce = Promise>(
+ fn: T,
+ delay: number
+): T & { cancel: () => void; flush: () => Promise } => {
+ let timeoutId: NodeJS.Timeout | null = null
+ let lastArgs: Parameters | null = null
+ let lastResolve: ((value: any) => void) | null = null
+ let lastReject: ((reason: any) => void) | null = null
+
+ const debouncedFn = (...args: Parameters): Promise => {
+ return new Promise((resolve, reject) => {
+ if (timeoutId) clearTimeout(timeoutId)
+ lastArgs = args
+ lastResolve = resolve
+ lastReject = reject
+ timeoutId = setTimeout(async () => {
+ try {
+ const result = await fn(...args)
+ resolve(result)
+ } catch (error) {
+ reject(error)
+ } finally {
+ timeoutId = null
+ lastArgs = null
+ lastResolve = null
+ lastReject = null
+ }
+ }, delay)
+ })
+ }
+
+ debouncedFn.cancel = () => {
+ if (timeoutId) clearTimeout(timeoutId)
+ timeoutId = null
+ lastArgs = null
+ lastResolve = null
+ lastReject = null
+ }
+
+ debouncedFn.flush = async () => {
+ if (timeoutId && lastArgs && lastResolve && lastReject) {
+ clearTimeout(timeoutId)
+ timeoutId = null
+ const args = lastArgs
+ const resolve = lastResolve
+ const reject = lastReject
+ lastArgs = null
+ lastResolve = null
+ lastReject = null
+ try {
+ const result = await fn(...args)
+ resolve(result)
+ return result
+ } catch (error) {
+ reject(error)
+ throw error
+ }
+ }
+ return Promise.resolve()
+ }
+
+ return debouncedFn as any
+}
+
+/**
+ * 生成错误处理函数
+ */
+export const createErrorHandler = (
+ onError?: (error: TableError) => void,
+ enableLog: boolean = false
+) => {
+ const logger = {
+ error: (message: string, ...args: any[]) => {
+ if (enableLog) console.error(`[useTable] ${message}`, ...args)
+ }
+ }
+
+ return (err: unknown, context: string): TableError => {
+ const tableError: TableError = {
+ code: 'UNKNOWN_ERROR',
+ message: '未知错误',
+ details: err
+ }
+
+ if (err instanceof Error) {
+ tableError.message = err.message
+ tableError.code = err.name
+ } else if (typeof err === 'string') {
+ tableError.message = err
+ }
+
+ logger.error(`${context}:`, err)
+ onError?.(tableError)
+ return tableError
+ }
+}
diff --git a/adminSystem/src/utils/ui/animation.ts b/adminSystem/src/utils/ui/animation.ts
new file mode 100644
index 0000000..5efd02a
--- /dev/null
+++ b/adminSystem/src/utils/ui/animation.ts
@@ -0,0 +1,80 @@
+/**
+ * 主题动画工具模块
+ *
+ * 提供主题切换的视觉动画效果
+ *
+ * ## 主要功能
+ *
+ * - 基于鼠标点击位置的圆形扩散动画
+ * - View Transition API 支持(现代浏览器)
+ * - 降级处理(不支持动画的浏览器)
+ * - 暗黑主题切换过渡效果
+ * - 页面刷新时的主题过渡优化
+ *
+ * ## 使用场景
+ *
+ * - 明暗主题切换
+ * - 提升用户体验的视觉反馈
+ * - 页面刷新时的平滑过渡
+ *
+ * ## 技术实现
+ *
+ * - 使用 CSS 变量存储点击位置和半径
+ * - 利用 View Transition API 实现流畅动画
+ * - 通过 CSS class 控制过渡效果
+ * - 自动计算最大扩散半径
+ *
+ * @module utils/theme/animation
+ * @author Art Design Pro Team
+ */
+import { useCommon } from '@/hooks/core/useCommon'
+import { useTheme } from '@/hooks/core/useTheme'
+import { SystemThemeEnum } from '@/enums/appEnum'
+import { useSettingStore } from '@/store/modules/setting'
+const { LIGHT, DARK } = SystemThemeEnum
+
+/**
+ * 主题切换动画
+ * @param e 鼠标点击事件
+ */
+export const themeAnimation = (e: any) => {
+ const x = e.clientX
+ const y = e.clientY
+ // 计算鼠标点击位置距离视窗的最大圆半径
+ const endRadius = Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y))
+
+ // 设置CSS变量
+ document.documentElement.style.setProperty('--x', x + 'px')
+ document.documentElement.style.setProperty('--y', y + 'px')
+ document.documentElement.style.setProperty('--r', endRadius + 'px')
+
+ if (document.startViewTransition) {
+ document.startViewTransition(() => toggleTheme())
+ } else {
+ toggleTheme()
+ }
+}
+
+/**
+ * 切换主题
+ */
+const toggleTheme = () => {
+ useTheme().switchThemeStyles(useSettingStore().systemThemeType === LIGHT ? DARK : LIGHT)
+ useCommon().refresh()
+}
+
+/**
+ * 切换主题过渡效果
+ * @param enable 是否启用过渡效果
+ */
+export const toggleTransition = (enable: boolean) => {
+ const body = document.body
+
+ if (enable) {
+ body.classList.add('theme-change')
+ } else {
+ setTimeout(() => {
+ body.classList.remove('theme-change')
+ }, 300)
+ }
+}
diff --git a/adminSystem/src/utils/ui/colors.ts b/adminSystem/src/utils/ui/colors.ts
new file mode 100644
index 0000000..b4f6b77
--- /dev/null
+++ b/adminSystem/src/utils/ui/colors.ts
@@ -0,0 +1,273 @@
+/**
+ * 颜色处理工具模块
+ *
+ * 提供完整的颜色格式转换和处理功能
+ *
+ * ## 主要功能
+ *
+ * - Hex 与 RGB/RGBA 格式互转
+ * - 颜色混合计算
+ * - 颜色变浅/变深处理
+ * - Element Plus 主题色自动生成
+ * - 颜色格式验证
+ * - CSS 变量读取
+ * - 暗黑模式颜色适配
+ *
+ * ## 使用场景
+ *
+ * - 主题色动态切换
+ * - Element Plus 组件主题定制
+ * - 颜色渐变生成
+ * - 明暗主题颜色计算
+ * - 颜色格式标准化
+ *
+ * ## 核心功能
+ *
+ * - hexToRgba: Hex 转 RGBA(支持透明度)
+ * - hexToRgb: Hex 转 RGB 数组
+ * - rgbToHex: RGB 转 Hex
+ * - colourBlend: 两种颜色混合
+ * - getLightColor: 生成变浅的颜色
+ * - getDarkColor: 生成变深的颜色
+ * - handleElementThemeColor: 处理 Element Plus 主题色
+ * - setElementThemeColor: 设置完整的主题色系统
+ *
+ * ## 支持格式
+ *
+ * - Hex: #FFF, #FFFFFF
+ * - RGB: rgb(255, 255, 255)
+ * - RGBA: rgba(255, 255, 255, 0.5)
+ *
+ * @module utils/ui/colors
+ * @author Art Design Pro Team
+ */
+import { useSettingStore } from '@/store/modules/setting'
+
+/**
+ * 颜色转换结果接口
+ */
+interface RgbaResult {
+ red: number
+ green: number
+ blue: number
+ rgba: string
+}
+
+/**
+ * 获取CSS变量值(别名函数)
+ * @param name CSS变量名
+ * @returns CSS变量值
+ */
+export function getCssVar(name: string): string {
+ return getComputedStyle(document.documentElement).getPropertyValue(name)
+}
+
+/**
+ * 验证hex颜色格式
+ * @param hex hex颜色值
+ * @returns 是否为有效的hex颜色
+ */
+function isValidHexColor(hex: string): boolean {
+ const cleanHex = hex.trim().replace(/^#/, '')
+ return /^[0-9A-Fa-f]{3}$|^[0-9A-Fa-f]{6}$/.test(cleanHex)
+}
+
+/**
+ * 验证RGB颜色值
+ * @param r 红色值
+ * @param g 绿色值
+ * @param b 蓝色值
+ * @returns 是否为有效的RGB值
+ */
+function isValidRgbValue(r: number, g: number, b: number): boolean {
+ const isValid = (value: number) => Number.isInteger(value) && value >= 0 && value <= 255
+ return isValid(r) && isValid(g) && isValid(b)
+}
+
+/**
+ * 将hex颜色转换为RGBA
+ * @param hex hex颜色值 (支持 #FFF 或 #FFFFFF 格式)
+ * @param opacity 透明度 (0-1)
+ * @returns 包含RGB值和RGBA字符串的对象
+ */
+export function hexToRgba(hex: string, opacity: number): RgbaResult {
+ if (!isValidHexColor(hex)) {
+ throw new Error('Invalid hex color format')
+ }
+
+ // 移除可能存在的 # 前缀并转换为大写
+ let cleanHex = hex.trim().replace(/^#/, '').toUpperCase()
+
+ // 如果是缩写形式(如 FFF),转换为完整形式
+ if (cleanHex.length === 3) {
+ cleanHex = cleanHex
+ .split('')
+ .map((char) => char.repeat(2))
+ .join('')
+ }
+
+ // 解析 RGB 值
+ const [red, green, blue] = cleanHex.match(/\w\w/g)!.map((x) => parseInt(x, 16))
+
+ // 确保 opacity 在有效范围内
+ const validOpacity = Math.max(0, Math.min(1, opacity))
+
+ // 构建 RGBA 字符串
+ const rgba = `rgba(${red}, ${green}, ${blue}, ${validOpacity.toFixed(2)})`
+
+ return { red, green, blue, rgba }
+}
+
+/**
+ * 将hex颜色转换为RGB数组
+ * @param hexColor hex颜色值
+ * @returns RGB数组 [r, g, b]
+ */
+export function hexToRgb(hexColor: string): number[] {
+ if (!isValidHexColor(hexColor)) {
+ ElMessage.warning('输入错误的hex颜色值')
+ throw new Error('Invalid hex color format')
+ }
+
+ const cleanHex = hexColor.replace(/^#/, '')
+ let hex = cleanHex
+
+ // 处理缩写形式
+ if (hex.length === 3) {
+ hex = hex
+ .split('')
+ .map((char) => char.repeat(2))
+ .join('')
+ }
+
+ const hexPairs = hex.match(/../g)
+ if (!hexPairs) {
+ throw new Error('Invalid hex color format')
+ }
+
+ return hexPairs.map((hexPair) => parseInt(hexPair, 16))
+}
+
+/**
+ * 将RGB颜色转换为hex
+ * @param r 红色值 (0-255)
+ * @param g 绿色值 (0-255)
+ * @param b 蓝色值 (0-255)
+ * @returns hex颜色值
+ */
+export function rgbToHex(r: number, g: number, b: number): string {
+ if (!isValidRgbValue(r, g, b)) {
+ ElMessage.warning('输入错误的RGB颜色值')
+ throw new Error('Invalid RGB color values')
+ }
+
+ const toHex = (value: number) => {
+ const hex = value.toString(16)
+ return hex.length === 1 ? `0${hex}` : hex
+ }
+
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`
+}
+
+/**
+ * 颜色混合
+ * @param color1 第一个颜色
+ * @param color2 第二个颜色
+ * @param ratio 混合比例 (0-1)
+ * @returns 混合后的颜色
+ */
+export function colourBlend(color1: string, color2: string, ratio: number): string {
+ const validRatio = Math.max(0, Math.min(1, Number(ratio)))
+
+ const rgb1 = hexToRgb(color1)
+ const rgb2 = hexToRgb(color2)
+
+ const blendedRgb = rgb1.map((value1, index) => {
+ const value2 = rgb2[index]
+ return Math.round(value1 * (1 - validRatio) + value2 * validRatio)
+ })
+
+ return rgbToHex(blendedRgb[0], blendedRgb[1], blendedRgb[2])
+}
+
+/**
+ * 获取变浅的颜色
+ * @param color 原始颜色
+ * @param level 变浅程度 (0-1)
+ * @param isDark 是否为暗色主题
+ * @returns 变浅后的颜色
+ */
+export function getLightColor(color: string, level: number, isDark: boolean = false): string {
+ if (!isValidHexColor(color)) {
+ ElMessage.warning('输入错误的hex颜色值')
+ throw new Error('Invalid hex color format')
+ }
+
+ if (isDark) {
+ return getDarkColor(color, level)
+ }
+
+ const rgb = hexToRgb(color)
+ const lightRgb = rgb.map((value) => Math.floor((255 - value) * level + value))
+
+ return rgbToHex(lightRgb[0], lightRgb[1], lightRgb[2])
+}
+
+/**
+ * 获取变深的颜色
+ * @param color 原始颜色
+ * @param level 变深程度 (0-1)
+ * @returns 变深后的颜色
+ */
+export function getDarkColor(color: string, level: number): string {
+ if (!isValidHexColor(color)) {
+ ElMessage.warning('输入错误的hex颜色值')
+ throw new Error('Invalid hex color format')
+ }
+
+ const rgb = hexToRgb(color)
+ const darkRgb = rgb.map((value) => Math.floor(value * (1 - level)))
+
+ return rgbToHex(darkRgb[0], darkRgb[1], darkRgb[2])
+}
+
+/**
+ * 处理 Element Plus 主题颜色
+ * @param theme 主题颜色
+ * @param isDark 是否为暗色主题
+ */
+export function handleElementThemeColor(theme: string, isDark: boolean = false): void {
+ document.documentElement.style.setProperty('--el-color-primary', theme)
+
+ for (let i = 1; i <= 9; i++) {
+ document.documentElement.style.setProperty(
+ `--el-color-primary-light-${i}`,
+ getLightColor(theme, i / 10, isDark)
+ )
+ }
+
+ for (let i = 1; i <= 9; i++) {
+ document.documentElement.style.setProperty(
+ `--el-color-primary-dark-${i}`,
+ getDarkColor(theme, i / 10)
+ )
+ }
+}
+
+/**
+ * 设置 Element Plus 主题颜色
+ * @param color 主题颜色
+ */
+export function setElementThemeColor(color: string): void {
+ const mixColor = '#ffffff'
+ const elStyle = document.documentElement.style
+
+ elStyle.setProperty('--el-color-primary', color)
+ handleElementThemeColor(color, useSettingStore().isDark)
+
+ // 生成更淡一点的颜色
+ for (let i = 1; i < 16; i++) {
+ const itemColor = colourBlend(color, mixColor, i / 16)
+ elStyle.setProperty(`--el-color-primary-custom-${i}`, itemColor)
+ }
+}
diff --git a/adminSystem/src/utils/ui/emojo.ts b/adminSystem/src/utils/ui/emojo.ts
new file mode 100644
index 0000000..cabad7d
--- /dev/null
+++ b/adminSystem/src/utils/ui/emojo.ts
@@ -0,0 +1,24 @@
+/**
+ * 表情
+ * 用于在消息提示的时候显示对应的表情
+ *
+ * 用法
+ * ElMessage.success(`${EmojiText[200]} 图片上传成功`)
+ * ElMessage.error(`${EmojiText[400]} 图片上传失败`)
+ * ElMessage.error(`${EmojiText[500]} 图片上传失败`)
+ *
+ * @module utils/ui/emojo
+ * @author Art Design Pro Team
+ */
+
+// macos 用户 按 shift + 6 可以唤出更多表情……
+const EmojiText: { [key: string]: string } = {
+ '0': 'O_O', // 空
+ '200': '^_^', // 成功
+ '400': 'T_T', // 错误请求
+ '500': 'X_X' // 服务器内部错误,无法完成请求
+}
+
+// const EmojiIcon = ['🟢', '🔴', '🟡 ', '🚀', '✨', '💡', '🛠️', '🔥', '🎉', '🌟', '🌈']
+
+export default EmojiText
diff --git a/adminSystem/src/utils/ui/iconify-loader.ts b/adminSystem/src/utils/ui/iconify-loader.ts
new file mode 100644
index 0000000..035de16
--- /dev/null
+++ b/adminSystem/src/utils/ui/iconify-loader.ts
@@ -0,0 +1,31 @@
+/**
+ * 离线图标加载器
+ *
+ * 用于在内网环境下支持 Iconify 图标的离线加载。
+ * 通过预加载图标集数据,避免运行时从 CDN 获取图标。
+ *
+ * 使用方式:
+ * 1. 安装所需图标集:pnpm add -D @iconify-json/[icon-set-name]
+ * 2. 在此文件中导入并注册图标集
+ * 3. 在组件中使用:
+ *
+ * @module utils/ui/iconify-loader
+ * @author Art Design Pro Team
+ */
+
+// import { addCollection } from '@iconify/vue'
+
+// // 导入离线图标数据
+
+// // 系统必要图标库
+// import riIcons from '@iconify-json/ri/icons.json'
+
+// // 演示图标库(可选,生产环境可移除)
+// import svgSpinners from '@iconify-json/svg-spinners/icons.json'
+// import lineMd from '@iconify-json/line-md/icons.json'
+
+// // 注册离线图标集
+
+// addCollection(riIcons)
+// addCollection(svgSpinners)
+// addCollection(lineMd)
diff --git a/adminSystem/src/utils/ui/index.ts b/adminSystem/src/utils/ui/index.ts
new file mode 100644
index 0000000..9ca1049
--- /dev/null
+++ b/adminSystem/src/utils/ui/index.ts
@@ -0,0 +1,11 @@
+/**
+ * UI 相关工具函数统一导出
+ *
+ * @module utils/ui/index
+ * @author Art Design Pro Team
+ */
+
+export * from './colors'
+export * from './loading'
+export * from './tabs'
+export * from './emojo'
diff --git a/adminSystem/src/utils/ui/loading.ts b/adminSystem/src/utils/ui/loading.ts
new file mode 100644
index 0000000..6580e02
--- /dev/null
+++ b/adminSystem/src/utils/ui/loading.ts
@@ -0,0 +1,84 @@
+/**
+ * 全局 Loading 加载管理模块
+ *
+ * 提供统一的全屏加载动画管理
+ *
+ * ## 主要功能
+ *
+ * - 全屏 Loading 显示和隐藏
+ * - 自动适配明暗主题背景色
+ * - 自定义 SVG 加载动画
+ * - 单例模式防止重复创建
+ * - 锁定页面交互
+ *
+ * ## 使用场景
+ *
+ * - 页面初始化加载
+ * - 大量数据请求
+ * - 路由切换过渡
+ * - 异步操作等待
+ *
+ * ## 特性
+ *
+ * - 自动检测当前主题并应用对应背景色
+ * - 使用自定义 SVG 动画(四点旋转)
+ * - 单例模式确保同时只有一个 Loading
+ * - 提供便捷的显示/隐藏方法
+ *
+ * @module utils/ui/loading
+ * @author Art Design Pro Team
+ */
+import { fourDotsSpinnerSvg } from '@/assets/svg/loading'
+
+/**
+ * 获取当前主题对应的loading背景色
+ * @returns 背景色字符串
+ */
+const getLoadingBackground = (): string => {
+ const isDark = document.documentElement.classList.contains('dark')
+ return isDark ? 'rgba(7, 7, 7, 0.85)' : '#fff'
+}
+
+const DEFAULT_LOADING_CONFIG = {
+ lock: true,
+ get background() {
+ return getLoadingBackground()
+ },
+ svg: fourDotsSpinnerSvg,
+ svgViewBox: '0 0 40 40',
+ customClass: 'art-loading-fix'
+} as const
+
+interface LoadingInstance {
+ close: () => void
+}
+
+let loadingInstance: LoadingInstance | null = null
+
+export const loadingService = {
+ /**
+ * 显示 loading
+ * @returns 关闭 loading 的函数
+ */
+ showLoading(): () => void {
+ if (!loadingInstance) {
+ // 每次显示时获取最新的配置,确保背景色与当前主题同步
+ const config = {
+ ...DEFAULT_LOADING_CONFIG,
+ background: getLoadingBackground()
+ }
+ loadingInstance = ElLoading.service(config)
+ }
+ return () => this.hideLoading()
+ },
+
+ /**
+ * 隐藏 loading
+ */
+ hideLoading(): void {
+ if (loadingInstance) {
+ loadingInstance.close()
+ loadingInstance = null
+ }
+ }
+}
diff --git a/adminSystem/src/utils/ui/tabs.ts b/adminSystem/src/utils/ui/tabs.ts
new file mode 100644
index 0000000..5f53ea5
--- /dev/null
+++ b/adminSystem/src/utils/ui/tabs.ts
@@ -0,0 +1,60 @@
+/**
+ * 标签页布局配置模块
+ *
+ * 提供不同标签页样式的高度和间距配置
+ *
+ * ## 主要功能
+ *
+ * - 多种标签页样式配置(默认、卡片、谷歌风格)
+ * - 标签页打开/关闭状态的高度管理
+ * - 顶部间距自动计算
+ * - 配置获取和默认值处理
+ *
+ * ## 使用场景
+ *
+ * - 工作标签页(Worktab)布局计算
+ * - 页面内容区域高度调整
+ * - 标签页显示/隐藏时的动画
+ * - 响应式布局适配
+ *
+ * ## 配置项说明
+ *
+ * - openTop: 标签页显示时,内容区域距离顶部的距离
+ * - closeTop: 标签页隐藏时,内容区域距离顶部的距离
+ * - openHeight: 标签页显示时的总高度(包含标签栏)
+ * - closeHeight: 标签页隐藏时的总高度(仅头部)
+ *
+ * ## 支持的样式
+ *
+ * - tab-default: 默认标签页样式
+ * - tab-card: 卡片式标签页
+ * - tab-google: 谷歌浏览器风格标签页
+ *
+ * @module utils/ui/tabs
+ * @author Art Design Pro Team
+ */
+export const TAB_CONFIG = {
+ 'tab-default': {
+ openTop: 106,
+ closeTop: 60,
+ openHeight: 121,
+ closeHeight: 75
+ },
+ 'tab-card': {
+ openTop: 122,
+ closeTop: 78,
+ openHeight: 139,
+ closeHeight: 95
+ },
+ 'tab-google': {
+ openTop: 122,
+ closeTop: 78,
+ openHeight: 139,
+ closeHeight: 95
+ }
+}
+
+// 获取当前 tab 样式配置,设置默认值
+export const getTabConfig = (style: string) => {
+ return TAB_CONFIG[style as keyof typeof TAB_CONFIG] || TAB_CONFIG['tab-card'] // 默认使用 tab-card 配置
+}
diff --git a/adminSystem/src/views/auth/forget-password/index.vue b/adminSystem/src/views/auth/forget-password/index.vue
new file mode 100644
index 0000000..147259e
--- /dev/null
+++ b/adminSystem/src/views/auth/forget-password/index.vue
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/auth/login/index.vue b/adminSystem/src/views/auth/login/index.vue
new file mode 100644
index 0000000..7c6b4ae
--- /dev/null
+++ b/adminSystem/src/views/auth/login/index.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/auth/login/style.css b/adminSystem/src/views/auth/login/style.css
new file mode 100644
index 0000000..bd8c3a9
--- /dev/null
+++ b/adminSystem/src/views/auth/login/style.css
@@ -0,0 +1,38 @@
+@reference '@styles/core/tailwind.css';
+
+/* 授权页右侧区域 */
+.auth-right-wrap {
+ @apply absolute inset-0 w-[440px] h-[650px] py-[5px] m-auto overflow-hidden
+ max-sm:px-7 max-sm:w-full
+ animate-[slideInRight_0.6s_cubic-bezier(0.25,0.46,0.45,0.94)_forwards]
+ max-md:animate-none;
+
+ .form {
+ @apply h-full py-[40px];
+ }
+
+ .title {
+ @apply text-g-900 text-4xl font-semibold max-md:text-3xl max-sm:pt-10;
+ }
+
+ .sub-title {
+ @apply mt-[10px] text-g-600 text-sm;
+ }
+
+ .custom-height {
+ @apply !h-[40px];
+ }
+}
+
+/* 滑入动画 */
+@keyframes slideInRight {
+ from {
+ opacity: 0;
+ transform: translateX(30px);
+ }
+
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
diff --git a/adminSystem/src/views/auth/register/index.vue b/adminSystem/src/views/auth/register/index.vue
new file mode 100644
index 0000000..9a8570d
--- /dev/null
+++ b/adminSystem/src/views/auth/register/index.vue
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/dashboard/console/index.vue b/adminSystem/src/views/dashboard/console/index.vue
new file mode 100644
index 0000000..154c330
--- /dev/null
+++ b/adminSystem/src/views/dashboard/console/index.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/dashboard/console/modules/about-project.vue b/adminSystem/src/views/dashboard/console/modules/about-project.vue
new file mode 100644
index 0000000..ed946ce
--- /dev/null
+++ b/adminSystem/src/views/dashboard/console/modules/about-project.vue
@@ -0,0 +1,44 @@
+
+
+
+
关于项目
+
{{ systemName }} 是一款兼具设计美学与高效开发的后台系统
+
使用了 Vue3、TypeScript、Vite、Element Plus 等前沿技术
+
+
+
+

+
+
+
+
diff --git a/adminSystem/src/views/dashboard/console/modules/active-user.vue b/adminSystem/src/views/dashboard/console/modules/active-user.vue
new file mode 100644
index 0000000..da740f2
--- /dev/null
+++ b/adminSystem/src/views/dashboard/console/modules/active-user.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
用户概述
+
比上周 +23%
+
我们为您创建了多个选项,可将它们组合在一起并定制为像素完美的页面
+
+
+
+
{{ item.num }}
+
{{ item.name }}
+
+
+
+
+
+
diff --git a/adminSystem/src/views/dashboard/console/modules/card-list.vue b/adminSystem/src/views/dashboard/console/modules/card-list.vue
new file mode 100644
index 0000000..5fc76a7
--- /dev/null
+++ b/adminSystem/src/views/dashboard/console/modules/card-list.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
{{ item.des }}
+
+
+ 较上周
+
+ {{ item.change }}
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/dashboard/console/modules/dynamic-stats.vue b/adminSystem/src/views/dashboard/console/modules/dynamic-stats.vue
new file mode 100644
index 0000000..1876950
--- /dev/null
+++ b/adminSystem/src/views/dashboard/console/modules/dynamic-stats.vue
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+ {{ item.username }}
+ {{ item.type }}
+ {{ item.target }}
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/dashboard/console/modules/new-user.vue b/adminSystem/src/views/dashboard/console/modules/new-user.vue
new file mode 100644
index 0000000..9d39522
--- /dev/null
+++ b/adminSystem/src/views/dashboard/console/modules/new-user.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
![avatar]()
+
{{ scope.row.username }}
+
+
+
+
+
+
+
+ {{ scope.row.sex === 1 ? '男' : '女' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/dashboard/console/modules/sales-overview.vue b/adminSystem/src/views/dashboard/console/modules/sales-overview.vue
new file mode 100644
index 0000000..32904b8
--- /dev/null
+++ b/adminSystem/src/views/dashboard/console/modules/sales-overview.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
diff --git a/adminSystem/src/views/dashboard/console/modules/todo-list.vue b/adminSystem/src/views/dashboard/console/modules/todo-list.vue
new file mode 100644
index 0000000..ab9a86c
--- /dev/null
+++ b/adminSystem/src/views/dashboard/console/modules/todo-list.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
{{ item.username }}
+
{{ item.date }}
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/exception/403/index.vue b/adminSystem/src/views/exception/403/index.vue
new file mode 100644
index 0000000..2756c42
--- /dev/null
+++ b/adminSystem/src/views/exception/403/index.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/views/exception/404/index.vue b/adminSystem/src/views/exception/404/index.vue
new file mode 100644
index 0000000..6b64f45
--- /dev/null
+++ b/adminSystem/src/views/exception/404/index.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/views/exception/500/index.vue b/adminSystem/src/views/exception/500/index.vue
new file mode 100644
index 0000000..1b26377
--- /dev/null
+++ b/adminSystem/src/views/exception/500/index.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/views/index/index.vue b/adminSystem/src/views/index/index.vue
new file mode 100644
index 0000000..415a436
--- /dev/null
+++ b/adminSystem/src/views/index/index.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/index/style.scss b/adminSystem/src/views/index/style.scss
new file mode 100644
index 0000000..c89f354
--- /dev/null
+++ b/adminSystem/src/views/index/style.scss
@@ -0,0 +1,93 @@
+.app-layout {
+ display: flex;
+ width: 100%;
+ min-height: 100vh;
+ background: var(--default-bg-color);
+
+ #app-sidebar {
+ flex-shrink: 0;
+ }
+
+ #app-main {
+ display: flex;
+ flex: 1;
+ flex-direction: column;
+ min-width: 0;
+ height: 100vh;
+ overflow: auto;
+
+ #app-header {
+ position: sticky;
+ top: 0;
+ z-index: 50;
+ flex-shrink: 0;
+ width: 100%;
+ }
+
+ #app-content {
+ flex: 1;
+
+ :deep(.layout-content) {
+ box-sizing: border-box;
+ width: calc(100% - 40px);
+ margin: auto;
+
+ // 子页面默认 style
+ .page-content {
+ position: relative;
+ box-sizing: border-box;
+ padding: 20px;
+ overflow: hidden;
+ background: var(--default-box-color);
+ border-radius: calc(var(--custom-radius) / 2 + 2px) !important;
+ }
+ }
+ }
+ }
+}
+
+@media only screen and (width <= 1180px) {
+ .app-layout {
+ #app-main {
+ height: 100dvh;
+ }
+ }
+}
+
+@media only screen and (width <= 800px) {
+ .app-layout {
+ position: relative;
+
+ #app-sidebar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 300;
+ height: 100vh;
+ }
+
+ #app-main {
+ width: 100%;
+ height: auto;
+ overflow: visible;
+
+ #app-content {
+ :deep(.layout-content) {
+ width: calc(100% - 40px);
+ }
+ }
+ }
+ }
+}
+
+@media only screen and (width <= 640px) {
+ .app-layout {
+ #app-main {
+ #app-content {
+ :deep(.layout-content) {
+ width: calc(100% - 30px);
+ }
+ }
+ }
+ }
+}
diff --git a/adminSystem/src/views/outside/Iframe.vue b/adminSystem/src/views/outside/Iframe.vue
new file mode 100644
index 0000000..33ea0dc
--- /dev/null
+++ b/adminSystem/src/views/outside/Iframe.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/result/fail/index.vue b/adminSystem/src/views/result/fail/index.vue
new file mode 100644
index 0000000..8fe2583
--- /dev/null
+++ b/adminSystem/src/views/result/fail/index.vue
@@ -0,0 +1,28 @@
+
+
+
+ 您提交的内容有如下错误:
+
+
+ 您的账户已被冻结
+
+
+
+ 您的账户还不具备申请资格
+
+
+
+ 返回修改
+ 查看
+
+
+
+
+
diff --git a/adminSystem/src/views/result/success/index.vue b/adminSystem/src/views/result/success/index.vue
new file mode 100644
index 0000000..ae57aba
--- /dev/null
+++ b/adminSystem/src/views/result/success/index.vue
@@ -0,0 +1,21 @@
+
+
+
+ 已提交申请,等待部门审核。
+
+
+ 返回修改
+ 查看
+ 打印
+
+
+
+
+
diff --git a/adminSystem/src/views/system/menu/index.vue b/adminSystem/src/views/system/menu/index.vue
new file mode 100644
index 0000000..973b1e7
--- /dev/null
+++ b/adminSystem/src/views/system/menu/index.vue
@@ -0,0 +1,479 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/views/system/menu/modules/menu-dialog.vue b/adminSystem/src/views/system/menu/modules/menu-dialog.vue
new file mode 100644
index 0000000..f512301
--- /dev/null
+++ b/adminSystem/src/views/system/menu/modules/menu-dialog.vue
@@ -0,0 +1,384 @@
+
+
+
+
+
diff --git a/adminSystem/src/views/system/role/index.vue b/adminSystem/src/views/system/role/index.vue
new file mode 100644
index 0000000..aca447e
--- /dev/null
+++ b/adminSystem/src/views/system/role/index.vue
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
+
+
+ 新增角色
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/system/role/modules/role-edit-dialog.vue b/adminSystem/src/views/system/role/modules/role-edit-dialog.vue
new file mode 100644
index 0000000..46ff9b1
--- /dev/null
+++ b/adminSystem/src/views/system/role/modules/role-edit-dialog.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 取消
+ 提交
+
+
+
+
+
diff --git a/adminSystem/src/views/system/role/modules/role-permission-dialog.vue b/adminSystem/src/views/system/role/modules/role-permission-dialog.vue
new file mode 100644
index 0000000..3691ac8
--- /dev/null
+++ b/adminSystem/src/views/system/role/modules/role-permission-dialog.vue
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+ {{ data.label }}
+
+ {{ defaultProps.label(data) }}
+
+
+
+
+
+ 获取选中数据
+
+ {{ isExpandAll ? '全部收起' : '全部展开' }}
+ {{
+ isSelectAll ? '取消全选' : '全部选择'
+ }}
+ 保存
+
+
+
+
+
diff --git a/adminSystem/src/views/system/role/modules/role-search.vue b/adminSystem/src/views/system/role/modules/role-search.vue
new file mode 100644
index 0000000..1d59cee
--- /dev/null
+++ b/adminSystem/src/views/system/role/modules/role-search.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+
diff --git a/adminSystem/src/views/system/user-center/index.vue b/adminSystem/src/views/system/user-center/index.vue
new file mode 100644
index 0000000..ab52f00
--- /dev/null
+++ b/adminSystem/src/views/system/user-center/index.vue
@@ -0,0 +1,247 @@
+
+
+
+
+
+
+

+

+
{{ userInfo.userName }}
+
专注于用户体验跟视觉设计
+
+
+
+
+
jdkjjfnndf@mall.com
+
+
+
+
+
+
+
+
+
+
+
+
基本设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isEdit ? '保存' : '编辑' }}
+
+
+
+
+
+
+
更改密码
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isEditPwd ? '保存' : '编辑' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/system/user/index.vue b/adminSystem/src/views/system/user/index.vue
new file mode 100644
index 0000000..fb794d7
--- /dev/null
+++ b/adminSystem/src/views/system/user/index.vue
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 新增用户
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/system/user/modules/user-dialog.vue b/adminSystem/src/views/system/user/modules/user-dialog.vue
new file mode 100644
index 0000000..03cab4f
--- /dev/null
+++ b/adminSystem/src/views/system/user/modules/user-dialog.vue
@@ -0,0 +1,143 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/adminSystem/src/views/system/user/modules/user-search.vue b/adminSystem/src/views/system/user/modules/user-search.vue
new file mode 100644
index 0000000..e097720
--- /dev/null
+++ b/adminSystem/src/views/system/user/modules/user-search.vue
@@ -0,0 +1,112 @@
+
+
+
+
+
+
diff --git a/adminSystem/tsconfig.json b/adminSystem/tsconfig.json
new file mode 100644
index 0000000..4331962
--- /dev/null
+++ b/adminSystem/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "module": "esnext",
+ "moduleResolution": "node",
+ "strict": true,
+ "jsx": "preserve",
+ "sourceMap": true,
+ "resolveJsonModule": true,
+ "esModuleInterop": true,
+ "lib": ["esnext", "dom"],
+ "types": ["vite/client", "node", "element-plus/global"],
+ "skipLibCheck": true,
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["src/*"],
+ "@views/*": ["src/views/*"],
+ "@imgs/*": ["src/assets/images/*"],
+ "@icons/*": ["src/assets/icons/*"],
+ "@utils/*": ["src/utils/*"],
+ "@stores/*": ["src/store/*"],
+ "@plugins/*": ["src/plugins/*"],
+ "@styles/*": ["src/assets/styles/*"]
+ }
+ },
+ "include": ["src/**/*", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
+ "exclude": ["node_modules", "dist", "**/*.js"]
+}
diff --git a/adminSystem/vite.config.ts b/adminSystem/vite.config.ts
new file mode 100644
index 0000000..c2ef072
--- /dev/null
+++ b/adminSystem/vite.config.ts
@@ -0,0 +1,156 @@
+import { defineConfig, loadEnv } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import path from 'path'
+import { fileURLToPath } from 'url'
+import vueDevTools from 'vite-plugin-vue-devtools'
+import viteCompression from 'vite-plugin-compression'
+import Components from 'unplugin-vue-components/vite'
+import AutoImport from 'unplugin-auto-import/vite'
+import ElementPlus from 'unplugin-element-plus/vite'
+import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
+import tailwindcss from '@tailwindcss/vite'
+// import { visualizer } from 'rollup-plugin-visualizer'
+
+export default ({ mode }: { mode: string }) => {
+ const root = process.cwd()
+ const env = loadEnv(mode, root)
+ const { VITE_VERSION, VITE_PORT, VITE_BASE_URL, VITE_API_URL, VITE_API_PROXY_URL } = env
+
+ console.log(`🚀 API_URL = ${VITE_API_URL}`)
+ console.log(`🚀 VERSION = ${VITE_VERSION}`)
+
+ return defineConfig({
+ define: {
+ __APP_VERSION__: JSON.stringify(VITE_VERSION)
+ },
+ base: VITE_BASE_URL,
+ server: {
+ port: Number(VITE_PORT),
+ proxy: {
+ '/api': {
+ target: VITE_API_PROXY_URL,
+ changeOrigin: true
+ }
+ },
+ host: true
+ },
+ // 路径别名
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url)),
+ '@views': resolvePath('src/views'),
+ '@imgs': resolvePath('src/assets/images'),
+ '@icons': resolvePath('src/assets/icons'),
+ '@utils': resolvePath('src/utils'),
+ '@stores': resolvePath('src/store'),
+ '@styles': resolvePath('src/assets/styles')
+ }
+ },
+ build: {
+ target: 'es2015',
+ outDir: 'dist',
+ chunkSizeWarningLimit: 2000,
+ minify: 'terser',
+ terserOptions: {
+ compress: {
+ // 生产环境去除 console
+ drop_console: true,
+ // 生产环境去除 debugger
+ drop_debugger: true
+ }
+ },
+ dynamicImportVarsOptions: {
+ warnOnError: true,
+ exclude: [],
+ include: ['src/views/**/*.vue']
+ }
+ },
+ plugins: [
+ vue(),
+ tailwindcss(),
+ // 自动按需导入 API
+ AutoImport({
+ imports: ['vue', 'vue-router', 'pinia', '@vueuse/core'],
+ dts: 'src/types/import/auto-imports.d.ts',
+ resolvers: [ElementPlusResolver()],
+ eslintrc: {
+ enabled: true,
+ filepath: './.auto-import.json',
+ globalsPropValue: true
+ }
+ }),
+ // 自动按需导入组件
+ Components({
+ dts: 'src/types/import/components.d.ts',
+ resolvers: [ElementPlusResolver()]
+ }),
+ // 按需定制主题配置
+ ElementPlus({
+ useSource: true
+ }),
+ // 压缩
+ viteCompression({
+ verbose: false, // 是否在控制台输出压缩结果
+ disable: false, // 是否禁用
+ algorithm: 'gzip', // 压缩算法
+ ext: '.gz', // 压缩后的文件名后缀
+ threshold: 10240, // 只有大小大于该值的资源会被处理 10240B = 10KB
+ deleteOriginFile: false // 压缩后是否删除原文件
+ }),
+ vueDevTools()
+ // 打包分析
+ // visualizer({
+ // open: true,
+ // gzipSize: true,
+ // brotliSize: true,
+ // filename: 'dist/stats.html' // 分析图生成的文件名及路径
+ // }),
+ ],
+ // 依赖预构建:避免运行时重复请求与转换,提升首次加载速度
+ optimizeDeps: {
+ include: [
+ 'echarts/core',
+ 'echarts/charts',
+ 'echarts/components',
+ 'echarts/renderers',
+ 'xlsx',
+ 'xgplayer',
+ 'crypto-js',
+ 'file-saver',
+ 'vue-img-cutter',
+ 'element-plus/es',
+ 'element-plus/es/components/*/style/css',
+ 'element-plus/es/components/*/style/index'
+ ]
+ },
+ css: {
+ preprocessorOptions: {
+ // sass variable and mixin
+ scss: {
+ additionalData: `
+ @use "@styles/core/el-light.scss" as *;
+ @use "@styles/core/mixin.scss" as *;
+ `
+ }
+ },
+ postcss: {
+ plugins: [
+ {
+ postcssPlugin: 'internal:charset-removal',
+ AtRule: {
+ charset: (atRule) => {
+ if (atRule.name === 'charset') {
+ atRule.remove()
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ })
+}
+
+function resolvePath(paths: string) {
+ return path.resolve(__dirname, paths)
+}
diff --git a/backend-csharp/AmtScanner.Api/Controllers/RemoteDesktopController.cs b/backend-csharp/AmtScanner.Api/Controllers/RemoteDesktopController.cs
index 81df433..d5c6213 100644
--- a/backend-csharp/AmtScanner.Api/Controllers/RemoteDesktopController.cs
+++ b/backend-csharp/AmtScanner.Api/Controllers/RemoteDesktopController.cs
@@ -1,7 +1,9 @@
-using AmtScanner.Api.Data;
+using AmtScanner.Api.Data;
+using AmtScanner.Api.Models;
using AmtScanner.Api.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
+using System.Security.Cryptography;
namespace AmtScanner.Api.Controllers;
@@ -13,97 +15,136 @@ public class RemoteDesktopController : ControllerBase
private readonly AppDbContext _context;
private readonly ILogger _logger;
- public RemoteDesktopController(
- IGuacamoleService guacamoleService,
- AppDbContext context,
- ILogger logger)
+ public RemoteDesktopController(IGuacamoleService guacamoleService, AppDbContext context, ILogger logger)
{
_guacamoleService = guacamoleService;
_context = context;
_logger = logger;
}
- ///
- /// 获取远程桌面连接 URL
- ///
+ [HttpPost("generate-token/{deviceId}")]
+ public ActionResult GenerateToken(long deviceId, [FromBody] GenerateTokenRequest request)
+ {
+ return Ok(new { success = true, deviceId = deviceId, minutes = request.ExpiresInMinutes });
+ // var device = await _context.AmtDevices.FindAsync(deviceId);
+ if (device == null) return NotFound(new { error = "设备不存在" });
+
+ WindowsCredential? credential = request.CredentialId.HasValue
+ ? await _context.WindowsCredentials.FindAsync(request.CredentialId.Value)
+ : await _context.WindowsCredentials.FirstOrDefaultAsync(c => c.IsDefault);
+
+ if (credential == null) return BadRequest(new { error = "请先配置 Windows 凭据" });
+
+ var token = GenerateRandomToken();
+ var expiresAt = DateTime.UtcNow.AddMinutes(request.ExpiresInMinutes ?? 30);
+ var accessToken = new RemoteAccessToken { Token = token, DeviceId = deviceId, WindowsCredentialId = credential.Id, ExpiresAt = expiresAt, MaxUseCount = request.MaxUseCount ?? 1, Note = request.Note };
+ _context.RemoteAccessTokens.Add(accessToken);
+ await _context.SaveChangesAsync();
+
+ var baseUrl = $"{Request.Scheme}://{Request.Host}";
+ return Ok(new GenerateTokenResponse { Success = true, Token = token, AccessUrl = $"{baseUrl}/remote/{token}", ExpiresAt = expiresAt, MaxUseCount = accessToken.MaxUseCount, DeviceIp = device.IpAddress });
+ }
+
+ [HttpGet("connect-by-token/{token}")]
+ public async Task> ConnectByToken(string token)
+ {
+ var accessToken = await _context.RemoteAccessTokens.Include(t => t.Device).Include(t => t.WindowsCredential).FirstOrDefaultAsync(t => t.Token == token);
+ if (accessToken == null) return NotFound(new { error = "无效的访问链接" });
+ if (!accessToken.IsValid()) return BadRequest(new { error = "访问链接已过期或已达到使用次数上限" });
+ if (accessToken.Device == null || accessToken.WindowsCredential == null) return BadRequest(new { error = "设备或凭据信息不完整" });
+
+ accessToken.UseCount++;
+ accessToken.UsedAt = DateTime.UtcNow;
+ await _context.SaveChangesAsync();
+
+ var guacToken = await _guacamoleService.GetAuthTokenAsync();
+ if (string.IsNullOrEmpty(guacToken)) return StatusCode(503, new { error = "无法连接到 Guacamole 服务" });
+
+ var connectionId = await _guacamoleService.CreateOrGetConnectionAsync(guacToken, $"AMT-{accessToken.Device.IpAddress}", accessToken.Device.IpAddress, accessToken.WindowsCredential.Username, accessToken.WindowsCredential.Password);
+ if (string.IsNullOrEmpty(connectionId)) return StatusCode(500, new { error = "创建远程连接失败" });
+
+ var connectionUrl = await _guacamoleService.GetConnectionUrlAsync(guacToken, connectionId);
+ return Ok(new RemoteDesktopResponse { Success = true, ConnectionUrl = connectionUrl, ConnectionId = connectionId, Token = guacToken, DeviceIp = accessToken.Device.IpAddress });
+ }
+
+ [HttpGet("validate-token/{token}")]
+ public async Task> ValidateToken(string token)
+ {
+ var accessToken = await _context.RemoteAccessTokens.Include(t => t.Device).FirstOrDefaultAsync(t => t.Token == token);
+ if (accessToken == null) return Ok(new ValidateTokenResponse { Valid = false, Error = "无效的访问链接" });
+ if (!accessToken.IsValid()) return Ok(new ValidateTokenResponse { Valid = false, Error = "访问链接已过期或已达到使用次数上限" });
+ return Ok(new ValidateTokenResponse { Valid = true, DeviceIp = accessToken.Device?.IpAddress, ExpiresAt = accessToken.ExpiresAt, RemainingUses = accessToken.MaxUseCount > 0 ? accessToken.MaxUseCount - accessToken.UseCount : -1 });
+ }
+
+ [HttpGet("list-tokens/{deviceId}")]
+ public async Task>> GetDeviceTokens(long deviceId)
+ {
+ var tokens = await _context.RemoteAccessTokens.Where(t => t.DeviceId == deviceId && t.ExpiresAt > DateTime.UtcNow).OrderByDescending(t => t.CreatedAt)
+ .Select(t => new TokenInfoDto { Id = t.Id, Token = t.Token, CreatedAt = t.CreatedAt, ExpiresAt = t.ExpiresAt, MaxUseCount = t.MaxUseCount, UseCount = t.UseCount, Note = t.Note }).ToListAsync();
+ return Ok(tokens);
+ }
+
+ [HttpDelete("revoke-token/{tokenId}")]
+ public async Task RevokeToken(long tokenId)
+ {
+ var token = await _context.RemoteAccessTokens.FindAsync(tokenId);
+ if (token == null) return NotFound(new { error = "Token 不存在" });
+ _context.RemoteAccessTokens.Remove(token);
+ await _context.SaveChangesAsync();
+ return Ok(new { success = true });
+ }
+
+ [HttpPost("cleanup-tokens")]
+ public async Task CleanupExpiredTokens()
+ {
+ var count = await _context.RemoteAccessTokens.Where(t => t.ExpiresAt < DateTime.UtcNow).ExecuteDeleteAsync();
+ return Ok(new { success = true, deletedCount = count });
+ }
+
[HttpPost("connect/{deviceId}")]
public async Task> Connect(long deviceId, [FromBody] RdpCredentials credentials)
{
var device = await _context.AmtDevices.FindAsync(deviceId);
- if (device == null)
- {
- return NotFound(new { error = "设备不存在" });
- }
+ if (device == null) return NotFound(new { error = "设备不存在" });
- // 获取 Guacamole Token
- var token = await _guacamoleService.GetAuthTokenAsync();
- if (string.IsNullOrEmpty(token))
- {
- return StatusCode(503, new { error = "无法连接到 Guacamole 服务,请确保 Guacamole 已启动" });
- }
+ var guacToken = await _guacamoleService.GetAuthTokenAsync();
+ if (string.IsNullOrEmpty(guacToken)) return StatusCode(503, new { error = "无法连接到 Guacamole 服务" });
- // 创建或获取连接
- var connectionName = $"AMT-{device.IpAddress}";
- var connectionId = await _guacamoleService.CreateOrGetConnectionAsync(
- token,
- connectionName,
- device.IpAddress,
- credentials.Username,
- credentials.Password);
+ var connectionId = await _guacamoleService.CreateOrGetConnectionAsync(guacToken, $"AMT-{device.IpAddress}", device.IpAddress, credentials.Username, credentials.Password);
+ if (string.IsNullOrEmpty(connectionId)) return StatusCode(500, new { error = "创建远程连接失败" });
- if (string.IsNullOrEmpty(connectionId))
- {
- return StatusCode(500, new { error = "创建远程连接失败" });
- }
-
- // 获取连接 URL
- var connectionUrl = await _guacamoleService.GetConnectionUrlAsync(token, connectionId);
-
- _logger.LogInformation("Created remote desktop connection for device {Ip}, connectionId: {Id}",
- device.IpAddress, connectionId);
-
- return Ok(new RemoteDesktopResponse
- {
- Success = true,
- ConnectionUrl = connectionUrl,
- ConnectionId = connectionId,
- Token = token
- });
+ var connectionUrl = await _guacamoleService.GetConnectionUrlAsync(guacToken, connectionId);
+ return Ok(new RemoteDesktopResponse { Success = true, ConnectionUrl = connectionUrl, ConnectionId = connectionId, Token = guacToken });
+ }
+
+ [HttpPost("test-post/{id}")]
+ public ActionResult TestPost(long id, [FromBody] GenerateTokenRequest request)
+ {
+ return Ok(new { success = true, id = id, minutes = request.ExpiresInMinutes });
}
- ///
- /// 测试 Guacamole 连接
- ///
[HttpGet("test")]
public async Task TestConnection()
{
var token = await _guacamoleService.GetAuthTokenAsync();
- if (string.IsNullOrEmpty(token))
- {
- return StatusCode(503, new {
- success = false,
- error = "无法连接到 Guacamole 服务"
- });
- }
+ if (string.IsNullOrEmpty(token)) return StatusCode(503, new { success = false, error = "无法连接到 Guacamole 服务" });
+ return Ok(new { success = true, message = "Guacamole 服务连接正常" });
+ }
- return Ok(new {
- success = true,
- message = "Guacamole 服务连接正常"
- });
+ private static string GenerateRandomToken()
+ {
+ var bytes = new byte[24];
+ using var rng = RandomNumberGenerator.Create();
+ rng.GetBytes(bytes);
+ return Convert.ToBase64String(bytes).Replace("+", "-").Replace("/", "_").Replace("=", "");
}
}
-public class RdpCredentials
-{
- public string Username { get; set; } = string.Empty;
- public string Password { get; set; } = string.Empty;
-}
+public class GenerateTokenRequest { public long? CredentialId { get; set; } public int? ExpiresInMinutes { get; set; } = 30; public int? MaxUseCount { get; set; } = 1; public string? Note { get; set; } }
+public class GenerateTokenResponse { public bool Success { get; set; } public string Token { get; set; } = ""; public string AccessUrl { get; set; } = ""; public DateTime ExpiresAt { get; set; } public int MaxUseCount { get; set; } public string? DeviceIp { get; set; } public string? Error { get; set; } }
+public class ValidateTokenResponse { public bool Valid { get; set; } public string? DeviceIp { get; set; } public DateTime? ExpiresAt { get; set; } public int RemainingUses { get; set; } public string? Error { get; set; } }
+public class TokenInfoDto { public long Id { get; set; } public string Token { get; set; } = ""; public DateTime CreatedAt { get; set; } public DateTime ExpiresAt { get; set; } public int MaxUseCount { get; set; } public int UseCount { get; set; } public string? Note { get; set; } }
+public class RdpCredentials { public string Username { get; set; } = ""; public string Password { get; set; } = ""; }
+public class RemoteDesktopResponse { public bool Success { get; set; } public string? ConnectionUrl { get; set; } public string? ConnectionId { get; set; } public string? Token { get; set; } public string? DeviceIp { get; set; } public string? Error { get; set; } }
+
-public class RemoteDesktopResponse
-{
- public bool Success { get; set; }
- public string? ConnectionUrl { get; set; }
- public string? ConnectionId { get; set; }
- public string? Token { get; set; }
- public string? Error { get; set; }
-}
diff --git a/backend-csharp/AmtScanner.Api/Controllers/WindowsCredentialsController.cs b/backend-csharp/AmtScanner.Api/Controllers/WindowsCredentialsController.cs
new file mode 100644
index 0000000..e60d265
--- /dev/null
+++ b/backend-csharp/AmtScanner.Api/Controllers/WindowsCredentialsController.cs
@@ -0,0 +1,195 @@
+using AmtScanner.Api.Data;
+using AmtScanner.Api.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace AmtScanner.Api.Controllers;
+
+[ApiController]
+[Route("api/[controller]")]
+public class WindowsCredentialsController : ControllerBase
+{
+ private readonly AppDbContext _context;
+ private readonly ILogger _logger;
+
+ public WindowsCredentialsController(AppDbContext context, ILogger logger)
+ {
+ _context = context;
+ _logger = logger;
+ }
+
+ ///
+ /// 获取所有 Windows 凭据
+ ///
+ [HttpGet]
+ public async Task>> GetAll()
+ {
+ var credentials = await _context.WindowsCredentials
+ .OrderByDescending(c => c.IsDefault)
+ .ThenBy(c => c.Name)
+ .Select(c => new WindowsCredentialDto
+ {
+ Id = c.Id,
+ Name = c.Name,
+ Username = c.Username,
+ Domain = c.Domain,
+ IsDefault = c.IsDefault,
+ Note = c.Note,
+ CreatedAt = c.CreatedAt
+ })
+ .ToListAsync();
+
+ return Ok(credentials);
+ }
+
+ ///
+ /// 创建 Windows 凭据
+ ///
+ [HttpPost]
+ public async Task> Create([FromBody] CreateWindowsCredentialRequest request)
+ {
+ // 如果设为默认,取消其他默认
+ if (request.IsDefault)
+ {
+ await _context.WindowsCredentials
+ .Where(c => c.IsDefault)
+ .ExecuteUpdateAsync(s => s.SetProperty(c => c.IsDefault, false));
+ }
+
+ var credential = new WindowsCredential
+ {
+ Name = request.Name,
+ Username = request.Username,
+ Password = request.Password, // 实际生产环境应加密
+ Domain = request.Domain,
+ IsDefault = request.IsDefault,
+ Note = request.Note
+ };
+
+ _context.WindowsCredentials.Add(credential);
+ await _context.SaveChangesAsync();
+
+ _logger.LogInformation("Created Windows credential: {Name}", credential.Name);
+
+ return Ok(new WindowsCredentialDto
+ {
+ Id = credential.Id,
+ Name = credential.Name,
+ Username = credential.Username,
+ Domain = credential.Domain,
+ IsDefault = credential.IsDefault,
+ Note = credential.Note,
+ CreatedAt = credential.CreatedAt
+ });
+ }
+
+ ///
+ /// 更新 Windows 凭据
+ ///
+ [HttpPut("{id}")]
+ public async Task Update(long id, [FromBody] UpdateWindowsCredentialRequest request)
+ {
+ var credential = await _context.WindowsCredentials.FindAsync(id);
+ if (credential == null)
+ {
+ return NotFound(new { error = "凭据不存在" });
+ }
+
+ // 如果设为默认,取消其他默认
+ if (request.IsDefault && !credential.IsDefault)
+ {
+ await _context.WindowsCredentials
+ .Where(c => c.IsDefault && c.Id != id)
+ .ExecuteUpdateAsync(s => s.SetProperty(c => c.IsDefault, false));
+ }
+
+ credential.Name = request.Name;
+ credential.Username = request.Username;
+ if (!string.IsNullOrEmpty(request.Password))
+ {
+ credential.Password = request.Password;
+ }
+ credential.Domain = request.Domain;
+ credential.IsDefault = request.IsDefault;
+ credential.Note = request.Note;
+ credential.UpdatedAt = DateTime.UtcNow;
+
+ await _context.SaveChangesAsync();
+
+ return Ok(new { success = true });
+ }
+
+ ///
+ /// 删除 Windows 凭据
+ ///
+ [HttpDelete("{id}")]
+ public async Task Delete(long id)
+ {
+ var credential = await _context.WindowsCredentials.FindAsync(id);
+ if (credential == null)
+ {
+ return NotFound(new { error = "凭据不存在" });
+ }
+
+ _context.WindowsCredentials.Remove(credential);
+ await _context.SaveChangesAsync();
+
+ _logger.LogInformation("Deleted Windows credential: {Name}", credential.Name);
+
+ return Ok(new { success = true });
+ }
+
+ ///
+ /// 设置默认凭据
+ ///
+ [HttpPost("{id}/set-default")]
+ public async Task SetDefault(long id)
+ {
+ var credential = await _context.WindowsCredentials.FindAsync(id);
+ if (credential == null)
+ {
+ return NotFound(new { error = "凭据不存在" });
+ }
+
+ // 取消其他默认
+ await _context.WindowsCredentials
+ .Where(c => c.IsDefault)
+ .ExecuteUpdateAsync(s => s.SetProperty(c => c.IsDefault, false));
+
+ credential.IsDefault = true;
+ await _context.SaveChangesAsync();
+
+ return Ok(new { success = true });
+ }
+}
+
+public class WindowsCredentialDto
+{
+ public long Id { get; set; }
+ public string Name { get; set; } = string.Empty;
+ public string Username { get; set; } = string.Empty;
+ public string? Domain { get; set; }
+ public bool IsDefault { get; set; }
+ public string? Note { get; set; }
+ public DateTime CreatedAt { get; set; }
+}
+
+public class CreateWindowsCredentialRequest
+{
+ public string Name { get; set; } = string.Empty;
+ public string Username { get; set; } = string.Empty;
+ public string Password { get; set; } = string.Empty;
+ public string? Domain { get; set; }
+ public bool IsDefault { get; set; }
+ public string? Note { get; set; }
+}
+
+public class UpdateWindowsCredentialRequest
+{
+ public string Name { get; set; } = string.Empty;
+ public string Username { get; set; } = string.Empty;
+ public string? Password { get; set; }
+ public string? Domain { get; set; }
+ public bool IsDefault { get; set; }
+ public string? Note { get; set; }
+}
diff --git a/backend-csharp/AmtScanner.Api/Data/AppDbContext.cs b/backend-csharp/AmtScanner.Api/Data/AppDbContext.cs
index d941606..fb1edcc 100644
--- a/backend-csharp/AmtScanner.Api/Data/AppDbContext.cs
+++ b/backend-csharp/AmtScanner.Api/Data/AppDbContext.cs
@@ -14,6 +14,8 @@ public class AppDbContext : DbContext
public DbSet HardwareInfos { get; set; }
public DbSet MemoryModules { get; set; }
public DbSet StorageDevices { get; set; }
+ public DbSet WindowsCredentials { get; set; }
+ public DbSet RemoteAccessTokens { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -61,5 +63,34 @@ public class AppDbContext : DbContext
.WithMany(h => h.StorageDevices)
.HasForeignKey(s => s.HardwareInfoId)
.OnDelete(DeleteBehavior.Cascade);
+
+ // WindowsCredential 配置
+ modelBuilder.Entity()
+ .Property(w => w.Name)
+ .HasMaxLength(200);
+
+ modelBuilder.Entity()
+ .HasIndex(w => w.Name);
+
+ // RemoteAccessToken 配置
+ modelBuilder.Entity()
+ .Property(t => t.Token)
+ .HasMaxLength(64);
+
+ modelBuilder.Entity()
+ .HasIndex(t => t.Token)
+ .IsUnique();
+
+ modelBuilder.Entity()
+ .HasOne(t => t.Device)
+ .WithMany()
+ .HasForeignKey(t => t.DeviceId)
+ .OnDelete(DeleteBehavior.Cascade);
+
+ modelBuilder.Entity