commit 368e80f6bf72e4c744ca3c21017e575b7b65727b Author: Zy <1448159279@qq.com> Date: Sun Sep 27 10:15:49 2026 +0800 init diff --git a/.claude/agents/frontend-api-types.md b/.claude/agents/frontend-api-types.md new file mode 100644 index 0000000..d9e20d6 --- /dev/null +++ b/.claude/agents/frontend-api-types.md @@ -0,0 +1,32 @@ +--- +name: frontend-api-types +description: 前端 API 与类型定义专家。用于 plus-ui 前端项目中的 src/api 层、types.ts、返回结构、Query/Form/VO/InfoVO 定义,以及前后端接口映射任务;默认基线为 Gitee 仓库 JavaLionLi/plus-ui 的 6.X-Vue 分支。 +--- + +你负责 plus-ui 前端项目中的 API 层和类型定义。 + +基线仓库:`https://gitee.com/JavaLionLi/plus-ui` +默认分支:`6.X-Vue` +远端引用必须同时标记仓库、分支和文件路径;不要写本机绝对路径。 + +## 核心原则 + +1. 先看当前模块已有 `src/api//`。 +2. API 路径、返回类型、命名风格与当前模块保持一致。 +3. 能明确写出类型时,不要偷懒用 `any`。 +4. 如果当前模块已有 `export default { ... }`,继续保持一致。 + +## 重点关注 + +- `Query` +- `VO` +- `Form` +- `InfoVO` +- `AxiosPromise>` +- 详情接口与列表接口返回结构 + +## 自检 + +- API 路径是否与后端一致 +- 类型是否覆盖接口真实结构 +- 是否不必要地把类型写宽了 diff --git a/.claude/agents/frontend-crud-coding.md b/.claude/agents/frontend-crud-coding.md new file mode 100644 index 0000000..cacdc31 --- /dev/null +++ b/.claude/agents/frontend-crud-coding.md @@ -0,0 +1,22 @@ +--- +name: frontend-crud-coding +description: 前端总入口。用于 plus-ui 前端项目中的标准 CRUD 页面、新增 API/types、复杂列表页增强、树筛选、导入导出、权限按钮与弹窗表单等任务,并根据任务类型选择合适的前端子 agent;默认基线为 Gitee 仓库 JavaLionLi/plus-ui 的 6.X-Vue 分支。 +--- + +你是 plus-ui 前端项目的总入口 agent。 + +基线仓库:`https://gitee.com/JavaLionLi/plus-ui` +默认分支:`6.X-Vue` +远端引用必须同时标记仓库、分支和文件路径;不要在 agent 文档中写本机绝对路径。 + +先判断任务类型,再按下面规则处理: + +1. 如果是新增标准 CRUD 页面、补 `src/api`、`types.ts`、`index.vue`,优先使用 `frontend-crud-page.md`。 +2. 如果是修改已有列表页、增强导入导出、树筛选、更多菜单、状态切换,优先使用 `frontend-page-enhancement.md`。 +3. 如果只改接口层和类型定义,优先使用 `frontend-api-types.md`。 + +通用要求: + +- 先读当前目录下最近似页面和 API,再动代码。 +- 冲突时优先相信当前项目真实页面,其次是公共组件和工具,再其次才是 plus-ui Gitee 仓库 `6.X-Vue` 分支的 `gen` 模板。 +- 默认直接产出可落地代码,而不是只给抽象建议。 diff --git a/.claude/agents/frontend-crud-page.md b/.claude/agents/frontend-crud-page.md new file mode 100644 index 0000000..88ea7bc --- /dev/null +++ b/.claude/agents/frontend-crud-page.md @@ -0,0 +1,41 @@ +--- +name: frontend-crud-page +description: 前端标准 CRUD 页面专家。用于 plus-ui 前端项目中的新建列表页、弹窗表单页、标准 API/types/index.vue 骨架,以及 gen 模板到项目风格的落地任务;默认参考 Gitee 仓库 JavaLionLi/plus-ui 的 6.X-Vue 分支。 +--- + +你负责 plus-ui 前端项目中的标准 CRUD 页面实现。 + +基线仓库:`https://gitee.com/JavaLionLi/plus-ui` +默认分支:`6.X-Vue` +远端模板引用必须同时标记仓库、分支和路径,例如 `branch=6.X-Vue, path=gen/index.vue.ftl`;不要写本机绝对路径。 + +## 核心原则 + +1. 先看当前模块最近似页面。 +2. 再参考 plus-ui Gitee 仓库 `6.X-Vue` 分支中的 `gen` 模板。 +3. 默认同时维护: + `src/api///index.ts` + `src/api///types.ts` + `src/views///index.vue` + +## 页面规则 + +- 页面优先使用 ` diff --git a/gen/index.vue.ftl b/gen/index.vue.ftl new file mode 100644 index 0000000..59df6cc --- /dev/null +++ b/gen/index.vue.ftl @@ -0,0 +1,615 @@ + + + + diff --git a/gen/types.ts.ftl b/gen/types.ts.ftl new file mode 100644 index 0000000..de4d9c9 --- /dev/null +++ b/gen/types.ts.ftl @@ -0,0 +1,48 @@ +export interface ${BusinessName}VO { +<#list columns as column> +<#if column.list> + /** + * ${column.columnComment} + */ + ${column.javaField}: ${column.tsType}; +<#if column.htmlType == "imageUpload"> + /** + * ${column.columnComment}Url + */ + ${column.javaField}Url: string; + + + +<#if table.tree> + /** + * 子对象 + */ + children: ${BusinessName}VO[]; + +} + +export interface ${BusinessName}Form extends BaseEntity { +<#list columns as column> +<#if column.insert || column.edit> + /** + * ${column.columnComment} + */ + ${column.javaField}?: ${column.tsType}; + + +} + +export interface ${BusinessName}Query<#if !table.tree> extends PageQuery { +<#list columns as column> +<#if column.query> + /** + * ${column.columnComment} + */ + ${column.javaField}?: ${column.tsType}; + + + /** + * 日期范围参数 + */ + params?: any; +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..db1c6ca --- /dev/null +++ b/index.html @@ -0,0 +1,214 @@ + + + + + + + + + %VITE_APP_TITLE% + + + + + +
+
+
+
+
+
正在加载系统资源,请耐心等待
+
+
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..fda8452 --- /dev/null +++ b/package.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://json.schemastore.org/package", + "name": "ruoyi-vue-plus", + "version": "6.0.0", + "description": "RuoYi-Vue-Plus后台管理系统", + "license": "MIT", + "author": "LionLi", + "repository": { + "type": "git", + "url": "https://gitee.com/JavaLionLi/plus-ui.git" + }, + "packageManager": "pnpm@10.34.5", + "type": "module", + "scripts": { + "build": "vite build --mode production", + "build:dev": "vite build --mode development", + "build:prod": "vite build --mode production", + "dev": "vite serve --mode development", + "fmt": "oxfmt .", + "lint": "oxlint src", + "lint:fix": "oxlint --fix src", + "preview": "vite preview" + }, + "dependencies": { + "@element-plus/icons-vue": "2.3.2", + "@highlightjs/vue-plugin": "2.1.2", + "@iconify/vue": "^5.0.1", + "@noble/ciphers": "^2.4.0", + "@vueuse/core": "14.4.0", + "@wangeditor-next/editor": "6.4.1", + "@wangeditor-next/editor-for-vue": "6.4.1", + "animate.css": "4.1.1", + "await-to-js": "3.0.0", + "axios": "1.20.0", + "echarts": "6.1.0", + "element-plus": "2.14.5", + "highlight.js": "11.12.0", + "image-conversion": "2.1.1", + "jsencrypt": "3.5.4", + "nprogress": "0.2.0", + "pinia": "4.0.3", + "vue": "3.5.42", + "vue-cropper": "1.1.4", + "vue-i18n": "11.4.10", + "vue-json-pretty": "2.6.0", + "vue-router": "5.3.1", + "vue-types": "7.0.0", + "vxe-pc-ui": "4.18.9", + "vxe-table": "4.21.9" + }, + "devDependencies": { + "@types/node": "^26.1.1", + "@types/nprogress": "0.2.3", + "@unocss/preset-attributify": "66.10.1", + "@unocss/preset-wind3": "66.10.1", + "@vitejs/plugin-vue": "^6.0.8", + "@vue/compiler-sfc": "3.5.42", + "autoprefixer": "10.5.5", + "oxfmt": "^0.67.0", + "oxlint": "^1.82.0", + "sass": "1.104.0", + "typescript": "^6.0.3", + "unocss": "66.10.1", + "unplugin-auto-import": "21.1.0", + "unplugin-vue-components": "32.1.0", + "unplugin-vue-setup-extend-plus": "1.0.1", + "vite": "^8.2.2", + "vite-plugin-svg-icons-ng": "^1.9.3", + "vitest": "4.1.11", + "vue-tsc": "^3.3.11" + }, + "browserslist": [ + "Chrome >= 87", + "Edge >= 88", + "Safari >= 14", + "Firefox >= 78" + ], + "engines": { + "node": ">=20.19.0", + "pnpm": ">=10.0.0" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..ad926f7 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4898 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + esbuild: 0.28.1 + +importers: + + .: + dependencies: + '@element-plus/icons-vue': + specifier: 2.3.2 + version: 2.3.2(vue@3.5.42(typescript@6.0.3)) + '@highlightjs/vue-plugin': + specifier: 2.1.2 + version: 2.1.2(highlight.js@11.12.0)(vue@3.5.42(typescript@6.0.3)) + '@iconify/vue': + specifier: ^5.0.1 + version: 5.0.1(vue@3.5.42(typescript@6.0.3)) + '@noble/ciphers': + specifier: ^2.4.0 + version: 2.4.0 + '@vueuse/core': + specifier: 14.4.0 + version: 14.4.0(vue@3.5.42(typescript@6.0.3)) + '@wangeditor-next/editor': + specifier: 6.4.1 + version: 6.4.1 + '@wangeditor-next/editor-for-vue': + specifier: 6.4.1 + version: 6.4.1(@wangeditor-next/editor@6.4.1)(vue@3.5.42(typescript@6.0.3)) + animate.css: + specifier: 4.1.1 + version: 4.1.1 + await-to-js: + specifier: 3.0.0 + version: 3.0.0 + axios: + specifier: 1.20.0 + version: 1.20.0 + echarts: + specifier: 6.1.0 + version: 6.1.0 + element-plus: + specifier: 2.14.5 + version: 2.14.5(vue@3.5.42(typescript@6.0.3)) + highlight.js: + specifier: 11.12.0 + version: 11.12.0 + image-conversion: + specifier: 2.1.1 + version: 2.1.1 + jsencrypt: + specifier: 3.5.4 + version: 3.5.4 + nprogress: + specifier: 0.2.0 + version: 0.2.0 + pinia: + specifier: 4.0.3 + version: 4.0.3(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.42(typescript@6.0.3)) + vue: + specifier: 3.5.42 + version: 3.5.42(typescript@6.0.3) + vue-cropper: + specifier: 1.1.4 + version: 1.1.4 + vue-i18n: + specifier: 11.4.10 + version: 11.4.10(vue@3.5.42(typescript@6.0.3)) + vue-json-pretty: + specifier: 2.6.0 + version: 2.6.0(vue@3.5.42(typescript@6.0.3)) + vue-router: + specifier: 5.3.1 + version: 5.3.1(@vue/compiler-sfc@3.5.42)(pinia@4.0.3(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.42(typescript@6.0.3)))(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3)) + vue-types: + specifier: 7.0.0 + version: 7.0.0(vue@3.5.42(typescript@6.0.3)) + vxe-pc-ui: + specifier: 4.18.9 + version: 4.18.9(vue@3.5.42(typescript@6.0.3)) + vxe-table: + specifier: 4.21.9 + version: 4.21.9 + devDependencies: + '@types/node': + specifier: ^26.1.1 + version: 26.1.1 + '@types/nprogress': + specifier: 0.2.3 + version: 0.2.3 + '@unocss/preset-attributify': + specifier: 66.10.1 + version: 66.10.1 + '@unocss/preset-wind3': + specifier: 66.10.1 + version: 66.10.1 + '@vitejs/plugin-vue': + specifier: ^6.0.8 + version: 6.0.8(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3)) + '@vue/compiler-sfc': + specifier: 3.5.42 + version: 3.5.42 + autoprefixer: + specifier: 10.5.5 + version: 10.5.5(postcss@8.5.28) + oxfmt: + specifier: ^0.67.0 + version: 0.67.0 + oxlint: + specifier: ^1.82.0 + version: 1.82.0 + sass: + specifier: 1.104.0 + version: 1.104.0 + typescript: + specifier: ^6.0.3 + version: 6.0.3 + unocss: + specifier: 66.10.1 + version: 66.10.1(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(cac@7.0.0)(crossws@0.4.12(srvx@1.0.4))(rolldown@1.2.8)(srvx@1.0.4)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + unplugin-auto-import: + specifier: 21.1.0 + version: 21.1.0(@vueuse/core@14.4.0(vue@3.5.42(typescript@6.0.3)))(oxc-parser@0.131.0)(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + unplugin-vue-components: + specifier: 32.1.0 + version: 32.1.0(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3)) + unplugin-vue-setup-extend-plus: + specifier: 1.0.1 + version: 1.0.1 + vite: + specifier: ^8.2.2 + version: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + vite-plugin-svg-icons-ng: + specifier: ^1.9.3 + version: 1.9.3(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + vitest: + specifier: 4.1.11 + version: 4.1.11(@types/node@26.1.1)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + vue-tsc: + specifier: ^3.3.11 + version: 3.3.11(typescript@6.0.3) + +packages: + + '@antfu/install-pkg@2.0.1': + resolution: {integrity: sha512-iCKVQcIC0e3oDxEfs3SHQGW+ovhBMZmS1TE+bTk50rVyMCBmCfClv7Qi3HQKlumYwvjb/iIMeWCW2i67q6kFfQ==} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.8': + resolution: {integrity: sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.8': + resolution: {integrity: sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==} + engines: {node: '>=6.9.0'} + + '@ctrl/tinycolor@4.2.1': + resolution: {integrity: sha512-Vh5uy9Y1JBMId6J1f68TMXnZrTCvNdo9Fu2k0d1YnJngR+sM5kWTKl441c/3TFs+cG0NWOjRWgQ/iaWhbYx+7A==} + engines: {node: '>=14'} + + '@devframes/hub@0.9.18': + resolution: {integrity: sha512-sYsJEYiNBPMXRIcte+YNY0wBCV7J4rsh5TUuRC7jnulP/1cFDiHsT4mFxxKbm1VUX38KMAHtGVKBqk+/LYus+g==} + peerDependencies: + devframe: 0.9.18 + + '@devframes/json-render@0.9.18': + resolution: {integrity: sha512-KMfcUxK3t6kbcoojDEXTy+mHZpt00IVXK6Yu9SJiqtLPTKmMsVU2MDkt+reafiX4ZczOycHHZUt5sMhl8tTDyQ==} + peerDependencies: + '@devframes/hub': 0.9.18 + devframe: 0.9.18 + peerDependenciesMeta: + '@devframes/hub': + optional: true + + '@devframes/service-shiki@0.9.18': + resolution: {integrity: sha512-xox/QfYreuGFS2eJfHbEOzliVdNjwHnrOV7UdA0dXn9YRob3vO0n1OR5iZTylXqkSCkN2v4iMV4OQfWl94sg+A==} + peerDependencies: + devframe: 0.9.18 + + '@devframes/vite@0.9.18': + resolution: {integrity: sha512-rWV4gwrRlihOgCBsqRomuP9b9urm9F1M3pxUSvpWFhJiA/Afa+7z+3ORs4ntZItZhMeLsOupT/0Fe6EsAui2Hg==} + peerDependencies: + '@devframes/hub': 0.9.18 + '@devframes/hub-ui': 0.9.18 + devframe: 0.9.18 + vite: ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@devframes/hub': + optional: true + '@devframes/hub-ui': + optional: true + vite: + optional: true + + '@element-plus/icons-vue@2.3.2': + resolution: {integrity: sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==} + peerDependencies: + vue: ^3.2.0 + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@floating-ui/core@1.8.0': + resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==} + + '@floating-ui/dom@1.8.0': + resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==} + + '@floating-ui/utils@0.2.12': + resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} + + '@highlightjs/vue-plugin@2.1.2': + resolution: {integrity: sha512-P7s9DLBGpBsbltk3ZKuYUu9lwDCEfpttjJ5ErI6Wco6GQWIsjcSCJzCoMHgXhRUNkFDtZo7ZgeF8M10POPbDLw==} + peerDependencies: + highlight.js: ^11.0.1 + vue: ^3 + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.1.7': + resolution: {integrity: sha512-JZHlwdID+dy+lTgbYC8NEC4zeugqeYsc6jewvzb4c58kHauJn+X7rNwQjxz5p2qSjqaEeQoLkCIQ9v/H4PK0/w==} + + '@iconify/vue@5.0.1': + resolution: {integrity: sha512-aumwwooJlFJ5H5qYWB6ZTAyM0C8hpfcSVLB9/a3qnH1GGvIJ+FEbpEs4s/HfErYe/M5qZeLjwmESR5fFm3lXEw==} + peerDependencies: + vue: '>=3.0.0' + + '@intlify/core-base@11.4.10': + resolution: {integrity: sha512-+yJ74JRWVJokdgG9zYNMyTSzeNV3O9T4vVxk8PvLFHmI+R/BYA//cITh7vhRK37hWLZ4/kTcKcUz1dlWOpypIg==} + engines: {node: '>= 22'} + + '@intlify/devtools-types@11.4.10': + resolution: {integrity: sha512-xZxzZsAuu6/0zoLRVQWdpXWe5Kjl0LnWpjlQA3r9u9FbLYMhapqt7IwkgQyn0Tm2GUNAqhj9eZiUmYOrB024BQ==} + engines: {node: '>= 22'} + + '@intlify/message-compiler@11.4.10': + resolution: {integrity: sha512-oUB/scz2EJENXDiUJ7JjZffOrH8UIZ1BuZeHvonbi5fWLavLt04aivuk2OIByOZA0tsci1bkeeQRmwhb5M8Imw==} + engines: {node: '>= 22'} + + '@intlify/shared@11.4.10': + resolution: {integrity: sha512-FeImVdPeoSHTm3NBFFZHv0eRP9gQ3F4lj2puDBX5Kw7iiM1uJW6JTf39ian0K/17pbXCI3ef5i9RVsRrALqI6Q==} + engines: {node: '>= 22'} + + '@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/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@json-render/core@0.20.0': + resolution: {integrity: sha512-cXAXn7h3QaylefQgh85AEGPuVq4C8SJ3k9JqigWLJhaBSozF1Fmsrm43DP16RmkqNiVgj5pBRdmB48lEO5qlow==} + peerDependencies: + zod: ^4.0.0 + + '@modelcontextprotocol/core@2.0.0': + resolution: {integrity: sha512-pJCEwGG7Lfr/+PQp9ZTwKXNeO5wzbfKL7H3MYpCorM4oFBoQrdjnBgEoqG+RjhsvS1FKrDbKux+M1HhlnGWqcA==} + engines: {node: '>=20'} + + '@modelcontextprotocol/server@2.0.0': + resolution: {integrity: sha512-YhHWdHfpFMQfd0prsEnxKeS3Qz3ytIGmsS0sth4KDjnacIT7hxk6hXHkJ9KysxlkvTM+WZAtQbbcUhdoP4Hvtw==} + engines: {node: '>=20'} + + '@napi-rs/wasm-runtime@1.2.3': + resolution: {integrity: sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==} + engines: {node: ^20.19.0 || ^22.13.0 || >=23.5.0} + peerDependencies: + '@emnapi/core': ^1.7.1 || ^2.0.0-alpha.4 + '@emnapi/runtime': ^1.7.1 || ^2.0.0-alpha.4 + + '@noble/ciphers@2.4.0': + resolution: {integrity: sha512-AnjFn0Jv92laAkvMrghlFZq4qQCIN/4DxFV/eooqtC2YTjB7kBeLMS2T9KJX4Dn+ZVXLOwK0lSgqDtx9gvxtiw==} + engines: {node: '>= 20.19.0'} + + '@nodable/entities@3.0.0': + resolution: {integrity: sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==} + + '@oxc-parser/binding-android-arm-eabi@0.131.0': + resolution: {integrity: sha512-t2xicr9pfzkSRYx5aPqZqlLaayIwJTqgQ81Jor31Xep2nGyL2Aq3d0K5wOfeR7VevaSdxaS9dzSQP9xDwn8fDg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxc-parser/binding-android-arm64@0.131.0': + resolution: {integrity: sha512-nlGIod6gw75x1aEDgLS+srj+JRGY0HHm9MI9YgzE/B64l6d6+H3MSP9NOgp0+HTg8tp4vV9rVfgQGgd+TfVZcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxc-parser/binding-darwin-arm64@0.131.0': + resolution: {integrity: sha512-jukuV6xe5RbQKFo7QD34NDCLDZp4PSOm8rmckhNdH/60ymG5zXbDzGBEyc+nTkuLQNama2aSGCt+CPfpjNTqyw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxc-parser/binding-darwin-x64@0.131.0': + resolution: {integrity: sha512-g3JOo4khe9rslHm5WYaVDWb0HS/M1MLR3I9S8560MkKIcC96VQY00QjOlsuRyfSj/JDXj8i9T7ryPO2RidiXVg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxc-parser/binding-freebsd-x64@0.131.0': + resolution: {integrity: sha512-1hziITDTxjMePnX+dR9ocVT+EuZkQ8wm4FPAbmbEiKG+Phbo73J1ZnPAA6Y/aGsWF3McOFnQuZIktAFwalkfJQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxc-parser/binding-linux-arm-gnueabihf@0.131.0': + resolution: {integrity: sha512-9uRxfXwyKG9+MwmGQBo2ncPNwZH5HTmCETFM2WiuDBNDCW4NC5ttSQkwCAMrTAWgwMzVBH1CP8pM0v7nebCWXQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm-musleabihf@0.131.0': + resolution: {integrity: sha512-mgbLvzRShXOLBdWGInf08Af4q+pfj1xD8hSgLClDZ9of/BXkB6+LIhTH7fihiDUipqB3yoSkKBWaZ3Ejlf5Yag==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxc-parser/binding-linux-arm64-gnu@0.131.0': + resolution: {integrity: sha512-OPT8++4aN6j2GJ8+3IZHS/byXoZP4aSBn+FoG6rgBJ2fKwPKXWF3MqrFMNW7NKHM28FLY579xYLxJSfgobEqPA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-arm64-musl@0.131.0': + resolution: {integrity: sha512-vtPiwmfVTAXzaxDKsOXG+LwgRAA7WEnaeHzhS5z0GE89gAK18KSXnly7Z6saXXq6L3dVMyK44uoTI03zKxrpmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-ppc64-gnu@0.131.0': + resolution: {integrity: sha512-8AW8L7w5cGHSdZPcyZX2yR0+GUODsT15rbRjfdD54rv6DMbtuEB19ysLOpKJlRGfH6UNYNpCHaU1uJWgTWf1/w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-gnu@0.131.0': + resolution: {integrity: sha512-vvpjkjEOUsPcsYf8evE4MO3aGx9+3wodXEBOicGNnOwTuAik8eBONNkgSdhkGsAblQmfVHJyanRnpxglddTXIA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-riscv64-musl@0.131.0': + resolution: {integrity: sha512-AqmcNC3fClXX+fxQ6VGEN1667xVFiRBkY0CZmDMSiaeFUsv1+UkBPYYi48IUKcA9/ivvoKNRzQl2I4//kT9F/w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-linux-s390x-gnu@0.131.0': + resolution: {integrity: sha512-7d3jOMKy7RSQCcDLIci+ySll2FgsOMl/GiRux4q2JNv0zg4EdhFISa9idvrdN/HEUIQQJNg6dmveUeJl2YErGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-gnu@0.131.0': + resolution: {integrity: sha512-JHK/h95qVqVQ+ITER837kcTdwBDFpFaNnOTYGCP0zdUSX/mLKC7tXOoyrTb6vG7iRPwGlcgBil3v2IjYw1FqJA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxc-parser/binding-linux-x64-musl@0.131.0': + resolution: {integrity: sha512-b2BO82O8azXAyf7EUgOPKu145nWypbNyk07HbU09fkzhm9lEA5oPvaN/M8Nlo7tOErVTa2WOgS4QbOnxAPXdDQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxc-parser/binding-openharmony-arm64@0.131.0': + resolution: {integrity: sha512-GHO9glZaX7LkX/OGfluEPf1yjg+ehiFbUdowbX6uNWOQhmwKWU4m4+nZ9FJkrHNKuxyI1KKertMdGjVKCApKWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxc-parser/binding-wasm32-wasi@0.131.0': + resolution: {integrity: sha512-3SkikPaEFoih1N83qLVEDLRLeY4nYsf6JT9SnWiMCQ5lGQdKup6bEuKCqkRiG9dD1IIaFeYz9RjlciPmYoFIWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@oxc-parser/binding-win32-arm64-msvc@0.131.0': + resolution: {integrity: sha512-Os5bEhryeA2jkH+ZrnZyAC1EP5gs+X4YB1Fjqml7UPD5kU7ecsK1MPEVMfCrdt/GDNpDbavYXiOXOdyJ5b3OPw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxc-parser/binding-win32-ia32-msvc@0.131.0': + resolution: {integrity: sha512-m+jNz9EuF0NXoiptc6B9h5yompZQVW/a5MJeOu5zojfH5yWk82tvF2ccrHkfhgtrS9h9DD5l1Qv8dWlfY7Nz8g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxc-parser/binding-win32-x64-msvc@0.131.0': + resolution: {integrity: sha512-o14Hk8dAyiEUMFEWEgmAwFZvBt1RzAYLM3xeQ+5315JXgVYhoemivgYcbYVRbsFkS71ShMGlAFE0kPnr460rww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxc-project/types@0.131.0': + resolution: {integrity: sha512-PgnWDfV0h+b16XNKbXU7Daib/BFSt/J2mEzfYIBu6JB/wNdlU+kVYXCkGA1A9fWkTbOgbjh4e6NhPeQOYvFhEA==} + + '@oxc-project/types@0.149.0': + resolution: {integrity: sha512-Efcc+iF0j3Bf67YjEqIqWXbX5XddXoK/Mw4K1/JuXwRCZ8N16VR7iT23nlCc9XrveFVh/E5Rqs2StT0V8v9LdA==} + + '@oxfmt/binding-android-arm-eabi@0.67.0': + resolution: {integrity: sha512-2olh3ioEmc4gRzQm7jxyB1b/PFBoFvTq8KdgYySeNpysDtA6DEg2Mvya4/I6flhL7G0eOrE8RD7JCNCIMhE16Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.67.0': + resolution: {integrity: sha512-ulfw8EHN1MBq/MFFDXw2/M1VAFu5mRUcnuZ8Hqbv9viAnFzO9t1jKSAsDqKYYDGMlytF/uj6Z5z5n/tHupnKhw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.67.0': + resolution: {integrity: sha512-MfONZx/O2o9M5v2jDFol556G9+A+P9xCuJ4DZ+qhE+RnaCdoscy6Eu5nq1dbuNxhwdJyZ6kLI7fnG9mwEeOeGg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.67.0': + resolution: {integrity: sha512-CYnIx5LvFVJnyJcCqwH2jxMKjFjqo5678MPjdmNFoSGMhlOvZ/xRZqvhDcolKrXc8fezW3AKh+C4wyoFuWOSSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.67.0': + resolution: {integrity: sha512-7/iF1orvIS9mxhKUqnmtMgm+OrSQ5acPwuvdQrm6ECgqbwPmC+Pw9cdke3sNfVN6pT2hbJ58+jP8BCThl5HXOg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.67.0': + resolution: {integrity: sha512-yy+OGys07IZOpOmYPZoObKyUQLkfxeQqeCypk+1jaZd8HGo77hzvU1Jg8X3+W75o+9lszOjBfg0nkGtlwYywXw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.67.0': + resolution: {integrity: sha512-wPIeeigXgJpwNw3wydYRt3U9iN9Y/ejpOZuYL9IA7igxWs7LIQMOkhKxTumRvy6dIv0iXKk3RTw3Vmjg0i+2sg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.67.0': + resolution: {integrity: sha512-0+XNxcdbkTfxdcD4qW6Ci9n+mBNJ8xTBumnxKvKBmRFOdx0Wf8/KiHjCJayooXmYkqRpRVd98Q5egvzx5BLSgQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.67.0': + resolution: {integrity: sha512-I75LKPJyNOYUzkqAiAMIE31+Ye7xtQXZdoty1IXn4B+bw5Zpmez5wfG19ejGpNnS/BzQ7LFS+7jxuTPb+vHiZw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.67.0': + resolution: {integrity: sha512-c2M5iRpe1QMZSRE/UvZoPdXBWb5Ic/ycvOyNiKCqPwQ/OyOKIMiJs02ynlNnjb7ZZJnRXYLmGcohoINOcwDK3w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.67.0': + resolution: {integrity: sha512-dQzzYlV24Udhfm5ECuSdgqRvFJU/CGHzcYYEO3dLM6W6+CHiBFrq9OjIllkdCcPhsoSQ8o223Dja84MOSzed9A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.67.0': + resolution: {integrity: sha512-rFNq1CgX4qMJANOq42LkAs90JE80GpiaEohAV2qn/gT2hGjQTW1zBO5zQBxArI4926pM1OSzo3CN0tBszGBIaA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.67.0': + resolution: {integrity: sha512-Sky6rEdz2o5IGq01lPhS12yEvDdChVEcaYrcLHkveh4Fx0qPjljE/Iul6SX/bRMl6lNc8J7J/mDQdzgBdA++Pg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.67.0': + resolution: {integrity: sha512-vPXmlNORV8AZq2Ocxh07pxwMjfENUWCV/eZArnao0qC3NO/hDeTVkQvee7SJJUbIiF5PZbBa4kYmaXnu7Rk58w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.67.0': + resolution: {integrity: sha512-x/WAtFqYtVr3vZ9ni8nr4kn9whSitg8fOljq/pZzBpxopRdY1BMLZCZkrbIbaBcYkm46qGbqVea2FCWmtQ2P9w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.67.0': + resolution: {integrity: sha512-eRw9Neh4/aA6i+q/R3WU1gGQINhVM0J4fXIm6t27caOamkr/37uAkp1IdBx4zlJH97hmXR63z/q9n5c5dN7MzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.67.0': + resolution: {integrity: sha512-YIMvb+sGNYN2uc6+QK2HLPeEKM2vl7QZ5onQzpAJRb6pnf0DwUFP5R8tdS9R0l8hdUil2gu4Uxd0Yxrop0iT4w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.67.0': + resolution: {integrity: sha512-LzmU9MyACPzwNDIK0ItMedHPz735Ug7ELWguxo4/kuy6zWuDoeglOAEFCY8jLg0PzRpFO3hDyLFe2Gu2eFDeGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.67.0': + resolution: {integrity: sha512-sbQOIDNLUEeVZcAJcSL5VURn7kfjvilPviody4Yl5n8lQCDtUm+C9oHTTwZS/m4d/Z6Vv3jNEiAofH932NPPCg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.82.0': + resolution: {integrity: sha512-a3LB+C5Dsj5b/qtmG/mv5WrzuiXEpg1KF5nXWcEvaoN5TYAqkIvxPOwTPp3Jy/FoGpRo8zsTFhMElMXfeoOEzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.82.0': + resolution: {integrity: sha512-OBlhRgNqFblGpGenno/aqOfJLOkQ2B8Ig3iDAalfn0H8hJGZKXPeexCRTDm6uwv6YUjSA9Xnwt1y/Bgj5ZH8uw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.82.0': + resolution: {integrity: sha512-dsopxqtY5ZdyT9uLHyGt1SyiLop6hi7hWI3PKpePodkRQOkLaCm+OE4fR9CAz9qdfjiFO8531tX/QDyP/psjFg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.82.0': + resolution: {integrity: sha512-94Lu0SgTClKColU66g1VDuigV3HkcbkJBnTtZjGYfE8UPugaWDgKrm2icjC6HJVUYler2OXaHP/X0TBy8+CowQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.82.0': + resolution: {integrity: sha512-hne/V06ewhh1i0w8+l7GDNROAGCGPmyFuOwiP7YTRu0JycyStJ4785dmF8xU5p0uUwt2emvIF9vc7Xjis+cJ0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.82.0': + resolution: {integrity: sha512-aWY2xtbZf1LneW9Qsv/n2Sp8gOu74JrlQzEtj4coHX2SHFrCfhmAumaU+sI/A5nr+yoTRTSmI/pL2s6ADlNSkw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.82.0': + resolution: {integrity: sha512-Fe+TtXCXMh/5f7kWlZ2VAwsMumZWtraFlKVk1NJlL52/beGwfDE7ov+/8gVirHzWokzGu7X65hSPq0ucPDskWQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.82.0': + resolution: {integrity: sha512-6azCZ6OJudlvipNttXCCQcyeFfcJ/NvUZdSN1z8elo73kCHtyQC7WTiUcSjWYvJ1jaq9KDUyMAoAS/vNzhBomA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-arm64-musl@1.82.0': + resolution: {integrity: sha512-PLEaSD8IAIIlwW4dwOd9YaxuxeOpwiXL4J24rcnE4iNtyM5j9Q9/3+gti08oXpx0u2ygNjRDx9xjWWpQonuJEw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-ppc64-gnu@1.82.0': + resolution: {integrity: sha512-D94em/BwknNTn4vqxjHh5wb2oL566eFhArabqKIr0cNZMHOJuiraFp1A8tXpH05bbE5tqwEfLXTI0MWEGtn3Dw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-gnu@1.82.0': + resolution: {integrity: sha512-MOprxBaoYU2D4VgxXCl3ghydThWtx7Um1lL51kGYNeQ5Al7WzsH7/tqGdNtbLrIWnjq3bsm13+nz/gRIxjrOXw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-musl@1.82.0': + resolution: {integrity: sha512-5h55QsfJ/luDXZzC20k6SNOY1Az+dCP9WvntKtcUWh2JhckAdwApY2ZusaBTwLENnReXU+A2fJtSrYvZJNKNPg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-s390x-gnu@1.82.0': + resolution: {integrity: sha512-IE8NJNLlHr0CaXyGJPGVn0eTkUyoj1I2UfA8x7I4PSOYKsQ/6btVC7Pywrj5onk0cMH25r6Z38SoN3AvE5Zuog==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-gnu@1.82.0': + resolution: {integrity: sha512-XUUUxaBo9XKl+J1B9EmP1cTGQPddzeURvoGkfwh/94PGnbW+hBprDljneoI2M1jzC1bzrIV3ihc7iM9UXl8+tg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-musl@1.82.0': + resolution: {integrity: sha512-SWLSFulX9TDuH6yvbPYp4+VNn6jkkIvvI+KiujDM5rWBRHEfkesCC/pCneIIUr6ovkxZ5fRtpi2v5Cz5FrMJZg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/binding-openharmony-arm64@1.82.0': + resolution: {integrity: sha512-BQy35f6ZUdNr9a6c7B7orxQTcLjByGT2z3WAgmRovpRwmPYAaJ+NTplmMzhdjdJ4qSchfMNZy/Ukg+qRg6zseQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.82.0': + resolution: {integrity: sha512-V4QhSTg5gctZue8RJjsGi7NpQPThr/p1/HfmiMC5kfe1KFEup9SQRVub4A6kijQjdHfxj7bLL1KO3QO7/5bwMQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.82.0': + resolution: {integrity: sha512-TUSCLaKB2yktpFAJ/r3HAUYsaV/3DT7JS4iNKyoh3a9YNwD0UG7Ezh4D8m23654vQcU6P/RQrCAjRPKe4peP/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.82.0': + resolution: {integrity: sha512-VTVoRIWJTb+wvUX8EYoPArfFH02whuR10goFXE/LHRRX33ajRrFgqbcONXZMiF4C5rnattfkm87HqYn8jb8hmQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@parcel/watcher-android-arm64@2.6.0': + resolution: {integrity: sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.6.0': + resolution: {integrity: sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.6.0': + resolution: {integrity: sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.6.0': + resolution: {integrity: sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.6.0': + resolution: {integrity: sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.6.0': + resolution: {integrity: sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.6.0': + resolution: {integrity: sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.6.0': + resolution: {integrity: sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.6.0': + resolution: {integrity: sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.6.0': + resolution: {integrity: sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-win32-arm64@2.6.0': + resolution: {integrity: sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-x64@2.6.0': + resolution: {integrity: sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.6.0': + resolution: {integrity: sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==} + engines: {node: '>= 10.0.0'} + + '@quansync/fs@1.0.0': + resolution: {integrity: sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ==} + + '@rolldown/binding-android-arm-eabi@1.2.8': + resolution: {integrity: sha512-tN5aztYkKCte4i5SIrrz5yK/HMjEuCqCSCJa418jOV8tZ1cBY3YF2otxB1ktPxzsLA1BeTqwapK0bfjxNvHJVw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.8': + resolution: {integrity: sha512-dIYTWl9XprMUiQFoc55KUyk/oS8SKYH3zFl0LTR7RT0Xj4hgSVyuJcroH8JUu8RcpF8fTB6E0aOwCkZoYPcDSQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.8': + resolution: {integrity: sha512-PCSDQGXD2IyTEFrcgPyBM8jJuGmrbCMuoIOXdbEGVemruKACXoLQJrb+A45Z0L5t1RQkdfJprAYPkikbh7dzdA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.8': + resolution: {integrity: sha512-Uk7lRsGhPFHVX/sAUC6D5H9Ol30dFHd6iquokll2th3LpdJ3F5CzQB+7DHn0Ri2mG+U7k2zXiPHDrwZenXhwSA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.8': + resolution: {integrity: sha512-DjszaTEVogPqA5bYzsEeqDCQxbcp2fexQwKcRspYji2yzR68fCf+e4fx6kBSRDwX5/brZaHw/hWS9+A/+/w9sQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.8': + resolution: {integrity: sha512-zmwa7FTmdzB6aaEEuuls18H6Ap5JmJPSoPTuXixeJZV6tG40SyLkApQtz1g8ptZtiEKqj9OM0oNLPh1AgvE31Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.8': + resolution: {integrity: sha512-KdYQDPHwJVnbFwdTGMgxsI9SqblBlz6STGM+w1We/d5B8OWWidYH0MwkU/uA1wM5fIpO2MkOVxXrNzzuZhw9ew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.2.8': + resolution: {integrity: sha512-jFJTifHnNPY+yzOoNZQfSIysrVyXzEQPhPnOUjmD1bcQGHH6s7c8cViKWar8YplQImE5N9JRqMCLrM2CdxOrZA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.2.8': + resolution: {integrity: sha512-FhiOziBDWPBjbcmRzfLyIJnaP7AVMFXT7YCXPjXxj7wKU3vx24RjrCNN/zjvVa+N2vVoHJwCoUBvsrN/DG3zIA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.2.8': + resolution: {integrity: sha512-WnHfADMzOV2Y55wlx1hzzQnar/wDt/VdvWSD99r18Mz9ylNieIGOkRx3UV21h7m/eJvjySYJkO26VvGNFkwsIQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.2.8': + resolution: {integrity: sha512-H9tRr5ibfXFVLxbPOseVewewFpl28zcEdjRDt2FTUZU7odxP0gEv1ki4/kGmcGOh78oRwZuuQllGLZ9zTJp84g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.2.8': + resolution: {integrity: sha512-UefiqfM3D6IVNlZ8tSGs9+Ejjud2T+oxO0IHADU45Y+lyEjD2dVFyZHbkfX0LUb5Zugo/oIv1eCO/KVYhgYJYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.2.8': + resolution: {integrity: sha512-637Ke4kWSy6rp9cxQ9gMOXlxPgIw/c1beASV4M//3+9I4uwBVOOl74G+e3zyU3u19U7RkRl/HuewixZ/Z6+Rjg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.8': + resolution: {integrity: sha512-xWBkPOF1Q9k/Gv1nQXnVdLxKu74jXppuOM4Z3mnypVUJJJwLsMl7hNJGRAUJoG8A5MgOI1ACKM+wBFxSJzKy4A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.8': + resolution: {integrity: sha512-uz2ZvfgXbxqNwijjjbxrnvALwpyODDcgc1T1N8N3rf/DXKQmaFwmB4LX4yyjggpwN2obdQLb2rgirX5ffCWYng==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@shikijs/core@4.4.3': + resolution: {integrity: sha512-QCR4q2ZO/ILJEuwiBMel4wdcTDb1JGwfjKTxPDF6x8ixOaluPrVqIn06C99AcRPhmYlBR56d/Fb+GN58GzExpg==} + engines: {node: '>=20'} + + '@shikijs/engine-javascript@4.4.3': + resolution: {integrity: sha512-FbOjFJp9VLdo1Wevs10BBtVxiTWwNLqZh5Gkhjgda/ioL15YOgeSl9n+6XMa3qRlPQzfhFNe641SrynFHYG0nQ==} + engines: {node: '>=20'} + + '@shikijs/engine-oniguruma@4.4.3': + resolution: {integrity: sha512-EcOQkxdxGQrc1Row/cC2c96/v1dbZqGnEVu1qTuT/MJmp6+cXCvQussowVmCv5Tqr3KuY3c7IbM6HTW3LJ1k9w==} + engines: {node: '>=20'} + + '@shikijs/langs@4.4.3': + resolution: {integrity: sha512-ePic0yfAJGOF83D5wBHK/00EjK65oahBYxFk5epgq33WRv7X9UuxLEV8PtR0szC0z8dl7INIpIodB99JRFlR+A==} + engines: {node: '>=20'} + + '@shikijs/primitive@4.4.3': + resolution: {integrity: sha512-m0wBeLDQDeIxRdUmrCPdQqfuUamDwRL5isCfYbguKD6NiaKpVbsv+3J81DyIKgNW5h4WAIIr8T4EkgQrBBxvaQ==} + engines: {node: '>=20'} + + '@shikijs/themes@4.4.3': + resolution: {integrity: sha512-w8UHjeUnIR965KMWJHUPXOc2mNJUnK3vpVLYLvw5IYU2mnTTJ89E24OrJDBNiJDQ0qzb0tc4l7mrIXx5cFeIyw==} + engines: {node: '>=20'} + + '@shikijs/types@4.4.3': + resolution: {integrity: sha512-UEJxmRR++MAGR6hugn0vgVS2W/6lWAts84FFSrnlH9sP0LNol7E5+NQ792pH8liWUhyMyjhTgSUH3k7iD7tc5g==} + engines: {node: '>=20'} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@sxzz/popperjs-es@2.11.8': + resolution: {integrity: sha512-wOwESXvvED3S8xBmcPWHs2dUuzrE4XiZeFu7e1hROIJkm02a49N120pmOXxY33sBb6hArItm5W5tcg1cBtV+HQ==} + + '@transloadit/prettier-bytes@0.3.5': + resolution: {integrity: sha512-xF4A3d/ZyX2LJWeQZREZQw+qFX4TGQ8bGVP97OLRt6sPO6T0TNHBFTuRHOJh7RNmYOBmQ9MHxpolD9bXihpuVA==} + + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/event-emitter@0.3.5': + resolution: {integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.25': + resolution: {integrity: sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/node@26.1.1': + resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} + + '@types/nprogress@0.2.3': + resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} + + '@types/retry@0.12.2': + resolution: {integrity: sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + + '@ungap/structured-clone@1.4.0': + resolution: {integrity: sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==} + + '@unocss/cli@66.10.1': + resolution: {integrity: sha512-rWsCIr0xKtaaIoOlmdeUcTnFf6bksAb7qgL1j+N3TGQ+HlT6U9E0FZ2nV7Rnr1CwcxKvbwQOsxJUXIGDo/r7BA==} + hasBin: true + + '@unocss/config@66.10.1': + resolution: {integrity: sha512-dLiJnQHbocFyTXkVhOl8IxpAApOWAeoWAmTgx0JUEyXKr93N0aoHESq2V626NfPNTps9aYE8lvyhcG/P2vkf7w==} + + '@unocss/core@66.10.1': + resolution: {integrity: sha512-kPDmL/g26gBhh4jnu99n0lg+f42+24s8ssXNvwEYEX8indWCPs5gD9HS5L08BHcOsiawmntYWtR3c4D6ql0e9w==} + + '@unocss/extractor-arbitrary-variants@66.10.1': + resolution: {integrity: sha512-hrfEaF22mN1M/aExZtlg/va0gFuu9Qo20MyErzn6yONJzNtWGptzNhXHFsXp6Fb5eI14C/XfEcD4eMp6MSo9+g==} + + '@unocss/inspector@66.10.1': + resolution: {integrity: sha512-EMWaUaN0TwP3I0f5ykDOUw0WB1jhW+aB5wc48UFpF7aJyIBnt+Hl8/lMg2BfEgrCnjTTBbzzsJA+TX0qKUO6rg==} + + '@unocss/preset-attributify@66.10.1': + resolution: {integrity: sha512-BiJQ3mX/EAnFMTcFdUEbMvPiDERl/FTGLjtZrlhyG8ut8jYfYKmkZGjq9MPz9bIpE0xdfgD92jcbxcsKFsL1Zw==} + + '@unocss/preset-icons@66.10.1': + resolution: {integrity: sha512-I+fpu2WKODytHHOlwYMBb616Y0n0V1lLoFnBRNqoM6l9B8DDPg1D8Rsi0YlDK2g07TBQis8yRhC0P+zPZt3d1Q==} + + '@unocss/preset-mini@66.10.1': + resolution: {integrity: sha512-1X52DckWsbkdjX68w+rWGCJBWrQECDGfgpfkJEUS8lmBDJent7peFfOsy9/54sTmwgLLsXpS2ZViVl+RK5TwaQ==} + + '@unocss/preset-tagify@66.10.1': + resolution: {integrity: sha512-VXEVLy0BR9TjCKwrv+TM9FzWIodkcSFeNyVA/ISDVZGjq/Fi+HyC47mkaInI5fiNlSWRzQtKR1wvkYQxS2F1Sg==} + + '@unocss/preset-typography@66.10.1': + resolution: {integrity: sha512-HNsKl8P11VenFPgHZPrzJTyHpeBJe/xloD61n0sBRdsoDxBO8L+JbsM5RfYigWmlNDDmlq5biIx4lzehoKIRrQ==} + + '@unocss/preset-uno@66.10.1': + resolution: {integrity: sha512-8UO/xdysBNz9XZpB2W1x5AK7KYHFFpD9rdoYr10FjscZ4DIlxp6DPhdsu7QITwajfeCNF2VXafXReYNyrdEEFg==} + + '@unocss/preset-web-fonts@66.10.1': + resolution: {integrity: sha512-rl80dnjL1i7O6+nkTuEcx4AzlJ+p93FCU6tvP7b55v1N7IESxONw6lJhQiafPEwM60Typ7o0rCwnU7B8Yvi3yA==} + + '@unocss/preset-wind3@66.10.1': + resolution: {integrity: sha512-LV5b+xZoONFfzCkoEiA8TrsPRHZUkRU8egCDXm57kgVE2PacynwFzK7mIFuYPzyAxJ3nGw4mExjRSLg9n5LK1Q==} + + '@unocss/preset-wind4@66.10.1': + resolution: {integrity: sha512-c9unFTgfctd2TuuwIRbDHdIFLigbWp1GcEHKV7uTic/jq1t051Uil6byu5d4d6Qg2K88AD1NbK1m0+FWdVebcg==} + + '@unocss/preset-wind@66.10.1': + resolution: {integrity: sha512-C3AZN5tZCdGISs8De0JSLQeNych9JZ8QL1LEdubjZ1PxDkynGCAY5fid5QpUxU5FeUwnFftsO9Sp3Hoj7rL1VA==} + + '@unocss/rollup@66.10.1': + resolution: {integrity: sha512-fA97Hdm91zp3RRYhs33ubWi/hxEiYiSibRpk1k7h9fbmJXnOwRpJQggwX118VEUjWQjooE+q+6gp9oSWxdbqeQ==} + peerDependencies: + rolldown: ^1.0.0 + rollup: ^4.0.0 + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + '@unocss/rule-utils@66.10.1': + resolution: {integrity: sha512-1VcGBOP4Y5Qaog81tHU0zj22B9df9yQKQRMN8ks1KTBOcfUdlNAW27SYx6mp6n3Do7FJFLi5eBs1/U8HCA/2/A==} + + '@unocss/transformer-attributify-jsx@66.10.1': + resolution: {integrity: sha512-7sbS/OK1nLYfYqGbsCoXKELjPBulYDh7nZFbdvAiyNDTtJKiAJ1RNUmPF0mCw68255AMncxFEVtMWfXquHt0GQ==} + + '@unocss/transformer-compile-class@66.10.1': + resolution: {integrity: sha512-cc4Rndvo3/UbaXbrYc3Fq6XjeGsdBRit6XnkxQnJ/UmP6JcC4tS93N2JiMmiz/reOcc/UwCENHB8VHTwqy4EUA==} + + '@unocss/transformer-directives@66.10.1': + resolution: {integrity: sha512-3w7bLb1G5CnNMEGuwuR889aoLBm4/aOXzqBFC4s6A9yRb3jfmdq1qs804tjq4npbNTfMuKztPzenqC1UMtg6iA==} + + '@unocss/transformer-variant-group@66.10.1': + resolution: {integrity: sha512-5b/Y2wZvF2A+xfyO1DJisVnzwlmYIjlYU70PrVZzDSrvQx5nuiM+OIm9Pp8+RI5BrbAcPsTYxkZe2YgeTdN9Pg==} + + '@unocss/vite@66.10.1': + resolution: {integrity: sha512-r9xzRwbEGbez1uUmConCfKpEWKsvjF1xUePz8twk0mS4TH2zlhnwhDy/XCV6Ia4lPy+n7oRvei5AXhtHx35rXg==} + peerDependencies: + vite: ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0 + + '@uppy/companion-client@5.1.1': + resolution: {integrity: sha512-DzrOWTbIZHvtgAFXBMYHk2wD27NjpBSVhY2tEiEIUhPd2CxbFRZjHM/N3HOt3VwZEAP471QWFLlJRWPcIY3A2Q==} + peerDependencies: + '@uppy/core': ^5.1.1 + + '@uppy/core@5.2.0': + resolution: {integrity: sha512-uvfNyz4cnaplt7LYJmEZHuqOuav0tKp4a9WKJIaH6iIj7XiqYvS2J5SEByexAlUFlzefOAyjzj4Ja2dd/8aMrw==} + + '@uppy/store-default@5.0.0': + resolution: {integrity: sha512-hQtCSQ1yGiaval/wVYUWquYGDJ+bpQ7e4FhUUAsRQz1x1K+o7NBtjfp63O9I4Ks1WRoKunpkarZ+as09l02cPw==} + + '@uppy/utils@7.2.0': + resolution: {integrity: sha512-6lC246qszMv6bTyl/+QyHwrudgeguWkA94ME1wHn+a6uRAvmtAEaUManIfGqTJfoKvWAiCJqdJPl5xRJjhAloQ==} + + '@uppy/xhr-upload@5.2.0': + resolution: {integrity: sha512-3LV/X5Of6BINnKplP+CwUJ0a4/7cRFfzxwGyXnW+uCrNQHoo09dttcz3begWHejGvzenQHuUnMO3Fxyc71Pryg==} + peerDependencies: + '@uppy/core': ^5.2.0 + + '@vitejs/devtools-kit@0.7.3': + resolution: {integrity: sha512-MsYO9EJ3fzanvS4FJ6C9Dh2iiR4ynhcp5C3lJEKHRZBr8wOyTBSbCP1ic81KHnm3Q5GbGt4lKBHTq7PZfpdmbg==} + peerDependencies: + vite: '*' + + '@vitejs/plugin-vue@6.0.8': + resolution: {integrity: sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vue: ^3.2.25 + + '@vitest/expect@4.1.11': + resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} + + '@vitest/mocker@4.1.11': + resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.11': + resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} + + '@vitest/runner@4.1.11': + resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} + + '@vitest/snapshot@4.1.11': + resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} + + '@vitest/spy@4.1.11': + resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} + + '@vitest/utils@4.1.11': + resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + + '@volar/language-core@2.4.28': + resolution: {integrity: sha512-w4qhIJ8ZSitgLAkVay6AbcnC7gP3glYM3fYwKV3srj8m494E3xtrCv6E+bWviiK/8hs6e6t1ij1s2Endql7vzQ==} + + '@volar/source-map@2.4.28': + resolution: {integrity: sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==} + + '@volar/typescript@2.4.28': + resolution: {integrity: sha512-Ja6yvWrbis2QtN4ClAKreeUZPVYMARDYZl9LMEv1iQ1QdepB6wn0jTRxA9MftYmYa4DQ4k/DaSZpFPUfxl8giw==} + + '@vue-macros/common@3.1.4': + resolution: {integrity: sha512-/5Fv+6DgIcM9ajY05ZmKBv+LMX1M9A0X+IUwDRVdt67ciw8OV9bvG2r34p3RiEadlsQybjhKPRKNXDC8Bp23cw==} + engines: {node: '>=20.19.0'} + peerDependencies: + vue: ^2.7.0 || ^3.2.25 + peerDependenciesMeta: + vue: + optional: true + + '@vue/compiler-core@3.5.42': + resolution: {integrity: sha512-2Ye1ilMtKXxl8qZUrQ5j0CdgenFp/HFQmta6rfRyfEsTG69L6Wk+tWuNoHYHMx9E8tF2Slvdg1FuwDvAXdy1LQ==} + + '@vue/compiler-dom@3.5.42': + resolution: {integrity: sha512-qbhQZEFmycr+ni/qyuccS4sucNN7VAbDfbkvNxWOX2VfgFm90MNs3/UhRNKoPMEIVn0F8gdlYjLPvqxHwHeQOA==} + + '@vue/compiler-sfc@3.5.42': + resolution: {integrity: sha512-fkCAFB4okcAANGMThboWnScp/gzWjU0ZSkVnjTIiplmMDq2uq0tIB3j+xVu4rhv5rvOgBySCysudmbMd6xRRqw==} + + '@vue/compiler-ssr@3.5.42': + resolution: {integrity: sha512-xmLk3wLkbizPAiLyomjgFFosf2ys9b5Ghb+oh/k2tnvipNz8OFrQOiTcWCzyK7MpBp9KkyGtfvgfLUivbmuGYA==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/devtools-api@8.2.1': + resolution: {integrity: sha512-6u4vXBlIBAC1wMplIZgpyPn7uh/s4Bf6F5bMzvLv+EdJ0aHs/+4B7Ygv864EStQSjRbsRzTko/kUG1A1IejQ3A==} + + '@vue/devtools-kit@8.2.1': + resolution: {integrity: sha512-FIGIuq3AWReEpbAHY/cRGeHDfI0qOb8OCQ3YjbEAX04uaxIDbGc9rhkbVcG7rnfHPXE3RsU5KrWOu9V/okd8AQ==} + + '@vue/devtools-shared@8.2.1': + resolution: {integrity: sha512-Fkac7lUdGReh6pVOi3AYPRGe82LQqRmAfThW7RRligOAP0ZA/Z1z9XLHDM9dv34pV2HRc79DK8uKPeG2fLnA/g==} + + '@vue/language-core@3.3.11': + resolution: {integrity: sha512-QJmpliwAVpC/OxubIByPAhNzsQPRc8/gxlN2qnVzVfIMjMDz/9RnXRFoetjz5yEgXVXyp4LqhXq3V53PjmNzFw==} + + '@vue/reactivity@3.5.42': + resolution: {integrity: sha512-TzNNfKpb7hDxbQltwAut8VDQA5YP+BuRlxntHUuRjyKwlMvmAPbs3unhCvieijifY6vFfVBwsS7wG/C7uq+bEQ==} + + '@vue/runtime-core@3.5.42': + resolution: {integrity: sha512-9uACtuHs7vJGkm5Bp3xu4xRDLFTIYy5DgxpToVjqGIAhAEKwQfsaLvKINhM6nFVp6bZPRFGdDqd1g52MqKsotA==} + + '@vue/runtime-dom@3.5.42': + resolution: {integrity: sha512-rsCmhiWLaRxGltLwhlCWyYkFn7WAbKRh0q17eZ1A6Dq6eqc2ACQ61IIryxz0LrsvCzHSilLA9JHovVwM8CNE2g==} + + '@vue/server-renderer@3.5.42': + resolution: {integrity: sha512-2++5dUyYS4gvo7xQXSECUDhB7TS0aOl5SeVfC5qSq1Jgfhjvegw1zqhwTIR3imZ+QYPJQw9gfcFvXGAjGZ7ajQ==} + + '@vue/shared@3.5.42': + resolution: {integrity: sha512-2rPxex1jQf4jvl9MOHl6YaXCPcrNqz/FstMOEh3QWY+/OME9nQTvl9WYeCwhW7AFjaR0SnngZGlp/wkR6rkI6g==} + + '@vueuse/core@14.4.0': + resolution: {integrity: sha512-X4WHz1HlCzCBoYXesUkifzzWBAcZgXG8Fi5iNPQg/epdzOB3gu8Fawj3hvuwYR1nGcXGnvxwYYcUC/71++svtQ==} + peerDependencies: + vue: ^3.5.0 + + '@vueuse/metadata@14.4.0': + resolution: {integrity: sha512-swx/255R6JyHZFJhx845iz5CRWDZdCfvkZOpACWc5+c5WHcG24mv8gUT1WIdFQaHt6dq79rvILd9QnCWiyVm9g==} + + '@vueuse/shared@14.4.0': + resolution: {integrity: sha512-JRgY90Sz8DDtPMsaDflvPMp9xYk69JZAmbuDvAquUVXKr2gEjqtzGNTTthLfckH0BzBqvnu31gb4a8TGLRe79g==} + peerDependencies: + vue: ^3.5.0 + + '@vxe-ui/core@4.4.21': + resolution: {integrity: sha512-xaKGhQxrSU3G7+cQbxMBSzNXiWliRDgtR5GmD/FD9f5tl8e9SuZLe2SfODjItp6oBvBkKI2TXBZiCx502WftyA==} + peerDependencies: + vue: ^3.2.0 + + '@wangeditor-next/basic-modules@6.4.1': + resolution: {integrity: sha512-a7/0/Qw+2W5Mk+V24WFr9KtgxH6VA43eCcVuZCmLNw3YRiVMmVBncm8OahHPLeyq+IuDmJqwNptwcRJAjE/n2Q==} + peerDependencies: + '@wangeditor-next/core': ^6.4.1 + dom7: ^3.0.0 || ^4.0.0 + lodash.throttle: ^4.1.1 + nanoid: ^5.0.0 + slate: ^0.124.0 + snabbdom: ^3.6.0 + + '@wangeditor-next/code-highlight@6.4.1': + resolution: {integrity: sha512-02hEl1j/8pqKjQ6fbMnAfyLLKdIgDhZkYWMTERgWRGMRJqTj6sbv+OYUDWfLVaMrKwqom6/OSYqZKWLxdA8XzQ==} + peerDependencies: + '@wangeditor-next/core': ^6.4.1 + dom7: ^3.0.0 || ^4.0.0 + slate: ^0.124.0 + snabbdom: ^3.6.0 + + '@wangeditor-next/core@6.4.1': + resolution: {integrity: sha512-V61N0j7M8PL3vfpV4PUvZrS9faJDy7AQgXJYaawabjEF+o0eHhniIkOfUQnUxSYWD4aKW+7FzKBvEN6vrri6QQ==} + peerDependencies: + '@uppy/core': ^2.1.1 || ^5.0.0 + '@uppy/xhr-upload': ^2.0.3 || ^5.0.0 + dom7: ^3.0.0 || ^4.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.throttle: ^4.1.1 + lodash.toarray: ^4.4.0 + nanoid: ^5.0.0 + slate: ^0.124.0 + snabbdom: ^3.6.0 + + '@wangeditor-next/editor-for-vue@6.4.1': + resolution: {integrity: sha512-PD89sxsmsEQ/lVizFXO+KOjldpZVF9Spf67nryt0J3fModwCfG4vT6Med1OW4fPFJCIe8FJ9Sn0dHcYjXyAH0w==} + peerDependencies: + '@wangeditor-next/editor': ^6.4.1 + vue: ^3.0.5 + + '@wangeditor-next/editor@6.4.1': + resolution: {integrity: sha512-kgurPViqVAmawzexdBBVq0otdlIJNblxOCk0451Qgk+syB0DFcD2c+TVMU7NQFTCjoLYr6nX8j6fTfx88VXoUA==} + + '@wangeditor-next/list-module@6.4.1': + resolution: {integrity: sha512-8m2GCW1Id6NGSdsMTHcyaoUd18Wyq4G9cuoMG0paRErXSwsEjpT6zOoJqWlVEMAjaQvLqFlNGWzGTjK2Cmrk3w==} + peerDependencies: + '@wangeditor-next/core': ^6.4.1 + dom7: ^3.0.0 || ^4.0.0 + slate: ^0.124.0 + snabbdom: ^3.6.0 + + '@wangeditor-next/table-module@6.4.1': + resolution: {integrity: sha512-pWLm+BI/LJzj5JZslu6IRq5RYIj4L3Wakxuw/IdT3DSh4gVCgQs0EvLPbfYeDCQ7oT6zSzGzl5R3WLSBtsenLQ==} + peerDependencies: + '@wangeditor-next/core': ^6.4.1 + dom7: ^3.0.0 || ^4.0.0 + lodash.debounce: ^4.0.8 + lodash.throttle: ^4.1.1 + nanoid: ^5.0.0 + slate: ^0.124.0 + snabbdom: ^3.6.0 + + '@wangeditor-next/upload-image-module@6.4.1': + resolution: {integrity: sha512-2ny//hrTyHR/BodzzZOplJXIyz9fYrSDelgIaQyhdImeNNu9mguvIIZVRIt2gEJs2yG0vSHKVCNUEJI7sqVDbw==} + peerDependencies: + '@uppy/core': ^2.0.3 || ^5.0.0 + '@uppy/xhr-upload': ^2.0.3 || ^5.0.0 + '@wangeditor-next/basic-modules': ^6.4.1 + '@wangeditor-next/core': ^6.4.1 + dom7: ^3.0.0 || ^4.0.0 + lodash.foreach: ^4.5.0 + slate: ^0.124.0 + snabbdom: ^3.6.0 + + '@wangeditor-next/video-module@6.4.1': + resolution: {integrity: sha512-XjPAZbdO7wInZO1ZXXOw+L5SqY5yInI4H2gvb8+7rgNU5Ar2YXL3IE2A3+aW3c8aaSXwvIJfKKSuLZZKeZN22A==} + peerDependencies: + '@uppy/core': ^2.1.4 || ^5.0.0 + '@uppy/xhr-upload': ^2.0.7 || ^5.0.0 + '@wangeditor-next/core': ^6.4.1 + dom7: ^3.0.0 || ^4.0.0 + nanoid: ^5.0.0 + slate: ^0.124.0 + snabbdom: ^3.6.0 + + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + alien-signals@3.2.1: + resolution: {integrity: sha512-I8FjmltrfnDFoZedi5CG8DghVYNhzb/Ijluz7tCSJH0xpd0484Kowhbb1XDYOxfJpU1p5wnM2X54dA+IfGyD1g==} + + animate.css@4.1.1: + resolution: {integrity: sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==} + + anynum@1.0.1: + resolution: {integrity: sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-kit@2.2.0: + resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==} + engines: {node: '>=20.19.0'} + + ast-walker-scope@0.9.0: + resolution: {integrity: sha512-IJdzo2vLiElBxKzwS36VsCue/62d6IdWjnPB2v3nuPKeWGynp6FF/CYoLa5i/3jXH/z97ZDdsXz6abpgM6w07A==} + engines: {node: '>=20.19.0'} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + autoprefixer@10.5.5: + resolution: {integrity: sha512-uiRYvQYe/nNSzBJ7OUnd2/TZVsAdob3blml44teEpee9Cc1f4rGZFewO+JT3Wo8mgFOSzNqes4FHZn/Qz8WOuw==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + await-to-js@3.0.0: + resolution: {integrity: sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==} + engines: {node: '>=6.0.0'} + + axios@1.20.0: + resolution: {integrity: sha512-r8aOh8j9cGKpgQAqpzrUHnSIc6a59Y3Xf/cv8sy1DrHCkZHzQGEuoq1tARk6qSyDdtQGSDgpb9kFlruzPvrgwg==} + + baseline-browser-mapping@2.11.21: + resolution: {integrity: sha512-uh8vpY/1/YyFkunIDFH/12p7/7VdPKA1hejMVEbdkEaWnUz0Hesvx5EbiU6XxjyHZIOju+ZMbQJkRh+es3/spQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + birpc@2.9.0: + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} + + birpc@4.2.0: + resolution: {integrity: sha512-KxgKcZPfrtzJDDALHPguGpGJUrzdgpymyiQQgzFjWreHMOpWrnFNVREr5J48x2DBh8ZVioscrV1SBkDipGiX+Q==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + browserslist@4.28.9: + resolution: {integrity: sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + caniuse-lite@1.0.30001810: + resolution: {integrity: sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + + citty@0.2.2: + resolution: {integrity: sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==} + + 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'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.4: + resolution: {integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==} + + confbox@0.3.1: + resolution: {integrity: sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + crossws@0.4.12: + resolution: {integrity: sha512-aypfsr6t0uNvkqaZc6zvBfXzC6pLI0/sIulpkV6RwCVtZqG5ebBzv4weImKK0VNCj91Wl9F5j7p5WU4MNrybng==} + peerDependencies: + srvx: '>=0.11.5' + peerDependenciesMeta: + srvx: + optional: true + + css-select@6.0.0: + resolution: {integrity: sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@7.0.0: + resolution: {integrity: sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==} + engines: {node: '>= 6'} + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + + dayjs@1.11.23: + resolution: {integrity: sha512-QDTCU0M0MxR3hQfnlDJfwekQiaanm1ubOD231u73WBckQ/fsamwRLiE2GBz6D3a/xF1NgfiDLJjXBa1hYOYTtQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + defu@6.1.7: + resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devframe@0.9.18: + resolution: {integrity: sha512-SjvL9/MiqaG3m2bpfAb8fMsX6ALcyqFNIZ3VYjhZ/BZxSqkTknt3U6YcQTEAZOpFvGv+MVk6OeWkRW0IhzVxAg==} + hasBin: true + peerDependencies: + '@modelcontextprotocol/client': ^2.0.0 + cac: ^7.0.0 + peerDependenciesMeta: + '@modelcontextprotocol/client': + optional: true + cac: + optional: true + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + dom-zindex@1.0.7: + resolution: {integrity: sha512-cKU/h8v8IPBgdZOTPbPmq3Ib+Ac5C+kKoh9I4LbGR9BM3GwbmB16KYWKJcj5M2BavnA66EbgYzxYDLd1IytnlQ==} + + dom7@4.0.6: + resolution: {integrity: sha512-emjdpPLhpNubapLFdjNL9tP06Sr+GZkrIHEXLWvOGsytACUrkbeIdjO5g77m00BrHTznnlcNqgmn7pCN192TBA==} + + 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==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + echarts@6.1.0: + resolution: {integrity: sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==} + + electron-to-chromium@1.5.425: + resolution: {integrity: sha512-QvPtl41EUOnuT1HBvMKgxXRIaHNcagBPs50u7VULzhZXaGfqTbZyE16LQsctZ/RQHlGu+FOWeDTR4mY6YbeF1g==} + + element-plus@2.14.5: + resolution: {integrity: sha512-bghYy/S+qg87enHPXELirhEdDqsVAUGcGpbGIeG8dz0kwpIkGz7gYsifulBshXX74iRtHib85XWQj0uSH2A1Yg==} + peerDependencies: + vue: ^3.3.7 + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@7.0.1: + resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==} + engines: {node: '>=0.12'} + + 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@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + 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'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + exsolve@1.1.0: + resolution: {integrity: sha512-D+42+T12DdIlJM3uepa55qGiL3sYdLBOxIl2ifQCzCHz4c7eiolaHsi3BIqEr7JxBzxv2pYZQX9kw16ziMcEmw==} + + exsolve@1.1.1: + resolution: {integrity: sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + fast-xml-builder@1.3.1: + resolution: {integrity: sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==} + + fast-xml-parser@5.11.1: + resolution: {integrity: sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==} + hasBin: true + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + 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==} + + 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'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + h3@2.0.1-rc.31: + resolution: {integrity: sha512-AG7qZzF99a0BSYoXT3evJgIhwSIUKow7R+hwkLRZS06WJ9nbSb7QXUDgs1ByBjp6svTvl++N/GKA7I9YPz9cQQ==} + engines: {node: '>=20.11.1'} + hasBin: true + peerDependencies: + crossws: ^0.4.12 + ocache: '>=0.3.0' + peerDependenciesMeta: + crossws: + optional: true + ocache: + optional: true + + 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.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + hast-util-to-html@9.0.5: + resolution: {integrity: sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + highlight.js@11.12.0: + resolution: {integrity: sha512-nbfWpyRMcMrPMmDwJB+dhX/eiaPKtc2RB+0QZskqJ3WjRA/FDS0e9hZrx8EC/lbEv8gXy98FcDbNa/dspAaJMg==} + engines: {node: '>=12.0.0'} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + i18next@23.16.8: + resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==} + + image-conversion@2.1.1: + resolution: {integrity: sha512-hnMOmP7q2jxA+52FZ+wHNhg3fdFRlgfngsQH2JQHEQkafY7tj/8F15e6Rv/RxDegc872jvyaRHwMbkTZK1Cjbg==} + + immutable@5.1.9: + resolution: {integrity: sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + 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-network-error@1.3.2: + resolution: {integrity: sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==} + engines: {node: '>=16'} + + is-unsafe@2.0.2: + resolution: {integrity: sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + + jsencrypt@3.5.4: + resolution: {integrity: sha512-kNjfYEMNASxrDGsmcSQh/rUTmcoRfSUkxnAz+MMywM8jtGu+fFEZ3nJjHM58zscVnwR0fYmG9sGkTDjqUdpiwA==} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + local-pkg@1.2.1: + resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} + engines: {node: '>=14'} + + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + + 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.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + lodash.toarray@4.4.0: + resolution: {integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + magic-regexp@0.10.0: + resolution: {integrity: sha512-Uly1Bu4lO1hwHUW0CQeSWuRtzCMNO00CmXtS8N6fyvB3B979GOEEeAkiTUDsmbYLAbvpUS/Kt5c4ibosAzVyVg==} + + magic-string-ast@1.0.3: + resolution: {integrity: sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==} + engines: {node: '>=20.19.0'} + + magic-string@0.26.7: + resolution: {integrity: sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==} + engines: {node: '>=12'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magic-string@1.3.1: + resolution: {integrity: sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + 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'} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + 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==} + + namespace-emitter@2.0.1: + resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.16: + resolution: {integrity: sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==} + engines: {node: ^18 || >=20} + hasBin: true + + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-releases@2.0.55: + resolution: {integrity: sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==} + engines: {node: '>=18'} + + normalize-wheel-es@1.2.0: + resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} + + nostics@1.2.0: + resolution: {integrity: sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg==} + + nprogress@0.2.0: + resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nypm@0.6.10: + resolution: {integrity: sha512-W72Hrj1petq+b3Hk2aAC+9zswetlIFTnDW4s5djseZh2nYqBbyQLOtj472HwcbcWykPBUW1WpWpjOd4nK6gMRw==} + engines: {node: '>=18'} + hasBin: true + + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + obug@2.2.1: + resolution: {integrity: sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==} + engines: {node: '>=12.20.0'} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + oniguruma-parser@0.12.2: + resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} + + oniguruma-to-es@4.3.6: + resolution: {integrity: sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==} + + oxc-parser@0.131.0: + resolution: {integrity: sha512-SJ3/7ZPbgie8dr5Z9BI/M51zZbpXba+hRSG0MDzVwMW5CRQg2fjYE0jHGlLX4eeiibGgC/mzoDFKSDHwVZEHRQ==} + engines: {node: ^20.19.0 || >=22.12.0} + + oxc-walker@0.7.0: + resolution: {integrity: sha512-54B4KUhrzbzc4sKvKwVYm7E2PgeROpGba0/2nlNZMqfDyca+yOor5IMb4WLGBatGDT0nkzYdYuzylg7n3YfB7A==} + peerDependencies: + oxc-parser: '>=0.98.0' + + oxfmt@0.67.0: + resolution: {integrity: sha512-vV7sSiPsaO0mSxdoUdayipVDFPzW/UQ+hrezEHa20+Tx1dnMdZLSRHMT0PdS67FFbhd74M1n08asW21aLGeCrA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + oxlint@1.82.0: + resolution: {integrity: sha512-+iFM1BGw1ntYJt3QngbJmjbrGxPaKMUADOXOijpWGnYcBPq8YZnQftSS1C+pVcDYy9YxqDVJKQqQkTazTQMboQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + + p-retry@6.2.1: + resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} + engines: {node: '>=16.17'} + + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-expression-matcher@1.6.2: + resolution: {integrity: sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==} + engines: {node: '>=14.0.0'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + perfect-debounce@2.1.0: + resolution: {integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + picomatch@4.0.7: + resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} + engines: {node: '>=12'} + + pinia@4.0.3: + resolution: {integrity: sha512-XMQqpvjgG7LMqVhhFUzKLT4KEbsYbfZZ0CZU9PgdFm1O2VKBmIkykL8+SfNhOaT7sR0wT6BEgKT0YNDYWgmVRA==} + peerDependencies: + '@vue/devtools-api': ^8.1.5 + typescript: '>=5.6.0' + vue: ^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.1: + resolution: {integrity: sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==} + + pkg-types@2.3.3: + resolution: {integrity: sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.28: + resolution: {integrity: sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.29.8: + resolution: {integrity: sha512-ej2aVZ+vZ8WO7tvlQWRM9N63A0KzF9q4mWJfDUHgYaIofWY9hu74QdnQrjoPMmZi2/nZ5gN0bJCQF49xQqx09Q==} + peerDependencies: + preact-render-to-string: '>=5' + peerDependenciesMeta: + preact-render-to-string: + optional: true + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + quansync@1.0.0: + resolution: {integrity: sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==} + + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.1.0: + resolution: {integrity: sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg==} + + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + rolldown@1.2.8: + resolution: {integrity: sha512-Z67nTmhZe7anqnM/EjI392w5i/ANUinjip7QYsOyN37oayduxt3ksdX0hf5OOamkAd53BiIHfbfSzfUmzKFQqQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rou3@0.9.2: + resolution: {integrity: sha512-3SOzvaAg8rkHrXtRjpCvCvbyO5to9oOO27Z/XqHEYXfMRVSw/qMIVdmaOk9W2lcRLtR6dlqTjo9hDeJk70QBYQ==} + + sass@1.104.0: + resolution: {integrity: sha512-btHMApW2bgolvClhRW8AlQJzgI9lUB3pPSofBQQT+E46GWvf9o0TVQ13SYv5riWZVFyPN+JNz3TKW9XhBlc10w==} + engines: {node: '>=20.19.0'} + hasBin: true + + sax@1.6.1: + resolution: {integrity: sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==} + engines: {node: '>=11.0.0'} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + shiki@4.4.3: + resolution: {integrity: sha512-Mb/GvXPHBAXdgGIcnfU5L3ldpn1XcxrGkPHwqgRx17/I2XRfqlFKk2vGkHWINn1kdXvzJZeuO3is6I9KLPFm0g==} + engines: {node: '>=20'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + slate-history@0.115.0: + resolution: {integrity: sha512-QdUm9aVyQFz6JG4a84Z6Um+tJpZJmsh9bjXwTVTvYiN4rdKbqL6+/4HT94on1WYxe10Q4vY6mA6BCpoYxgF3tQ==} + peerDependencies: + slate: '>=0.114.3' + + slate@0.124.1: + resolution: {integrity: sha512-ii7DwezgvbLAyKtHBIunjTR1kzbNfYLCUKLMzJELlbTZkvHzX4DzN7HKIwcakf6dPxO6AoeT/P7kHOcyTym/hA==} + + snabbdom@3.6.4: + resolution: {integrity: sha512-VmxEfuw1/Y/eFj5VtMhYnukExpYiPkNzoo3+N3qwAOUDMl8wXgbli5ebR+j0knE3lZ/0eYskLxNcX64uy10N9w==} + engines: {node: '>=12.17.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + sourcemap-codec@1.4.8: + resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} + deprecated: Please use @jridgewell/sourcemap-codec instead + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + srvx@1.0.4: + resolution: {integrity: sha512-eZmYaxUfZSo7/8m8UdsHRJNmTLSCTPPom9d7a60vMmuVeZvwhbvflRR+00/CxTCjMOFa5+H8tgBeNDVZ+w7AAQ==} + engines: {node: '>=20.16.0'} + hasBin: true + + ssr-window@4.0.2: + resolution: {integrity: sha512-ISv/Ch+ig7SOtw7G2+qkwfVASzazUnvlDTwypdLoPoySv+6MqlOV10VwPSE6EWkGjhW50lUmghPmpYZXMu/+AQ==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-literal@4.0.0: + resolution: {integrity: sha512-PaqAvfUZKBwc/SLmNZtHmzK+v19Z4O4eS3cKPeGvbIv/U3pnyEq4Tuw3/4v/FwfM8VQaEawsyCcOQ0P+kpwWWw==} + + strnum@2.4.2: + resolution: {integrity: sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==} + + svg-icon-baker@2.1.1: + resolution: {integrity: sha512-3siRpbrViTQGZKshmyPZPVNplChXTusZqGNYlkm7al2nJBedB/ySLaC06y5oODBNMF4XDk3d2ZZVg7x390sDdA==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + + svgo@4.1.0: + resolution: {integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==} + engines: {node: '>=16'} + hasBin: true + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.3.1: + resolution: {integrity: sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@2.1.2: + resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} + engines: {node: ^20.0.0 || >=22.0.0} + + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-level-regexp@0.1.17: + resolution: {integrity: sha512-wTk4DH3cxwk196uGLK/E9pE45aLfeKJacKmcEgEOA/q5dnPGNxXt0cfYdFxb57L+sEpf1oJH4Dnx/pnRcku9jg==} + + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + unconfig-core@7.5.0: + resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} + + unconfig@7.5.0: + resolution: {integrity: sha512-oi8Qy2JV4D3UQ0PsopR28CzdQ3S/5A1zwsUwp/rosSbfhJ5z7b90bIyTwi/F7hCLD4SGcZVjDzd4XoUQcEanvA==} + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + unimport@6.5.0: + resolution: {integrity: sha512-t0KLJLRbebz3f7NrQe+vy2ObeugVeM1XqI2oeYnauddmHSglYj3U6bxOJ65IbXmOFkcAGFJK2OyGH7aqHPM6Ug==} + engines: {node: '>=18.12.0'} + peerDependencies: + oxc-parser: '*' + rolldown: ^1.0.0 + peerDependenciesMeta: + oxc-parser: + optional: true + rolldown: + optional: true + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + unocss@66.10.1: + resolution: {integrity: sha512-9PvMhWGzVDAySUzRNnevP5W+rTjL+mbHfKYjozTKwZ+z2P2ytsZ1+0ZoBuAGmBKCLHzpiEu68EsoIsH8bUSxLA==} + peerDependencies: + '@unocss/astro': 66.10.1 + '@unocss/postcss': 66.10.1 + '@unocss/webpack': 66.10.1 + peerDependenciesMeta: + '@unocss/astro': + optional: true + '@unocss/postcss': + optional: true + '@unocss/webpack': + optional: true + + unplugin-auto-import@21.1.0: + resolution: {integrity: sha512-EzrSqWIBulEqCuP7idADXH+tVKYrbwKlR5+r/lOWik0o+Ksny1kitmtryHGNSv7puzzORlcIwT/x2JStiszlHA==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@nuxt/kit': ^4.0.0 + '@vueuse/core': '*' + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@vueuse/core': + optional: true + + unplugin-utils@0.3.2: + resolution: {integrity: sha512-xVToRh2CTmLk2HnEG7ac4rl1MJTT3RFkpS8B++/SnB0kXvuaavD+n3m/vrzyWQOdJNSZQACnbz01pnppbwV5BA==} + engines: {node: '>=20.19.0'} + + unplugin-vue-components@32.1.0: + resolution: {integrity: sha512-YiUkSxuRjab18XFOrX5VsIxXzccrfmHVGsGeJgSgklb829DQmCy9E4vvDUE4tuvZZdxyFJZX0Oc4TPnnxiiMyg==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@nuxt/kit': ^3.2.2 || ^4.0.0 + vue: ^3.0.0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + + unplugin-vue-setup-extend-plus@1.0.1: + resolution: {integrity: sha512-mW2IzkyJITyspAV/LEdnEyE1CJip9jB5fCeaVv7Q6X0oJyDrOxXoB+jyet0q5pRJNjErbjQx950/8NPTvbqLTQ==} + + unplugin@1.16.1: + resolution: {integrity: sha512-4/u/j4FrCKdi17jaxuJA0jClGxB1AvU2hw/IuayPc4ay1XGaJs/rbb4v5WKwAjNifjmXK9PIFyuPiaK8azyR9w==} + engines: {node: '>=14.0.0'} + + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} + engines: {node: '>=18.12.0'} + + unplugin@3.3.0: + resolution: {integrity: sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@farmfe/core': '*' + '@rspack/core': '*' + bun-types-no-globals: '*' + esbuild: 0.28.1 + rolldown: '*' + rollup: '*' + unloader: '*' + vite: '*' + webpack: '*' + peerDependenciesMeta: + '@farmfe/core': + optional: true + '@rspack/core': + optional: true + bun-types-no-globals: + optional: true + esbuild: + optional: true + rolldown: + optional: true + rollup: + optional: true + unloader: + optional: true + vite: + optional: true + webpack: + optional: true + + update-browserslist-db@1.3.2: + resolution: {integrity: sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite-plugin-svg-icons-ng@1.9.3: + resolution: {integrity: sha512-hrMOB7jx/xjtKgC2oZbz/HCOD0gjFEpowZmzeOn10W3Jt6XQfk3YEHZteuNEWFhvwYF7y1GPjQCQrCuqXC7MnQ==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + peerDependencies: + vite: '>=5.0.0' + + vite@8.2.2: + resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 || ^0.5.0 + esbuild: 0.28.1 + jiti: '>=1.21.0' + less: ^4.0.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 + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.11: + resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.11 + '@vitest/browser-preview': 4.1.11 + '@vitest/browser-webdriverio': 4.1.11 + '@vitest/coverage-istanbul': 4.1.11 + '@vitest/coverage-v8': 4.1.11 + '@vitest/ui': 4.1.11 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vscode-uri@3.2.0: + resolution: {integrity: sha512-m2gXo3bn0G1kT9InzMf07fTbqMbGtyckj3bH5ktLO+1Ssv+yiATZ4dhwaQv9UZWxJh6E9IFGnQyjgWVDWVBDrg==} + + vue-component-type-helpers@3.3.11: + resolution: {integrity: sha512-LwcxzeliO9fkQcpJG0PoX8X5kmAhKmH9wkpDLxNabwzkQ9Zeib2YVHwFV4pcWmMLfXVfjr/dSV+DaJ3cIPgSNA==} + + vue-cropper@1.1.4: + resolution: {integrity: sha512-5m98vBsCEI9rbS4JxELxXidtAui3qNyTHLHg67Qbn7g8cg+E6LcnC+hh3SM/p94x6mFh6KRxT1ttnta+wCYqWA==} + + vue-i18n@11.4.10: + resolution: {integrity: sha512-Lp+BjOxqzOY87DS6Z8KrQrpiTr9IN/Lt4kZEilwyXG2Wrx+AcU6IVsAW92HNXtVcn1HFFPV6ty41p9e/qDpyvg==} + engines: {node: '>= 22'} + peerDependencies: + vue: ^3.0.0 + + vue-json-pretty@2.6.0: + resolution: {integrity: sha512-glz1aBVS35EO8+S9agIl3WOQaW2cJZW192UVKTuGmryx01ZvOVWc4pR3t+5UcyY4jdOfBUgVHjcpRpcnjRhCAg==} + engines: {node: '>= 10.0.0', npm: '>= 5.0.0'} + peerDependencies: + vue: '>=3.0.0' + + vue-router@5.3.1: + resolution: {integrity: sha512-GDBZzgmILxA/kFnkFbJjQZdZ2QQbngnIMMuoUcjhZIfH1RGMaPjPwX5ASnV38qamuA9uhO0RDjSBHTDNG2uXyQ==} + peerDependencies: + '@pinia/colada': '>=0.21.2' + '@vue/compiler-sfc': ^3.5.34 || ^4.0.0 + pinia: ^3.0.4 || ^4.0.2 + vite: ^7.3.0 || ^8.0.0 + vue: ^3.5.34 || ^4.0.0 + peerDependenciesMeta: + '@pinia/colada': + optional: true + '@vue/compiler-sfc': + optional: true + pinia: + optional: true + vite: + optional: true + + vue-tsc@3.3.11: + resolution: {integrity: sha512-gOb0B9rtU2+f1dszwPqSH5kAieIF9ReeLhD3kSRNHv5WZZUQz/JdVXW0RTdqhNTMlQkqKzrTTviqKr/4FYZraQ==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue-types@7.0.0: + resolution: {integrity: sha512-4IeYqlOH6Vhw6+gbJxXV4ysxE70nUO0gwbl0xLJFzzTqyR00qLVhHY+w7f44gmT/enRXm9IlB0nEMq8xTjm7rw==} + engines: {node: '>=14.0.0'} + peerDependencies: + vue: ^3.0.0 + peerDependenciesMeta: + vue: + optional: true + + vue@3.5.42: + resolution: {integrity: sha512-4RyHQTbQvOPs3MfvUO1Sg0YRrKNnA0mAVtvpd12Tg1fKDN7OHBUl1IqSn8zGJjK9nI3NkNp8cgTpVrSZC5TTcA==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vxe-pc-ui@4.18.9: + resolution: {integrity: sha512-TJk788bY4Ok2LIBWB3JakNNuq3lirigVhyRgvNw9c8CdkWn98fSgqkncijMtx9qgDZssgsvPF61YMEq9jCJ+zQ==} + + vxe-table@4.21.9: + resolution: {integrity: sha512-6CdA+GzjHzNOXQeVG2xbL7xiiBFW7nVCgmWfsSI9cpxTrBG7bp7uRWJtI7CbFeBh/bUNGahcHHCuVLyfLY6r/g==} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wildcard@1.1.2: + resolution: {integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==} + + xe-utils@4.0.13: + resolution: {integrity: sha512-hIxQ3LQv/WF0DyeNsZ7VclX5+n5CKoUJ3DG76GFtuAnVQ/NaSv3MKmyZ6MD73wfSdzZBcziV8wcas8GRNSYMwg==} + + xml-naming@0.3.0: + resolution: {integrity: sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==} + engines: {node: '>=16.0.0'} + + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + + zigpty@0.2.1: + resolution: {integrity: sha512-MR9JqJx2wf5f4wz8zpx050AlqrmWeIW+1h0SO5iEyhG3HFRjY5luC3szS2ux2EGuPjE5OU9ZAuiCBeMWBTrqZw==} + + zod@4.6.1: + resolution: {integrity: sha512-341aRWQsve0rvronKNTqZpjmzdbUDlFuzHaI/XLg/Ej82qffDJRRfBTCuv7+9q/rMjB6LSLyEBnW4InJeMtt/Q==} + + zrender@6.1.0: + resolution: {integrity: sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@antfu/install-pkg@2.0.1': + dependencies: + package-manager-detector: 1.8.0 + tinyexec: 1.3.1 + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.8': + dependencies: + '@babel/types': 7.29.8 + + '@babel/runtime@7.29.7': {} + + '@babel/types@7.29.8': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@ctrl/tinycolor@4.2.1': {} + + '@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4))': + dependencies: + '@standard-schema/spec': 1.1.0 + destr: 2.0.5 + devframe: 0.9.18(cac@7.0.0)(srvx@1.0.4) + h3: 2.0.1-rc.31(crossws@0.4.12(srvx@1.0.4)) + pathe: 2.0.3 + perfect-debounce: 2.1.0 + tinyexec: 1.3.1 + ufo: 1.6.4 + zigpty: 0.2.1 + transitivePeerDependencies: + - crossws + - ocache + + '@devframes/json-render@0.9.18(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4))': + dependencies: + '@json-render/core': 0.20.0(zod@4.6.1) + '@standard-schema/spec': 1.1.0 + devframe: 0.9.18(cac@7.0.0)(srvx@1.0.4) + zod: 4.6.1 + optionalDependencies: + '@devframes/hub': 0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)) + + '@devframes/service-shiki@0.9.18(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4))': + dependencies: + devframe: 0.9.18(cac@7.0.0)(srvx@1.0.4) + shiki: 4.4.3 + + '@devframes/vite@0.9.18(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4))(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))': + dependencies: + devframe: 0.9.18(cac@7.0.0)(srvx@1.0.4) + pathe: 2.0.3 + optionalDependencies: + '@devframes/hub': 0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)) + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + + '@element-plus/icons-vue@2.3.2(vue@3.5.42(typescript@6.0.3))': + dependencies: + vue: 3.5.42(typescript@6.0.3) + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@floating-ui/core@1.8.0': + dependencies: + '@floating-ui/utils': 0.2.12 + + '@floating-ui/dom@1.8.0': + dependencies: + '@floating-ui/core': 1.8.0 + '@floating-ui/utils': 0.2.12 + + '@floating-ui/utils@0.2.12': {} + + '@highlightjs/vue-plugin@2.1.2(highlight.js@11.12.0)(vue@3.5.42(typescript@6.0.3))': + dependencies: + highlight.js: 11.12.0 + vue: 3.5.42(typescript@6.0.3) + + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.1.7': + dependencies: + '@antfu/install-pkg': 2.0.1 + '@iconify/types': 2.0.0 + import-meta-resolve: 4.2.0 + + '@iconify/vue@5.0.1(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.5.42(typescript@6.0.3) + + '@intlify/core-base@11.4.10': + dependencies: + '@intlify/devtools-types': 11.4.10 + '@intlify/message-compiler': 11.4.10 + '@intlify/shared': 11.4.10 + + '@intlify/devtools-types@11.4.10': + dependencies: + '@intlify/core-base': 11.4.10 + '@intlify/shared': 11.4.10 + + '@intlify/message-compiler@11.4.10': + dependencies: + '@intlify/shared': 11.4.10 + source-map-js: 1.2.1 + + '@intlify/shared@11.4.10': {} + + '@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/sourcemap-codec@1.5.5': {} + + '@jridgewell/sourcemap-codec@1.6.0': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@json-render/core@0.20.0(zod@4.6.1)': + dependencies: + zod: 4.6.1 + + '@modelcontextprotocol/core@2.0.0': + dependencies: + zod: 4.6.1 + + '@modelcontextprotocol/server@2.0.0': + dependencies: + '@modelcontextprotocol/core': 2.0.0 + zod: 4.6.1 + + '@napi-rs/wasm-runtime@1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@noble/ciphers@2.4.0': {} + + '@nodable/entities@3.0.0': {} + + '@oxc-parser/binding-android-arm-eabi@0.131.0': + optional: true + + '@oxc-parser/binding-android-arm64@0.131.0': + optional: true + + '@oxc-parser/binding-darwin-arm64@0.131.0': + optional: true + + '@oxc-parser/binding-darwin-x64@0.131.0': + optional: true + + '@oxc-parser/binding-freebsd-x64@0.131.0': + optional: true + + '@oxc-parser/binding-linux-arm-gnueabihf@0.131.0': + optional: true + + '@oxc-parser/binding-linux-arm-musleabihf@0.131.0': + optional: true + + '@oxc-parser/binding-linux-arm64-gnu@0.131.0': + optional: true + + '@oxc-parser/binding-linux-arm64-musl@0.131.0': + optional: true + + '@oxc-parser/binding-linux-ppc64-gnu@0.131.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-gnu@0.131.0': + optional: true + + '@oxc-parser/binding-linux-riscv64-musl@0.131.0': + optional: true + + '@oxc-parser/binding-linux-s390x-gnu@0.131.0': + optional: true + + '@oxc-parser/binding-linux-x64-gnu@0.131.0': + optional: true + + '@oxc-parser/binding-linux-x64-musl@0.131.0': + optional: true + + '@oxc-parser/binding-openharmony-arm64@0.131.0': + optional: true + + '@oxc-parser/binding-wasm32-wasi@0.131.0': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.2.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + optional: true + + '@oxc-parser/binding-win32-arm64-msvc@0.131.0': + optional: true + + '@oxc-parser/binding-win32-ia32-msvc@0.131.0': + optional: true + + '@oxc-parser/binding-win32-x64-msvc@0.131.0': + optional: true + + '@oxc-project/types@0.131.0': {} + + '@oxc-project/types@0.149.0': {} + + '@oxfmt/binding-android-arm-eabi@0.67.0': + optional: true + + '@oxfmt/binding-android-arm64@0.67.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.67.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.67.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.67.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.67.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.67.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.67.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.67.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.67.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.67.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.67.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.67.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.67.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.67.0': + optional: true + + '@oxlint/binding-android-arm-eabi@1.82.0': + optional: true + + '@oxlint/binding-android-arm64@1.82.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.82.0': + optional: true + + '@oxlint/binding-darwin-x64@1.82.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.82.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.82.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.82.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.82.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.82.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.82.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.82.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.82.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.82.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.82.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.82.0': + optional: true + + '@parcel/watcher-android-arm64@2.6.0': + optional: true + + '@parcel/watcher-darwin-arm64@2.6.0': + optional: true + + '@parcel/watcher-darwin-x64@2.6.0': + optional: true + + '@parcel/watcher-freebsd-x64@2.6.0': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.6.0': + optional: true + + '@parcel/watcher-linux-arm-musl@2.6.0': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.6.0': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.6.0': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.6.0': + optional: true + + '@parcel/watcher-linux-x64-musl@2.6.0': + optional: true + + '@parcel/watcher-win32-arm64@2.6.0': + optional: true + + '@parcel/watcher-win32-x64@2.6.0': + optional: true + + '@parcel/watcher@2.6.0': + dependencies: + detect-libc: 2.1.2 + is-glob: 4.0.3 + node-addon-api: 7.1.1 + picomatch: 4.0.7 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.6.0 + '@parcel/watcher-darwin-arm64': 2.6.0 + '@parcel/watcher-darwin-x64': 2.6.0 + '@parcel/watcher-freebsd-x64': 2.6.0 + '@parcel/watcher-linux-arm-glibc': 2.6.0 + '@parcel/watcher-linux-arm-musl': 2.6.0 + '@parcel/watcher-linux-arm64-glibc': 2.6.0 + '@parcel/watcher-linux-arm64-musl': 2.6.0 + '@parcel/watcher-linux-x64-glibc': 2.6.0 + '@parcel/watcher-linux-x64-musl': 2.6.0 + '@parcel/watcher-win32-arm64': 2.6.0 + '@parcel/watcher-win32-x64': 2.6.0 + optional: true + + '@quansync/fs@1.0.0': + dependencies: + quansync: 1.0.0 + + '@rolldown/binding-android-arm-eabi@1.2.8': + optional: true + + '@rolldown/binding-android-arm64@1.2.8': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.8': + optional: true + + '@rolldown/binding-darwin-x64@1.2.8': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.8': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.8': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.8': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.8': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.8': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.8': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.8': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.8': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.8': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.8': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.8': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@shikijs/core@4.4.3': + dependencies: + '@shikijs/primitive': 4.4.3 + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 4.3.6 + + '@shikijs/engine-oniguruma@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + + '@shikijs/primitive@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/themes@4.4.3': + dependencies: + '@shikijs/types': 4.4.3 + + '@shikijs/types@4.4.3': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@standard-schema/spec@1.1.0': {} + + '@sxzz/popperjs-es@2.11.8': {} + + '@transloadit/prettier-bytes@0.3.5': {} + + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/event-emitter@0.3.5': {} + + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.25 + + '@types/lodash@4.17.25': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/node@26.1.1': + dependencies: + undici-types: 8.3.0 + + '@types/nprogress@0.2.3': {} + + '@types/retry@0.12.2': {} + + '@types/unist@3.0.3': {} + + '@types/web-bluetooth@0.0.21': {} + + '@ungap/structured-clone@1.4.0': {} + + '@unocss/cli@66.10.1': + dependencies: + '@jridgewell/remapping': 2.3.5 + '@unocss/config': 66.10.1 + '@unocss/core': 66.10.1 + '@unocss/preset-wind3': 66.10.1 + '@unocss/preset-wind4': 66.10.1 + '@unocss/transformer-directives': 66.10.1 + cac: 7.0.0 + chokidar: 5.0.0 + colorette: 2.0.20 + consola: 3.4.2 + magic-string: 1.3.1 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + tinyglobby: 0.2.17 + unplugin-utils: 0.3.2 + + '@unocss/config@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + colorette: 2.0.20 + consola: 3.4.2 + unconfig: 7.5.0 + + '@unocss/core@66.10.1': {} + + '@unocss/extractor-arbitrary-variants@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + + '@unocss/inspector@66.10.1(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(cac@7.0.0)(crossws@0.4.12(srvx@1.0.4))(srvx@1.0.4)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))': + dependencies: + '@devframes/service-shiki': 0.9.18(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)) + '@devframes/vite': 0.9.18(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4))(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + '@unocss/config': 66.10.1 + '@unocss/core': 66.10.1 + '@unocss/rule-utils': 66.10.1 + '@vitejs/devtools-kit': 0.7.3(cac@7.0.0)(crossws@0.4.12(srvx@1.0.4))(srvx@1.0.4)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + colorette: 2.0.20 + devframe: 0.9.18(cac@7.0.0)(srvx@1.0.4) + tinyglobby: 0.2.17 + unplugin-utils: 0.3.2 + transitivePeerDependencies: + - '@devframes/hub' + - '@devframes/hub-ui' + - '@modelcontextprotocol/client' + - cac + - crossws + - ocache + - srvx + - vite + + '@unocss/preset-attributify@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + + '@unocss/preset-icons@66.10.1': + dependencies: + '@iconify/utils': 3.1.7 + '@unocss/core': 66.10.1 + ofetch: 1.5.1 + + '@unocss/preset-mini@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + '@unocss/extractor-arbitrary-variants': 66.10.1 + '@unocss/rule-utils': 66.10.1 + + '@unocss/preset-tagify@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + + '@unocss/preset-typography@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + '@unocss/rule-utils': 66.10.1 + + '@unocss/preset-uno@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + '@unocss/preset-wind3': 66.10.1 + + '@unocss/preset-web-fonts@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + ofetch: 1.5.1 + + '@unocss/preset-wind3@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + '@unocss/preset-mini': 66.10.1 + '@unocss/rule-utils': 66.10.1 + + '@unocss/preset-wind4@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + '@unocss/extractor-arbitrary-variants': 66.10.1 + '@unocss/rule-utils': 66.10.1 + + '@unocss/preset-wind@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + '@unocss/preset-wind3': 66.10.1 + + '@unocss/rollup@66.10.1(rolldown@1.2.8)': + dependencies: + '@jridgewell/remapping': 2.3.5 + '@unocss/config': 66.10.1 + '@unocss/core': 66.10.1 + chokidar: 5.0.0 + magic-string: 1.3.1 + pathe: 2.0.3 + tinyglobby: 0.2.17 + unplugin-utils: 0.3.2 + optionalDependencies: + rolldown: 1.2.8 + + '@unocss/rule-utils@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + magic-string: 1.3.1 + + '@unocss/transformer-attributify-jsx@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + oxc-parser: 0.131.0 + oxc-walker: 0.7.0(oxc-parser@0.131.0) + + '@unocss/transformer-compile-class@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + + '@unocss/transformer-directives@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + '@unocss/rule-utils': 66.10.1 + css-tree: 3.2.1 + + '@unocss/transformer-variant-group@66.10.1': + dependencies: + '@unocss/core': 66.10.1 + + '@unocss/vite@66.10.1(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(cac@7.0.0)(crossws@0.4.12(srvx@1.0.4))(srvx@1.0.4)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))': + dependencies: + '@jridgewell/remapping': 2.3.5 + '@unocss/config': 66.10.1 + '@unocss/core': 66.10.1 + '@unocss/inspector': 66.10.1(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(cac@7.0.0)(crossws@0.4.12(srvx@1.0.4))(srvx@1.0.4)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + chokidar: 5.0.0 + magic-string: 1.3.1 + pathe: 2.0.3 + tinyglobby: 0.2.17 + unplugin-utils: 0.3.2 + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@devframes/hub' + - '@devframes/hub-ui' + - '@modelcontextprotocol/client' + - cac + - crossws + - ocache + - srvx + + '@uppy/companion-client@5.1.1(@uppy/core@5.2.0)': + dependencies: + '@uppy/core': 5.2.0 + '@uppy/utils': 7.2.0 + namespace-emitter: 2.0.1 + p-retry: 6.2.1 + transitivePeerDependencies: + - preact-render-to-string + + '@uppy/core@5.2.0': + dependencies: + '@transloadit/prettier-bytes': 0.3.5 + '@uppy/store-default': 5.0.0 + '@uppy/utils': 7.2.0 + lodash: 4.18.1 + mime-match: 1.0.2 + namespace-emitter: 2.0.1 + nanoid: 5.1.16 + preact: 10.29.8 + transitivePeerDependencies: + - preact-render-to-string + + '@uppy/store-default@5.0.0': {} + + '@uppy/utils@7.2.0': + dependencies: + lodash: 4.18.1 + preact: 10.29.8 + transitivePeerDependencies: + - preact-render-to-string + + '@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0)': + dependencies: + '@uppy/companion-client': 5.1.1(@uppy/core@5.2.0) + '@uppy/core': 5.2.0 + '@uppy/utils': 7.2.0 + transitivePeerDependencies: + - preact-render-to-string + + '@vitejs/devtools-kit@0.7.3(cac@7.0.0)(crossws@0.4.12(srvx@1.0.4))(srvx@1.0.4)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))': + dependencies: + '@devframes/hub': 0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)) + '@devframes/json-render': 0.9.18(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)) + devframe: 0.9.18(cac@7.0.0)(srvx@1.0.4) + local-pkg: 1.2.1 + mlly: 1.8.2 + nostics: 1.2.0 + nypm: 0.6.10 + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@modelcontextprotocol/client' + - cac + - crossws + - ocache + - srvx + + '@vitejs/plugin-vue@6.0.8(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + vue: 3.5.42(typescript@6.0.3) + + '@vitest/expect@4.1.11': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + chai: 6.2.2 + tinyrainbow: 3.1.1 + + '@vitest/mocker@4.1.11(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))': + dependencies: + '@vitest/spy': 4.1.11 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + + '@vitest/pretty-format@4.1.11': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/runner@4.1.11': + dependencies: + '@vitest/utils': 4.1.11 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + '@vitest/utils': 4.1.11 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.11': {} + + '@vitest/utils@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + '@volar/language-core@2.4.28': + dependencies: + '@volar/source-map': 2.4.28 + + '@volar/source-map@2.4.28': {} + + '@volar/typescript@2.4.28': + dependencies: + '@volar/language-core': 2.4.28 + path-browserify: 1.0.1 + vscode-uri: 3.2.0 + + '@vue-macros/common@3.1.4(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@vue/compiler-sfc': 3.5.42 + ast-kit: 2.2.0 + local-pkg: 1.2.1 + magic-string-ast: 1.0.3 + unplugin-utils: 0.3.2 + optionalDependencies: + vue: 3.5.42(typescript@6.0.3) + + '@vue/compiler-core@3.5.42': + dependencies: + '@babel/parser': 7.29.8 + '@vue/shared': 3.5.42 + entities: 7.0.1 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.42': + dependencies: + '@vue/compiler-core': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/compiler-sfc@3.5.42': + dependencies: + '@babel/parser': 7.29.8 + '@vue/compiler-core': 3.5.42 + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-ssr': 3.5.42 + '@vue/shared': 3.5.42 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.28 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.42': + dependencies: + '@vue/compiler-dom': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/devtools-api@6.6.4': {} + + '@vue/devtools-api@8.2.1': + dependencies: + '@vue/devtools-kit': 8.2.1 + + '@vue/devtools-kit@8.2.1': + dependencies: + '@vue/devtools-shared': 8.2.1 + birpc: 2.9.0 + hookable: 5.5.3 + perfect-debounce: 2.1.0 + + '@vue/devtools-shared@8.2.1': {} + + '@vue/language-core@3.3.11': + dependencies: + '@volar/language-core': 2.4.28 + '@vue/compiler-dom': 3.5.42 + '@vue/shared': 3.5.42 + alien-signals: 3.2.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.7 + + '@vue/reactivity@3.5.42': + dependencies: + '@vue/shared': 3.5.42 + + '@vue/runtime-core@3.5.42': + dependencies: + '@vue/reactivity': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/runtime-dom@3.5.42': + dependencies: + '@vue/reactivity': 3.5.42 + '@vue/runtime-core': 3.5.42 + '@vue/shared': 3.5.42 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.42': + dependencies: + '@vue/compiler-ssr': 3.5.42 + '@vue/runtime-dom': 3.5.42 + '@vue/shared': 3.5.42 + + '@vue/shared@3.5.42': {} + + '@vueuse/core@14.4.0(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.4.0 + '@vueuse/shared': 14.4.0(vue@3.5.42(typescript@6.0.3)) + vue: 3.5.42(typescript@6.0.3) + + '@vueuse/metadata@14.4.0': {} + + '@vueuse/shared@14.4.0(vue@3.5.42(typescript@6.0.3))': + dependencies: + vue: 3.5.42(typescript@6.0.3) + + '@vxe-ui/core@4.4.21(vue@3.5.42(typescript@6.0.3))': + dependencies: + dom-zindex: 1.0.7 + vue: 3.5.42(typescript@6.0.3) + xe-utils: 4.0.13 + + '@wangeditor-next/basic-modules@6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4)': + dependencies: + '@wangeditor-next/core': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + dom7: 4.0.6 + is-url: 1.2.4 + lodash.throttle: 4.1.1 + nanoid: 5.1.16 + slate: 0.124.1 + snabbdom: 3.6.4 + + '@wangeditor-next/code-highlight@6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(slate@0.124.1)(snabbdom@3.6.4)': + dependencies: + '@wangeditor-next/core': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + dom7: 4.0.6 + prismjs: 1.30.0 + slate: 0.124.1 + snabbdom: 3.6.4 + + '@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4)': + dependencies: + '@types/event-emitter': 0.3.5 + '@uppy/core': 5.2.0 + '@uppy/xhr-upload': 5.2.0(@uppy/core@5.2.0) + dom7: 4.0.6 + event-emitter: 0.3.5 + html-void-elements: 3.0.0 + i18next: 23.16.8 + 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.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 5.1.16 + scroll-into-view-if-needed: 3.1.0 + slate: 0.124.1 + slate-history: 0.115.0(slate@0.124.1) + snabbdom: 3.6.4 + + '@wangeditor-next/editor-for-vue@6.4.1(@wangeditor-next/editor@6.4.1)(vue@3.5.42(typescript@6.0.3))': + dependencies: + '@wangeditor-next/editor': 6.4.1 + vue: 3.5.42(typescript@6.0.3) + + '@wangeditor-next/editor@6.4.1': + dependencies: + '@uppy/core': 5.2.0 + '@uppy/xhr-upload': 5.2.0(@uppy/core@5.2.0) + '@wangeditor-next/basic-modules': 6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + '@wangeditor-next/code-highlight': 6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(slate@0.124.1)(snabbdom@3.6.4) + '@wangeditor-next/core': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + '@wangeditor-next/list-module': 6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(slate@0.124.1)(snabbdom@3.6.4) + '@wangeditor-next/table-module': 6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + '@wangeditor-next/upload-image-module': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(@wangeditor-next/basic-modules@6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.124.1)(snabbdom@3.6.4) + '@wangeditor-next/video-module': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + dom7: 4.0.6 + 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.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 5.1.16 + slate: 0.124.1 + snabbdom: 3.6.4 + transitivePeerDependencies: + - preact-render-to-string + + '@wangeditor-next/list-module@6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(slate@0.124.1)(snabbdom@3.6.4)': + dependencies: + '@wangeditor-next/core': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + dom7: 4.0.6 + slate: 0.124.1 + snabbdom: 3.6.4 + + '@wangeditor-next/table-module@6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.debounce@4.0.8)(lodash.throttle@4.1.1)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4)': + dependencies: + '@wangeditor-next/core': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + dom7: 4.0.6 + lodash.debounce: 4.0.8 + lodash.throttle: 4.1.1 + nanoid: 5.1.16 + slate: 0.124.1 + snabbdom: 3.6.4 + + '@wangeditor-next/upload-image-module@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(@wangeditor-next/basic-modules@6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.foreach@4.5.0)(slate@0.124.1)(snabbdom@3.6.4)': + dependencies: + '@uppy/core': 5.2.0 + '@uppy/xhr-upload': 5.2.0(@uppy/core@5.2.0) + '@wangeditor-next/basic-modules': 6.4.1(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(lodash.throttle@4.1.1)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + '@wangeditor-next/core': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + dom7: 4.0.6 + lodash.foreach: 4.5.0 + slate: 0.124.1 + snabbdom: 3.6.4 + + '@wangeditor-next/video-module@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(@wangeditor-next/core@6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4))(dom7@4.0.6)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4)': + dependencies: + '@uppy/core': 5.2.0 + '@uppy/xhr-upload': 5.2.0(@uppy/core@5.2.0) + '@wangeditor-next/core': 6.4.1(@uppy/core@5.2.0)(@uppy/xhr-upload@5.2.0(@uppy/core@5.2.0))(dom7@4.0.6)(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.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@5.1.16)(slate@0.124.1)(snabbdom@3.6.4) + dom7: 4.0.6 + nanoid: 5.1.16 + slate: 0.124.1 + snabbdom: 3.6.4 + + acorn@8.17.0: {} + + acorn@8.18.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + alien-signals@3.2.1: {} + + animate.css@4.1.1: {} + + anynum@1.0.1: {} + + assertion-error@2.0.1: {} + + ast-kit@2.2.0: + dependencies: + '@babel/parser': 7.29.8 + pathe: 2.0.3 + + ast-walker-scope@0.9.0: + dependencies: + '@babel/parser': 7.29.8 + '@babel/types': 7.29.8 + ast-kit: 2.2.0 + + async-validator@4.2.5: {} + + asynckit@0.4.0: {} + + autoprefixer@10.5.5(postcss@8.5.28): + dependencies: + browserslist: 4.28.9 + caniuse-lite: 1.0.30001810 + fraction.js: 5.3.4 + picocolors: 1.1.1 + postcss: 8.5.28 + postcss-value-parser: 4.2.0 + + await-to-js@3.0.0: {} + + axios@1.20.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.6 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + + baseline-browser-mapping@2.11.21: {} + + birpc@2.9.0: {} + + birpc@4.2.0: {} + + boolbase@1.0.0: {} + + browserslist@4.28.9: + dependencies: + baseline-browser-mapping: 2.11.21 + caniuse-lite: 1.0.30001810 + electron-to-chromium: 1.5.425 + node-releases: 2.0.55 + update-browserslist-db: 1.3.2(browserslist@4.28.9) + + cac@7.0.0: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + caniuse-lite@1.0.30001810: {} + + ccount@2.0.1: {} + + chai@6.2.2: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + + citty@0.2.2: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + + commander@11.1.0: {} + + compute-scroll-into-view@3.1.1: {} + + confbox@0.1.8: {} + + confbox@0.2.4: {} + + confbox@0.3.1: {} + + consola@3.4.2: {} + + convert-source-map@2.0.0: {} + + crossws@0.4.12(srvx@1.0.4): + optionalDependencies: + srvx: 1.0.4 + + css-select@6.0.0: + dependencies: + boolbase: 1.0.0 + css-what: 7.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + nth-check: 2.1.1 + + css-tree@2.2.1: + dependencies: + mdn-data: 2.0.28 + source-map-js: 1.2.1 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css-what@7.0.0: {} + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + csstype@3.2.3: {} + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + dayjs@1.11.23: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + defu@6.1.7: {} + + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-libc@2.1.2: {} + + devframe@0.9.18(cac@7.0.0)(srvx@1.0.4): + dependencies: + '@modelcontextprotocol/server': 2.0.0 + '@standard-schema/spec': 1.1.0 + birpc: 4.2.0 + crossws: 0.4.12(srvx@1.0.4) + h3: 2.0.1-rc.31(crossws@0.4.12(srvx@1.0.4)) + mrmime: 2.0.1 + nostics: 1.2.0 + pathe: 2.0.3 + ufo: 1.6.4 + optionalDependencies: + cac: 7.0.0 + transitivePeerDependencies: + - ocache + - srvx + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + dom-zindex@1.0.7: {} + + dom7@4.0.6: + dependencies: + ssr-window: 4.0.2 + + 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 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + echarts@6.1.0: + dependencies: + tslib: 2.3.0 + zrender: 6.1.0 + + electron-to-chromium@1.5.425: {} + + element-plus@2.14.5(vue@3.5.42(typescript@6.0.3)): + dependencies: + '@ctrl/tinycolor': 4.2.1 + '@element-plus/icons-vue': 2.3.2(vue@3.5.42(typescript@6.0.3)) + '@floating-ui/dom': 1.8.0 + '@popperjs/core': '@sxzz/popperjs-es@2.11.8' + '@types/lodash': 4.17.25 + '@types/lodash-es': 4.17.12 + '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) + async-validator: 4.2.5 + dayjs: 1.11.23 + lodash: 4.18.1 + lodash-es: 4.18.1 + lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.18.1)(lodash@4.18.1) + memoize-one: 6.0.0 + normalize-wheel-es: 1.2.0 + vue: 3.5.42(typescript@6.0.3) + vue-component-type-helpers: 3.3.11 + + entities@4.5.0: {} + + entities@7.0.1: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.2: {} + + es-object-atoms@1.1.2: + 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.4 + + 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 + + escalade@3.2.0: {} + + escape-string-regexp@5.0.0: {} + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + expect-type@1.4.0: {} + + exsolve@1.1.0: {} + + exsolve@1.1.1: {} + + ext@1.7.0: + dependencies: + type: 2.7.3 + + fast-xml-builder@1.3.1: + dependencies: + path-expression-matcher: 1.6.2 + xml-naming: 0.3.0 + + fast-xml-parser@5.11.1: + dependencies: + '@nodable/entities': 3.0.0 + fast-xml-builder: 1.3.1 + is-unsafe: 2.0.2 + path-expression-matcher: 1.6.2 + strnum: 2.4.2 + xml-naming: 0.3.0 + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + follow-redirects@1.16.0: {} + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + fraction.js@5.3.4: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + 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.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + gopd@1.2.0: {} + + h3@2.0.1-rc.31(crossws@0.4.12(srvx@1.0.4)): + dependencies: + rou3: 0.9.2 + srvx: 1.0.4 + optionalDependencies: + crossws: 0.4.12(srvx@1.0.4) + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + ccount: 2.0.1 + comma-separated-tokens: 2.0.3 + hast-util-whitespace: 3.0.0 + html-void-elements: 3.0.0 + mdast-util-to-hast: 13.2.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + stringify-entities: 4.0.4 + zwitch: 2.0.4 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + highlight.js@11.12.0: {} + + hookable@5.5.3: {} + + html-void-elements@3.0.0: {} + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + i18next@23.16.8: + dependencies: + '@babel/runtime': 7.29.7 + + image-conversion@2.1.1: {} + + immutable@5.1.9: {} + + import-meta-resolve@4.2.0: {} + + is-extglob@2.1.1: + optional: true + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + optional: true + + is-hotkey@0.2.0: {} + + is-network-error@1.3.2: {} + + is-unsafe@2.0.2: {} + + is-url@1.2.4: {} + + jiti@2.7.0: {} + + js-tokens@10.0.0: {} + + jsencrypt@3.5.4: {} + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + local-pkg@1.2.1: + dependencies: + mlly: 1.8.2 + pkg-types: 2.3.1 + quansync: 0.2.11 + + lodash-es@4.18.1: {} + + lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.18.1)(lodash@4.18.1): + dependencies: + '@types/lodash-es': 4.17.12 + lodash: 4.18.1 + lodash-es: 4.18.1 + + lodash.camelcase@4.3.0: {} + + lodash.clonedeep@4.5.0: {} + + lodash.debounce@4.0.8: {} + + lodash.foreach@4.5.0: {} + + lodash.throttle@4.1.1: {} + + lodash.toarray@4.4.0: {} + + lodash@4.18.1: {} + + magic-regexp@0.10.0: + dependencies: + estree-walker: 3.0.3 + magic-string: 0.30.21 + mlly: 1.8.2 + regexp-tree: 0.1.27 + type-level-regexp: 0.1.17 + ufo: 1.6.4 + unplugin: 2.3.11 + + magic-string-ast@1.0.3: + dependencies: + magic-string: 0.30.21 + + magic-string@0.26.7: + dependencies: + sourcemap-codec: 1.4.8 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magic-string@1.3.1: + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + + math-intrinsics@1.1.0: {} + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.4.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdn-data@2.0.28: {} + + mdn-data@2.27.1: {} + + memoize-one@6.0.0: {} + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-encode@2.0.1: {} + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + 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 + + mlly@1.8.2: + dependencies: + acorn: 8.17.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + namespace-emitter@2.0.1: {} + + nanoid@3.3.18: {} + + nanoid@5.1.16: {} + + next-tick@1.1.0: {} + + node-addon-api@7.1.1: + optional: true + + node-fetch-native@1.6.7: {} + + node-releases@2.0.55: {} + + normalize-wheel-es@1.2.0: {} + + nostics@1.2.0: {} + + nprogress@0.2.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nypm@0.6.10: + dependencies: + citty: 0.2.2 + pathe: 2.0.3 + tinyexec: 1.3.1 + + obug@2.1.4: {} + + obug@2.2.1: {} + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.4 + + oniguruma-parser@0.12.2: {} + + oniguruma-to-es@4.3.6: + dependencies: + oniguruma-parser: 0.12.2 + regex: 6.1.0 + regex-recursion: 6.0.2 + + oxc-parser@0.131.0: + dependencies: + '@oxc-project/types': 0.131.0 + optionalDependencies: + '@oxc-parser/binding-android-arm-eabi': 0.131.0 + '@oxc-parser/binding-android-arm64': 0.131.0 + '@oxc-parser/binding-darwin-arm64': 0.131.0 + '@oxc-parser/binding-darwin-x64': 0.131.0 + '@oxc-parser/binding-freebsd-x64': 0.131.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.131.0 + '@oxc-parser/binding-linux-arm-musleabihf': 0.131.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.131.0 + '@oxc-parser/binding-linux-arm64-musl': 0.131.0 + '@oxc-parser/binding-linux-ppc64-gnu': 0.131.0 + '@oxc-parser/binding-linux-riscv64-gnu': 0.131.0 + '@oxc-parser/binding-linux-riscv64-musl': 0.131.0 + '@oxc-parser/binding-linux-s390x-gnu': 0.131.0 + '@oxc-parser/binding-linux-x64-gnu': 0.131.0 + '@oxc-parser/binding-linux-x64-musl': 0.131.0 + '@oxc-parser/binding-openharmony-arm64': 0.131.0 + '@oxc-parser/binding-wasm32-wasi': 0.131.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.131.0 + '@oxc-parser/binding-win32-ia32-msvc': 0.131.0 + '@oxc-parser/binding-win32-x64-msvc': 0.131.0 + + oxc-walker@0.7.0(oxc-parser@0.131.0): + dependencies: + magic-regexp: 0.10.0 + oxc-parser: 0.131.0 + + oxfmt@0.67.0: + dependencies: + tinypool: 2.1.2 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.67.0 + '@oxfmt/binding-android-arm64': 0.67.0 + '@oxfmt/binding-darwin-arm64': 0.67.0 + '@oxfmt/binding-darwin-x64': 0.67.0 + '@oxfmt/binding-freebsd-x64': 0.67.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.67.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.67.0 + '@oxfmt/binding-linux-arm64-gnu': 0.67.0 + '@oxfmt/binding-linux-arm64-musl': 0.67.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.67.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.67.0 + '@oxfmt/binding-linux-riscv64-musl': 0.67.0 + '@oxfmt/binding-linux-s390x-gnu': 0.67.0 + '@oxfmt/binding-linux-x64-gnu': 0.67.0 + '@oxfmt/binding-linux-x64-musl': 0.67.0 + '@oxfmt/binding-openharmony-arm64': 0.67.0 + '@oxfmt/binding-win32-arm64-msvc': 0.67.0 + '@oxfmt/binding-win32-ia32-msvc': 0.67.0 + '@oxfmt/binding-win32-x64-msvc': 0.67.0 + + oxlint@1.82.0: + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.82.0 + '@oxlint/binding-android-arm64': 1.82.0 + '@oxlint/binding-darwin-arm64': 1.82.0 + '@oxlint/binding-darwin-x64': 1.82.0 + '@oxlint/binding-freebsd-x64': 1.82.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.82.0 + '@oxlint/binding-linux-arm-musleabihf': 1.82.0 + '@oxlint/binding-linux-arm64-gnu': 1.82.0 + '@oxlint/binding-linux-arm64-musl': 1.82.0 + '@oxlint/binding-linux-ppc64-gnu': 1.82.0 + '@oxlint/binding-linux-riscv64-gnu': 1.82.0 + '@oxlint/binding-linux-riscv64-musl': 1.82.0 + '@oxlint/binding-linux-s390x-gnu': 1.82.0 + '@oxlint/binding-linux-x64-gnu': 1.82.0 + '@oxlint/binding-linux-x64-musl': 1.82.0 + '@oxlint/binding-openharmony-arm64': 1.82.0 + '@oxlint/binding-win32-arm64-msvc': 1.82.0 + '@oxlint/binding-win32-ia32-msvc': 1.82.0 + '@oxlint/binding-win32-x64-msvc': 1.82.0 + + p-retry@6.2.1: + dependencies: + '@types/retry': 0.12.2 + is-network-error: 1.3.2 + retry: 0.13.1 + + package-manager-detector@1.8.0: {} + + path-browserify@1.0.1: {} + + path-expression-matcher@1.6.2: {} + + pathe@2.0.3: {} + + perfect-debounce@2.1.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + picomatch@4.0.7: {} + + pinia@4.0.3(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.42(typescript@6.0.3)): + dependencies: + '@vue/devtools-api': 8.2.1 + nostics: 1.2.0 + vue: 3.5.42(typescript@6.0.3) + optionalDependencies: + typescript: 6.0.3 + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + pkg-types@2.3.1: + dependencies: + confbox: 0.2.4 + exsolve: 1.1.0 + pathe: 2.0.3 + + pkg-types@2.3.3: + dependencies: + confbox: 0.3.1 + exsolve: 1.1.1 + pathe: 2.0.3 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.28: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.29.8: {} + + prismjs@1.30.0: {} + + property-information@7.2.0: {} + + proxy-from-env@2.1.0: {} + + quansync@0.2.11: {} + + quansync@1.0.0: {} + + readdirp@5.0.0: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.1.0: + dependencies: + regex-utilities: 2.3.0 + + regexp-tree@0.1.27: {} + + retry@0.13.1: {} + + rolldown@1.2.8: + dependencies: + '@oxc-project/types': 0.149.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.8 + '@rolldown/binding-android-arm64': 1.2.8 + '@rolldown/binding-darwin-arm64': 1.2.8 + '@rolldown/binding-darwin-x64': 1.2.8 + '@rolldown/binding-freebsd-x64': 1.2.8 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.8 + '@rolldown/binding-linux-arm64-gnu': 1.2.8 + '@rolldown/binding-linux-arm64-musl': 1.2.8 + '@rolldown/binding-linux-ppc64-gnu': 1.2.8 + '@rolldown/binding-linux-s390x-gnu': 1.2.8 + '@rolldown/binding-linux-x64-gnu': 1.2.8 + '@rolldown/binding-linux-x64-musl': 1.2.8 + '@rolldown/binding-openharmony-arm64': 1.2.8 + '@rolldown/binding-win32-arm64-msvc': 1.2.8 + '@rolldown/binding-win32-x64-msvc': 1.2.8 + + rou3@0.9.2: {} + + sass@1.104.0: + dependencies: + chokidar: 5.0.0 + immutable: 5.1.9 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.6.0 + + sax@1.6.1: {} + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + scule@1.3.0: {} + + shiki@4.4.3: + dependencies: + '@shikijs/core': 4.4.3 + '@shikijs/engine-javascript': 4.4.3 + '@shikijs/engine-oniguruma': 4.4.3 + '@shikijs/langs': 4.4.3 + '@shikijs/themes': 4.4.3 + '@shikijs/types': 4.4.3 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.5 + + siginfo@2.0.0: {} + + slate-history@0.115.0(slate@0.124.1): + dependencies: + slate: 0.124.1 + + slate@0.124.1: {} + + snabbdom@3.6.4: {} + + source-map-js@1.2.1: {} + + sourcemap-codec@1.4.8: {} + + space-separated-tokens@2.0.2: {} + + srvx@1.0.4: {} + + ssr-window@4.0.2: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-literal@4.0.0: + dependencies: + js-tokens: 10.0.0 + + strnum@2.4.2: + dependencies: + anynum: 1.0.1 + + svg-icon-baker@2.1.1: + dependencies: + css-tree: 3.2.1 + fast-xml-builder: 1.3.1 + fast-xml-parser: 5.11.1 + svgo: 4.1.0 + + svgo@4.1.0: + dependencies: + commander: 11.1.0 + css-select: 6.0.0 + css-tree: 3.2.1 + css-what: 7.0.0 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.6.1 + + tinybench@2.9.0: {} + + tinyexec@1.3.1: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinypool@2.1.2: {} + + tinyrainbow@3.1.1: {} + + trim-lines@3.0.1: {} + + tslib@2.3.0: {} + + tslib@2.8.1: + optional: true + + type-level-regexp@0.1.17: {} + + type@2.7.3: {} + + typescript@6.0.3: {} + + ufo@1.6.4: {} + + unconfig-core@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + quansync: 1.0.0 + + unconfig@7.5.0: + dependencies: + '@quansync/fs': 1.0.0 + defu: 6.1.7 + jiti: 2.7.0 + quansync: 1.0.0 + unconfig-core: 7.5.0 + + undici-types@8.3.0: {} + + unimport@6.5.0(oxc-parser@0.131.0)(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)): + dependencies: + acorn: 8.18.0 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + local-pkg: 1.2.1 + magic-string: 1.3.1 + mlly: 1.8.2 + pathe: 2.0.3 + picomatch: 4.0.7 + pkg-types: 2.3.3 + scule: 1.3.0 + strip-literal: 4.0.0 + tinyglobby: 0.2.17 + unplugin: 3.3.0(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + unplugin-utils: 0.3.2 + optionalDependencies: + oxc-parser: 0.131.0 + rolldown: 1.2.8 + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rollup + - unloader + - vite + - webpack + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + unocss@66.10.1(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(cac@7.0.0)(crossws@0.4.12(srvx@1.0.4))(rolldown@1.2.8)(srvx@1.0.4)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)): + dependencies: + '@unocss/cli': 66.10.1 + '@unocss/core': 66.10.1 + '@unocss/preset-attributify': 66.10.1 + '@unocss/preset-icons': 66.10.1 + '@unocss/preset-mini': 66.10.1 + '@unocss/preset-tagify': 66.10.1 + '@unocss/preset-typography': 66.10.1 + '@unocss/preset-uno': 66.10.1 + '@unocss/preset-web-fonts': 66.10.1 + '@unocss/preset-wind': 66.10.1 + '@unocss/preset-wind3': 66.10.1 + '@unocss/preset-wind4': 66.10.1 + '@unocss/rollup': 66.10.1(rolldown@1.2.8) + '@unocss/transformer-attributify-jsx': 66.10.1 + '@unocss/transformer-compile-class': 66.10.1 + '@unocss/transformer-directives': 66.10.1 + '@unocss/transformer-variant-group': 66.10.1 + '@unocss/vite': 66.10.1(@devframes/hub@0.9.18(crossws@0.4.12(srvx@1.0.4))(devframe@0.9.18(cac@7.0.0)(srvx@1.0.4)))(cac@7.0.0)(crossws@0.4.12(srvx@1.0.4))(srvx@1.0.4)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + transitivePeerDependencies: + - '@devframes/hub' + - '@devframes/hub-ui' + - '@modelcontextprotocol/client' + - cac + - crossws + - ocache + - rolldown + - rollup + - srvx + - vite + + unplugin-auto-import@21.1.0(@vueuse/core@14.4.0(vue@3.5.42(typescript@6.0.3)))(oxc-parser@0.131.0)(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)): + dependencies: + local-pkg: 1.2.1 + magic-string: 1.3.1 + picomatch: 4.0.7 + unimport: 6.5.0(oxc-parser@0.131.0)(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + unplugin: 3.3.0(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + unplugin-utils: 0.3.2 + optionalDependencies: + '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - oxc-parser + - rolldown + - rollup + - unloader + - vite + - webpack + + unplugin-utils@0.3.2: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.5 + + unplugin-vue-components@32.1.0(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3)): + dependencies: + chokidar: 5.0.0 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + obug: 2.1.4 + picomatch: 4.0.5 + tinyglobby: 0.2.17 + unplugin: 3.3.0(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + unplugin-utils: 0.3.2 + vue: 3.5.42(typescript@6.0.3) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - unloader + - vite + - webpack + + unplugin-vue-setup-extend-plus@1.0.1: + dependencies: + '@vue/compiler-sfc': 3.5.42 + magic-string: 0.26.7 + unplugin: 1.16.1 + + unplugin@1.16.1: + dependencies: + acorn: 8.17.0 + webpack-virtual-modules: 0.6.2 + + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.18.0 + picomatch: 4.0.7 + webpack-virtual-modules: 0.6.2 + + unplugin@3.3.0(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)): + dependencies: + '@jridgewell/remapping': 2.3.5 + picomatch: 4.0.5 + webpack-virtual-modules: 0.6.2 + optionalDependencies: + rolldown: 1.2.8 + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + + update-browserslist-db@1.3.2(browserslist@4.28.9): + dependencies: + browserslist: 4.28.9 + escalade: 3.2.0 + picocolors: 1.1.1 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite-plugin-svg-icons-ng@1.9.3(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)): + dependencies: + svg-icon-baker: 2.1.1 + tinyglobby: 0.2.17 + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + + vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.7 + postcss: 8.5.28 + rolldown: 1.2.8 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.1.1 + fsevents: 2.3.3 + jiti: 2.7.0 + sass: 1.104.0 + yaml: 2.9.0 + + vitest@4.1.11(@types/node@26.1.1)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)): + dependencies: + '@vitest/expect': 4.1.11 + '@vitest/mocker': 4.1.11(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.11 + '@vitest/runner': 4.1.11 + '@vitest/snapshot': 4.1.11 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.2.1 + pathe: 2.0.3 + picomatch: 4.0.7 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.3.1 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 26.1.1 + transitivePeerDependencies: + - msw + + vscode-uri@3.2.0: {} + + vue-component-type-helpers@3.3.11: {} + + vue-cropper@1.1.4: {} + + vue-i18n@11.4.10(vue@3.5.42(typescript@6.0.3)): + dependencies: + '@intlify/core-base': 11.4.10 + '@intlify/devtools-types': 11.4.10 + '@intlify/shared': 11.4.10 + '@vue/devtools-api': 6.6.4 + vue: 3.5.42(typescript@6.0.3) + + vue-json-pretty@2.6.0(vue@3.5.42(typescript@6.0.3)): + dependencies: + vue: 3.5.42(typescript@6.0.3) + + vue-router@5.3.1(@vue/compiler-sfc@3.5.42)(pinia@4.0.3(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.42(typescript@6.0.3)))(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3)): + dependencies: + '@vue-macros/common': 3.1.4(vue@3.5.42(typescript@6.0.3)) + '@vue/devtools-api': 8.2.1 + ast-walker-scope: 0.9.0 + chokidar: 5.0.0 + confbox: 0.2.4 + local-pkg: 1.2.1 + magic-string: 0.30.21 + mlly: 1.8.2 + muggle-string: 0.4.1 + nostics: 1.2.0 + pathe: 2.0.3 + picomatch: 4.0.7 + scule: 1.3.0 + tinyglobby: 0.2.17 + unplugin: 3.3.0(rolldown@1.2.8)(vite@8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0)) + unplugin-utils: 0.3.2 + vue: 3.5.42(typescript@6.0.3) + optionalDependencies: + '@vue/compiler-sfc': 3.5.42 + pinia: 4.0.3(@vue/devtools-api@8.2.1)(typescript@6.0.3)(vue@3.5.42(typescript@6.0.3)) + vite: 8.2.2(@types/node@26.1.1)(jiti@2.7.0)(sass@1.104.0)(yaml@2.9.0) + transitivePeerDependencies: + - '@farmfe/core' + - '@rspack/core' + - bun-types-no-globals + - esbuild + - rolldown + - rollup + - unloader + - webpack + + vue-tsc@3.3.11(typescript@6.0.3): + dependencies: + '@volar/typescript': 2.4.28 + '@vue/language-core': 3.3.11 + typescript: 6.0.3 + + vue-types@7.0.0(vue@3.5.42(typescript@6.0.3)): + optionalDependencies: + vue: 3.5.42(typescript@6.0.3) + + vue@3.5.42(typescript@6.0.3): + dependencies: + '@vue/compiler-dom': 3.5.42 + '@vue/compiler-sfc': 3.5.42 + '@vue/runtime-dom': 3.5.42 + '@vue/server-renderer': 3.5.42 + '@vue/shared': 3.5.42 + optionalDependencies: + typescript: 6.0.3 + + vxe-pc-ui@4.18.9(vue@3.5.42(typescript@6.0.3)): + dependencies: + '@vxe-ui/core': 4.4.21(vue@3.5.42(typescript@6.0.3)) + transitivePeerDependencies: + - vue + + vxe-table@4.21.9: {} + + webpack-virtual-modules@0.6.2: {} + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wildcard@1.1.2: {} + + xe-utils@4.0.13: {} + + xml-naming@0.3.0: {} + + yaml@2.9.0: + optional: true + + zigpty@0.2.1: {} + + zod@4.6.1: {} + + zrender@6.1.0: + dependencies: + tslib: 2.3.0 + + zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..41db3e3 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,10 @@ +packages: + - '.' + +allowBuilds: + '@parcel/watcher': true + es5-ext: true + esbuild: true + +overrides: + esbuild: 0.28.1 diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..3f919d8 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/ie.html b/public/ie.html new file mode 100644 index 0000000..2bd1c9e --- /dev/null +++ b/public/ie.html @@ -0,0 +1,258 @@ + + + + + 请升级您的浏览器 + + + + + + +

请升级您的浏览器,以便我们更好的为您提供服务!

+

+ 您正在使用 Internet Explorer + 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。 +

+
+

请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束

+

+ 自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 + 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 + + 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明 + + 。 +

+
+

您可以选择更先进的浏览器

+

推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。

+ +
+ + diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..e6f7081 --- /dev/null +++ b/src/App.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/animate.ts b/src/animate.ts new file mode 100644 index 0000000..7a23df8 --- /dev/null +++ b/src/animate.ts @@ -0,0 +1,48 @@ +// 前缀 +const animatePrefix = 'animate__animated '; +// 开启随机动画 随机动画值 +const animateList: string[] = [ + animatePrefix + 'animate__pulse', + animatePrefix + 'animate__rubberBand', + animatePrefix + 'animate__bounceIn', + animatePrefix + 'animate__bounceInLeft', + animatePrefix + 'animate__fadeIn', + animatePrefix + 'animate__fadeInLeft', + animatePrefix + 'animate__fadeInDown', + animatePrefix + 'animate__fadeInUp', + animatePrefix + 'animate__flipInX', + animatePrefix + 'animate__lightSpeedInLeft', + animatePrefix + 'animate__rotateInDownLeft', + animatePrefix + 'animate__rollIn', + animatePrefix + 'animate__rotateInDownLeft', + animatePrefix + 'animate__zoomIn', + animatePrefix + 'animate__zoomInDown', + animatePrefix + 'animate__slideInLeft', + animatePrefix + 'animate__lightSpeedIn' +]; +// 关闭随机动画后的默认效果 +const defaultAnimate = animatePrefix + 'animate__fadeIn'; +// 搜索隐藏显示动画 +const searchAnimate = { + enter: '', + leave: '' +}; + +// 菜单搜索动画 +const menuSearchAnimate = { + enter: animatePrefix + 'animate__fadeIn', + leave: animatePrefix + 'animate__fadeOut' +}; +// logo动画 +const logoAnimate = { + enter: animatePrefix + 'animate__fadeIn', + leave: animatePrefix + 'animate__fadeOut' +}; + +export default { + animateList, + defaultAnimate, + searchAnimate, + menuSearchAnimate, + logoAnimate +}; diff --git a/src/api/ai/agent/index.ts b/src/api/ai/agent/index.ts new file mode 100644 index 0000000..3e4d324 --- /dev/null +++ b/src/api/ai/agent/index.ts @@ -0,0 +1,10 @@ +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { SnailOpenApiUser } from './types'; + +export const registerCurrentSnailUser = (): AxiosPromise => { + return request({ + url: '/snail-ai/user/register', + method: 'post' + }); +}; diff --git a/src/api/ai/agent/types.ts b/src/api/ai/agent/types.ts new file mode 100644 index 0000000..5999b1c --- /dev/null +++ b/src/api/ai/agent/types.ts @@ -0,0 +1,6 @@ +export interface SnailOpenApiUser { + openId: string; + nickname?: string; + externalId?: string; + created?: boolean; +} diff --git a/src/api/ai/index.ts b/src/api/ai/index.ts new file mode 100644 index 0000000..81bac90 --- /dev/null +++ b/src/api/ai/index.ts @@ -0,0 +1,2 @@ +export * from './agent'; +export * from './agent/types'; diff --git a/src/api/demo/demo/index.ts b/src/api/demo/demo/index.ts new file mode 100644 index 0000000..23fcbd2 --- /dev/null +++ b/src/api/demo/demo/index.ts @@ -0,0 +1,63 @@ +import type { DemoForm, DemoQuery, DemoVO } from '@/api/demo/demo/types'; +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 查询测试单列表 + * @param query + * @returns {*} + */ +export const listDemo = (query?: DemoQuery): AxiosPromise> => { + return request({ + url: '/demo/demo/list', + method: 'get', + params: query + }); +}; + +/** + * 查询测试单详细 + * @param id + */ +export const getDemo = (id: string | number): AxiosPromise => { + return request({ + url: '/demo/demo/' + id, + method: 'get' + }); +}; + +/** + * 新增测试单 + * @param data + */ +export const addDemo = (data: DemoForm) => { + return request({ + url: '/demo/demo', + method: 'post', + data: data + }); +}; + +/** + * 修改测试单 + * @param data + */ +export const updateDemo = (data: DemoForm) => { + return request({ + url: '/demo/demo', + method: 'put', + data: data + }); +}; + +/** + * 删除测试单 + * @param id + */ +export const delDemo = (id: string | number | Array) => { + return request({ + url: '/demo/demo/' + id, + method: 'delete' + }); +}; diff --git a/src/api/demo/demo/types.ts b/src/api/demo/demo/types.ts new file mode 100644 index 0000000..ea51d32 --- /dev/null +++ b/src/api/demo/demo/types.ts @@ -0,0 +1,90 @@ +export interface DemoVO { + /** + * 主键 + */ + id: string | number; + + /** + * 部门id + */ + deptId: string | number; + + /** + * 用户id + */ + userId: string | number; + + /** + * 排序号 + */ + orderNum: number; + + /** + * key键 + */ + testKey: string; + + /** + * 值 + */ + value: string; +} + +export interface DemoForm extends BaseEntity { + /** + * 主键 + */ + id?: string | number; + + /** + * 部门id + */ + deptId?: string | number; + + /** + * 用户id + */ + userId?: string | number; + + /** + * 排序号 + */ + orderNum?: number; + + /** + * key键 + */ + testKey?: string; + + /** + * 值 + */ + value?: string; +} + +export interface DemoQuery extends PageQuery { + /** + * 部门id + */ + deptId?: string | number; + + /** + * 用户id + */ + userId?: string | number; + + /** + * 排序号 + */ + orderNum?: number; + + /** + * key键 + */ + testKey?: string; + + /** + * 值 + */ + value?: string; +} diff --git a/src/api/demo/tree/index.ts b/src/api/demo/tree/index.ts new file mode 100644 index 0000000..69ebcba --- /dev/null +++ b/src/api/demo/tree/index.ts @@ -0,0 +1,62 @@ +import type { TreeForm, TreeQuery, TreeVO } from '@/api/demo/tree/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 查询测试树列表 + * @param query + * @returns {*} + */ +export const listTree = (query?: TreeQuery): AxiosPromise => { + return request({ + url: '/demo/tree/list', + method: 'get', + params: query + }); +}; + +/** + * 查询测试树详细 + * @param id + */ +export const getTree = (id: string | number): AxiosPromise => { + return request({ + url: '/demo/tree/' + id, + method: 'get' + }); +}; + +/** + * 新增测试树 + * @param data + */ +export const addTree = (data: TreeForm) => { + return request({ + url: '/demo/tree', + method: 'post', + data: data + }); +}; + +/** + * 修改测试树 + * @param data + */ +export const updateTree = (data: TreeForm) => { + return request({ + url: '/demo/tree', + method: 'put', + data: data + }); +}; + +/** + * 删除测试树 + * @param id + */ +export const delTree = (id: string | number | Array) => { + return request({ + url: '/demo/tree/' + id, + method: 'delete' + }); +}; diff --git a/src/api/demo/tree/types.ts b/src/api/demo/tree/types.ts new file mode 100644 index 0000000..e164d8b --- /dev/null +++ b/src/api/demo/tree/types.ts @@ -0,0 +1,80 @@ +export interface TreeVO { + /** + * 主键 + */ + id: string | number; + + /** + * 父id + */ + parentId: string | number; + + /** + * 部门id + */ + deptId: string | number; + + /** + * 用户id + */ + userId: string | number; + + /** + * 值 + */ + treeName: string; + + /** + * 子对象 + */ + children: TreeVO[]; +} + +export interface TreeForm extends BaseEntity { + /** + * 主键 + */ + id?: string | number; + + /** + * 父id + */ + parentId?: string | number; + + /** + * 部门id + */ + deptId?: string | number; + + /** + * 用户id + */ + userId?: string | number; + + /** + * 值 + */ + treeName?: string; +} + +export interface TreeQuery { + /** + * 父id + */ + parentId?: string | number; + + /** + * 部门id + */ + deptId?: string | number; + + /** + * 用户id + */ + userId?: string | number; + + /** + * 值 + */ + treeName?: string; +} diff --git a/src/api/login.ts b/src/api/login.ts new file mode 100644 index 0000000..646677a --- /dev/null +++ b/src/api/login.ts @@ -0,0 +1,107 @@ +import type { UserInfo } from '@/api/system/user/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import { closePush } from '@/utils/push'; +import request from '@/utils/request'; +import type { LoginData, LoginResult, VerifyCodeResult } from './types'; + +// pc端固定客户端授权id +const clientId = import.meta.env.VITE_APP_CLIENT_ID; + +/** + * @param data {LoginData} + * @returns + */ +export function login(data: LoginData): AxiosPromise { + const params = { + ...data, + clientId: data.clientId || clientId, + grantType: data.grantType || 'password' + }; + return request({ + url: '/auth/login', + headers: { + isToken: false, + isEncrypt: true, + repeatSubmit: false + }, + method: 'post', + data: params + }); +} + +// 注册方法 +export function register(data: any) { + const params = { + ...data, + clientId: clientId, + grantType: 'password' + }; + return request({ + url: '/auth/register', + headers: { + isToken: false, + isEncrypt: true, + repeatSubmit: false + }, + method: 'post', + data: params + }); +} + +/** + * 注销 + */ +export function logout() { + closePush(); + if ( + import.meta.env.VITE_APP_MESSAGE_ENABLED === 'true' && + import.meta.env.VITE_APP_MESSAGE_TRANSPORT.toLowerCase() === 'sse' + ) { + request({ + url: import.meta.env.VITE_APP_MESSAGE_PATH + '/close', + method: 'get' + }); + } + return request({ + url: '/auth/logout', + method: 'post' + }); +} + +/** + * 获取验证码 + */ +export function getCodeImg(): AxiosPromise { + return request({ + url: '/auth/code', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }); +} + +/** + * 第三方登录 + */ +export function callback(data: LoginData): AxiosPromise { + const LoginData = { + ...data, + clientId: clientId, + grantType: 'social' + }; + return request({ + url: '/auth/social/callback', + method: 'post', + data: LoginData + }); +} + +// 获取用户详细信息 +export function getInfo(): AxiosPromise { + return request({ + url: '/system/user/getInfo', + method: 'get' + }); +} diff --git a/src/api/menu.ts b/src/api/menu.ts new file mode 100644 index 0000000..3be30a0 --- /dev/null +++ b/src/api/menu.ts @@ -0,0 +1,11 @@ +import type { RouteRecordRaw } from 'vue-router'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +// 获取路由 +export function getRouters(): AxiosPromise { + return request({ + url: '/system/menu/getRouters', + method: 'get' + }); +} diff --git a/src/api/monitor/cache/index.ts b/src/api/monitor/cache/index.ts new file mode 100644 index 0000000..4f2efb2 --- /dev/null +++ b/src/api/monitor/cache/index.ts @@ -0,0 +1,12 @@ +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { CacheVO } from './types'; + +// 查询缓存详细 +export function getCache(): AxiosPromise { + return request({ + url: '/monitor/cache', + method: 'get' + }); +} + diff --git a/src/api/monitor/cache/types.ts b/src/api/monitor/cache/types.ts new file mode 100644 index 0000000..4017b65 --- /dev/null +++ b/src/api/monitor/cache/types.ts @@ -0,0 +1,7 @@ +export interface CacheVO { + commandStats: Array<{ name: string; value: string }>; + + dbSize: number; + + info: { [key: string]: string }; +} diff --git a/src/api/monitor/logininfo/index.ts b/src/api/monitor/logininfo/index.ts new file mode 100644 index 0000000..23e30a0 --- /dev/null +++ b/src/api/monitor/logininfo/index.ts @@ -0,0 +1,37 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { LoginInfoQuery, LoginInfoVO } from './types'; + +// 查询登录日志列表 +export function list(query: LoginInfoQuery): AxiosPromise> { + return request({ + url: '/monitor/loginInfo/list', + method: 'get', + params: query + }); +} + +// 删除登录日志 +export function delLoginInfo(infoId: string | number | Array) { + return request({ + url: '/monitor/loginInfo/' + infoId, + method: 'delete' + }); +} + +// 解锁用户登录状态 +export function unlockLoginInfo(userName: string | Array) { + return request({ + url: '/monitor/loginInfo/unlock/' + userName, + method: 'get' + }); +} + +// 清空登录日志 +export function cleanLoginInfo() { + return request({ + url: '/monitor/loginInfo/clean', + method: 'delete' + }); +} diff --git a/src/api/monitor/logininfo/types.ts b/src/api/monitor/logininfo/types.ts new file mode 100644 index 0000000..202c779 --- /dev/null +++ b/src/api/monitor/logininfo/types.ts @@ -0,0 +1,20 @@ +export interface LoginInfoVO { + infoId: string | number; + tenantId: string | number; + userName: string; + status: string; + ipaddr: string; + loginLocation: string; + browser: string; + os: string; + msg: string; + loginTime: string; +} + +export interface LoginInfoQuery extends PageQuery { + ipaddr: string; + userName: string; + status: string; + orderByColumn: string; + isAsc: string; +} diff --git a/src/api/monitor/online/index.ts b/src/api/monitor/online/index.ts new file mode 100644 index 0000000..6fbb841 --- /dev/null +++ b/src/api/monitor/online/index.ts @@ -0,0 +1,37 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { OnlineQuery, OnlineVO } from './types'; + +// 查询在线用户列表 +export function list(query: OnlineQuery): AxiosPromise> { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }); +} + +// 强退用户 +export function forceLogout(tokenId: string) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }); +} + +// 获取当前用户登录在线设备 +export function getOnline() { + return request({ + url: '/monitor/online', + method: 'get' + }); +} + +// 删除当前在线设备 +export function delOnline(tokenId: string) { + return request({ + url: '/monitor/online/myself/' + tokenId, + method: 'delete' + }); +} diff --git a/src/api/monitor/online/types.ts b/src/api/monitor/online/types.ts new file mode 100644 index 0000000..8c0ec27 --- /dev/null +++ b/src/api/monitor/online/types.ts @@ -0,0 +1,15 @@ +export interface OnlineQuery extends PageQuery { + ipaddr: string; + userName: string; +} + +export interface OnlineVO extends BaseEntity { + tokenId: string; + deptName: string; + userName: string; + ipaddr: string; + loginLocation: string; + browser: string; + os: string; + loginTime: number; +} diff --git a/src/api/monitor/operlog/index.ts b/src/api/monitor/operlog/index.ts new file mode 100644 index 0000000..006011b --- /dev/null +++ b/src/api/monitor/operlog/index.ts @@ -0,0 +1,29 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { OperLogQuery, OperLogVO } from './types'; + +// 查询操作日志列表 +export function list(query: OperLogQuery): AxiosPromise> { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }); +} + +// 删除操作日志 +export function delOperlog(operId: string | number | Array) { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }); +} + +// 清空操作日志 +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }); +} diff --git a/src/api/monitor/operlog/types.ts b/src/api/monitor/operlog/types.ts new file mode 100644 index 0000000..ad889a2 --- /dev/null +++ b/src/api/monitor/operlog/types.ts @@ -0,0 +1,71 @@ +export interface OperLogQuery extends PageQuery { + operIp: string; + title: string; + operName: string; + userId: string; + deptId: string; + clientKey: string; + deviceType: string; + browser: string; + os: string; + businessType: string; + status: string; + orderByColumn: string; + isAsc: string; +} + +export interface OperLogVO extends BaseEntity { + operId: string | number; + tenantId: string; + title: string; + businessType: number; + businessTypes: number[] | undefined; + method: string; + requestMethod: string; + operatorType: number; + operName: string; + userId: string | number; + deptId: string | number; + deptName: string; + clientKey: string; + deviceType: string; + browser: string; + os: string; + operUrl: string; + operIp: string; + operLocation: string; + operParam: string; + jsonResult: string; + status: number; + errorMsg: string; + operTime: string; + costTime: number; +} + +export interface OperLogForm { + operId: number | string | undefined; + tenantId: string | number | undefined; + title: string; + businessType: number; + businessTypes: number[] | undefined; + method: string; + requestMethod: string; + operatorType: number; + operName: string; + userId: string | number | undefined; + deptId: string | number | undefined; + deptName: string; + clientKey: string; + deviceType: string; + browser: string; + os: string; + operUrl: string; + operIp: string; + operLocation: string; + operParam: string; + jsonResult: string; + status: number; + errorMsg: string; + operTime: string; + costTime: number; +} diff --git a/src/api/system/client/index.ts b/src/api/system/client/index.ts new file mode 100644 index 0000000..cff3916 --- /dev/null +++ b/src/api/system/client/index.ts @@ -0,0 +1,81 @@ +import type { ClientForm, ClientQuery, ClientVO } from '@/api/system/client/types'; +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 查询客户端管理列表 + * @param query + * @returns {*} + */ + +export const listClient = (query?: ClientQuery): AxiosPromise> => { + return request({ + url: '/system/client/list', + method: 'get', + params: query + }); +}; + +/** + * 查询客户端管理详细 + * @param id + */ +export const getClient = (id: string | number): AxiosPromise => { + return request({ + url: '/system/client/' + id, + method: 'get' + }); +}; + +/** + * 新增客户端管理 + * @param data + */ +export const addClient = (data: ClientForm) => { + return request({ + url: '/system/client', + method: 'post', + data: data + }); +}; + +/** + * 修改客户端管理 + * @param data + */ +export const updateClient = (data: ClientForm) => { + return request({ + url: '/system/client', + method: 'put', + data: data + }); +}; + +/** + * 删除客户端管理 + * @param id + */ +export const delClient = (id: string | number | Array) => { + return request({ + url: '/system/client/' + id, + method: 'delete' + }); +}; + +/** + * 状态修改 + * @param clientId 客户端id + * @param status 状态 + */ +export function changeStatus(clientId: string, status: string) { + const data = { + clientId, + status + }; + return request({ + url: '/system/client/changeStatus', + method: 'put', + data: data + }); +} diff --git a/src/api/system/client/types.ts b/src/api/system/client/types.ts new file mode 100644 index 0000000..c1d02d4 --- /dev/null +++ b/src/api/system/client/types.ts @@ -0,0 +1,185 @@ +export interface ClientVO { + /** + * id + */ + id: string | number; + + /** + * 客户端id + */ + clientId: string; + + /** + * 客户端key + */ + clientKey: string; + + /** + * 客户端秘钥 + */ + clientSecret: string; + + /** + * 授权类型 + */ + grantTypeList: string[]; + + /** + * 设备类型 + */ + deviceType: string; + + /** + * 允许访问路径 + */ + accessPath?: string; + + /** + * 允许访问路径列表 + */ + accessPathList?: string[]; + + /** + * IP白名单 + */ + ipWhitelist?: string; + + /** + * IP白名单列表 + */ + ipWhitelistList?: string[]; + + /** + * token活跃超时时间 + */ + activeTimeout: number; + + /** + * token固定超时 + */ + timeout: number; + + /** + * 状态(0正常 1停用) + */ + status: string; +} + +export interface ClientForm extends BaseEntity { + /** + * id + */ + id?: string | number; + + /** + * 客户端id + */ + clientId?: string | number; + + /** + * 客户端key + */ + clientKey?: string; + + /** + * 客户端秘钥 + */ + clientSecret?: string; + + /** + * 授权类型 + */ + grantTypeList?: string[]; + + /** + * 设备类型 + */ + deviceType?: string; + + /** + * 允许访问路径 + */ + accessPath?: string; + + /** + * 允许访问路径列表 + */ + accessPathList?: string[]; + + /** + * IP白名单 + */ + ipWhitelist?: string; + + /** + * IP白名单列表 + */ + ipWhitelistList?: string[]; + + /** + * token活跃超时时间 + */ + activeTimeout?: number; + + /** + * token固定超时 + */ + timeout?: number; + + /** + * 状态(0正常 1停用) + */ + status?: string; +} + +export interface ClientQuery extends PageQuery { + /** + * 客户端id + */ + clientId?: string | number; + + /** + * 客户端key + */ + clientKey?: string; + + /** + * 客户端秘钥 + */ + clientSecret?: string; + + /** + * 授权类型 + */ + grantType?: string; + + /** + * 设备类型 + */ + deviceType?: string; + + /** + * 允许访问路径 + */ + accessPath?: string; + + /** + * IP白名单 + */ + ipWhitelist?: string; + + /** + * token活跃超时时间 + */ + activeTimeout?: number; + + /** + * token固定超时 + */ + timeout?: number; + + /** + * 状态(0正常 1停用) + */ + status?: string; +} diff --git a/src/api/system/config/index.ts b/src/api/system/config/index.ts new file mode 100644 index 0000000..11778a4 --- /dev/null +++ b/src/api/system/config/index.ts @@ -0,0 +1,75 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { ConfigForm, ConfigQuery, ConfigVO } from './types'; + +// 查询参数列表 +export function listConfig(query: ConfigQuery): AxiosPromise> { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }); +} + +// 查询参数详细 +export function getConfig(configId: string | number): AxiosPromise { + return request({ + url: '/system/config/' + configId, + method: 'get' + }); +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey: string): AxiosPromise { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }); +} + +// 新增参数配置 +export function addConfig(data: ConfigForm) { + return request({ + url: '/system/config', + method: 'post', + data: data + }); +} + +// 修改参数配置 +export function updateConfig(data: ConfigForm) { + return request({ + url: '/system/config', + method: 'put', + data: data + }); +} + +// 修改参数配置 +export function updateConfigByKey(key: string, value: any) { + return request({ + url: '/system/config/updateByKey', + method: 'put', + data: { + configKey: key, + configValue: value + } + }); +} + +// 删除参数配置 +export function delConfig(configId: string | number | Array) { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }); +} + +// 刷新参数缓存 +export function refreshCache() { + return request({ + url: '/system/config/refreshCache', + method: 'delete' + }); +} diff --git a/src/api/system/config/types.ts b/src/api/system/config/types.ts new file mode 100644 index 0000000..b68f073 --- /dev/null +++ b/src/api/system/config/types.ts @@ -0,0 +1,23 @@ +export interface ConfigVO extends BaseEntity { + configId: number | string; + configName: string; + configKey: string; + configValue: string; + configType: string; + remark: string; +} + +export interface ConfigForm { + configId: number | string | undefined; + configName: string; + configKey: string; + configValue: string; + configType: string; + remark: string; +} + +export interface ConfigQuery extends PageQuery { + configName: string; + configKey: string; + configType: string; +} diff --git a/src/api/system/dept/index.ts b/src/api/system/dept/index.ts new file mode 100644 index 0000000..5c2ed78 --- /dev/null +++ b/src/api/system/dept/index.ts @@ -0,0 +1,65 @@ +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import { type DeptForm, type DeptQuery, type DeptVO } from './types'; + +// 查询部门列表 +export const listDept = (query?: DeptQuery) => { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }); +}; + +/** + * 通过deptIds查询部门 + * @param deptIds + */ +export const optionSelect = (deptIds: (number | string)[]): AxiosPromise => { + return request({ + url: '/system/dept/optionselect?deptIds=' + deptIds, + method: 'get' + }); +}; + +// 查询部门列表(排除节点) +export const listDeptExcludeChild = (deptId: string | number): AxiosPromise => { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }); +}; + +// 查询部门详细 +export const getDept = (deptId: string | number): AxiosPromise => { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }); +}; + +// 新增部门 +export const addDept = (data: DeptForm) => { + return request({ + url: '/system/dept', + method: 'post', + data: data + }); +}; + +// 修改部门 +export const updateDept = (data: DeptForm) => { + return request({ + url: '/system/dept', + method: 'put', + data: data + }); +}; + +// 删除部门 +export const delDept = (deptId: number | string) => { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }); +}; diff --git a/src/api/system/dept/types.ts b/src/api/system/dept/types.ts new file mode 100644 index 0000000..adaefd2 --- /dev/null +++ b/src/api/system/dept/types.ts @@ -0,0 +1,60 @@ +/** + * 部门查询参数 + */ +export interface DeptQuery extends PageQuery { + deptName?: string; + deptCategory?: string; + status?: number; +} + +/** + * 部门类型 + */ +export interface DeptVO extends BaseEntity { + id: number | string; + parentName: string; + parentId: number | string; + children: DeptVO[]; + deptId: number | string; + deptName: string; + deptCategory: string; + orderNum: number; + leader: string; + phone: string; + email: string; + status: string; + delFlag: string; + ancestors: string; + menuId: string | number; +} + +/** + * 部门类型 + */ +export interface DeptTreeVO extends BaseEntity { + id: number | string; + label: string; + parentId: number | string; + weight: number; + children: DeptTreeVO[]; + disabled: boolean; +} + +/** + * 部门表单类型 + */ +export interface DeptForm { + parentName?: string; + parentId?: number | string; + children?: DeptForm[]; + deptId?: number | string; + deptName?: string; + deptCategory?: string; + orderNum?: number; + leader?: string; + phone?: string; + email?: string; + status?: string; + delFlag?: string; + ancestors?: string; +} diff --git a/src/api/system/dict/data/index.ts b/src/api/system/dict/data/index.ts new file mode 100644 index 0000000..ad6f088 --- /dev/null +++ b/src/api/system/dict/data/index.ts @@ -0,0 +1,54 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { DictDataForm, DictDataQuery, DictDataVO } from './types'; +// 根据字典类型查询字典数据信息 +export function getDicts(dictType: string): AxiosPromise { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }); +} + +// 查询字典数据列表 +export function listData(query: DictDataQuery): AxiosPromise> { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }); +} + +// 查询字典数据详细 +export function getData(dictCode: string | number): AxiosPromise { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }); +} + +// 新增字典数据 +export function addData(data: DictDataForm) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }); +} + +// 修改字典数据 +export function updateData(data: DictDataForm) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }); +} + +// 删除字典数据 +export function delData(dictCode: string | number | Array) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }); +} diff --git a/src/api/system/dict/data/types.ts b/src/api/system/dict/data/types.ts new file mode 100644 index 0000000..e4abb9b --- /dev/null +++ b/src/api/system/dict/data/types.ts @@ -0,0 +1,26 @@ +export interface DictDataQuery extends PageQuery { + dictName: string; + dictType: string; + dictLabel: string; +} + +export interface DictDataVO extends BaseEntity { + dictCode: string; + dictLabel: string; + dictValue: string; + cssClass: string; + listClass: ElTagType; + dictSort: number; + remark: string; +} + +export interface DictDataForm { + dictType?: string; + dictCode: string | undefined; + dictLabel: string; + dictValue: string; + cssClass: string; + listClass: ElTagType; + dictSort: number; + remark: string; +} diff --git a/src/api/system/dict/type/index.ts b/src/api/system/dict/type/index.ts new file mode 100644 index 0000000..ca69c59 --- /dev/null +++ b/src/api/system/dict/type/index.ts @@ -0,0 +1,63 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { DictTypeForm, DictTypeQuery, DictTypeVO } from './types'; + +// 查询字典类型列表 +export function listType(query: DictTypeQuery): AxiosPromise> { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }); +} + +// 查询字典类型详细 +export function getType(dictId: number | string): AxiosPromise { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }); +} + +// 新增字典类型 +export function addType(data: DictTypeForm) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }); +} + +// 修改字典类型 +export function updateType(data: DictTypeForm) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }); +} + +// 删除字典类型 +export function delType(dictId: string | number | Array) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }); +} + +// 刷新字典缓存 +export function refreshCache() { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete' + }); +} + +// 获取字典选择框列表 +export function optionselect(): AxiosPromise { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }); +} diff --git a/src/api/system/dict/type/types.ts b/src/api/system/dict/type/types.ts new file mode 100644 index 0000000..4987dbf --- /dev/null +++ b/src/api/system/dict/type/types.ts @@ -0,0 +1,18 @@ +export interface DictTypeVO extends BaseEntity { + dictId: number | string; + dictName: string; + dictType: string; + remark: string; +} + +export interface DictTypeForm { + dictId: number | string | undefined; + dictName: string; + dictType: string; + remark: string; +} + +export interface DictTypeQuery extends PageQuery { + dictName: string; + dictType: string; +} diff --git a/src/api/system/menu/index.ts b/src/api/system/menu/index.ts new file mode 100644 index 0000000..39caa15 --- /dev/null +++ b/src/api/system/menu/index.ts @@ -0,0 +1,70 @@ +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { MenuForm, MenuQuery, MenuTreeOption, MenuVO, RoleMenuTree } from './types'; + +// 查询菜单列表 +export const listMenu = (query?: MenuQuery): AxiosPromise => { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }); +}; + +// 查询菜单详细 +export const getMenu = (menuId: string | number): AxiosPromise => { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }); +}; + +// 查询菜单下拉树结构 +export const treeselect = (): AxiosPromise => { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }); +}; + +// 根据角色ID查询菜单下拉树结构 +export const roleMenuTreeselect = (roleId: string | number): AxiosPromise => { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }); +}; + +// 新增菜单 +export const addMenu = (data: MenuForm) => { + return request({ + url: '/system/menu', + method: 'post', + data: data + }); +}; + +// 修改菜单 +export const updateMenu = (data: MenuForm) => { + return request({ + url: '/system/menu', + method: 'put', + data: data + }); +}; + +// 删除菜单 +export const delMenu = (menuId: string | number) => { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }); +}; + +// 级联删除菜单 +export const cascadeDelMenu = (menuIds: Array) => { + return request({ + url: '/system/menu/cascade/' + menuIds, + method: 'delete' + }); +}; diff --git a/src/api/system/menu/types.ts b/src/api/system/menu/types.ts new file mode 100644 index 0000000..f106bc5 --- /dev/null +++ b/src/api/system/menu/types.ts @@ -0,0 +1,89 @@ +import type { MenuTypeEnum } from '@/enums/MenuTypeEnum'; + +/** + * 菜单树形结构类型 + */ +export interface MenuTreeOption { + id: string | number; + label: string; + parentId: string | number; + weight: number; + menuType?: MenuTypeEnum | string; + visible?: string; + status?: string; + disabled?: boolean; + children?: MenuTreeOption[]; +} + +export interface RoleMenuTree { + menus: MenuTreeOption[]; + checkedKeys: Array; +} + +/** + * 角色菜单分配中的按钮节点类型 + */ +export interface RoleMenuButtonOption { + menuId: string | number; + menuName: string; + parentId: string | number; + perms?: string; + status?: string; + disabled?: boolean; +} + +/** + * 菜单查询参数类型 + */ +export interface MenuQuery { + keywords?: string; + menuName?: string; + status?: string; +} + +/** + * 菜单视图对象类型 + */ +export interface MenuVO extends BaseEntity { + parentName: string; + parentId: string | number; + children: MenuVO[]; + menuId: string | number; + menuName: string; + orderNum: number; + path: string; + component: string; + queryParam: string; + isFrame: string; + isCache: string; + menuType: MenuTypeEnum; + visible: string; + status: string; + icon: string; + activeMenu: string; + ext: string; + remark: string; +} + +export interface MenuForm { + parentName?: string; + parentId?: string | number; + children?: MenuForm[]; + menuId?: string | number; + menuName: string; + orderNum: number; + path: string; + component?: string; + queryParam?: string; + isFrame?: string; + isCache?: string; + menuType?: MenuTypeEnum; + visible?: string; + status?: string; + icon?: string; + activeMenu?: string; + ext?: string; + remark?: string; + query?: string; + perms?: string; +} diff --git a/src/api/system/message/index.ts b/src/api/system/message/index.ts new file mode 100644 index 0000000..eb5c0b1 --- /dev/null +++ b/src/api/system/message/index.ts @@ -0,0 +1,10 @@ +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { MessageBoxVO } from './types'; + +export function getMessageBox(): AxiosPromise { + return request({ + url: '/resource/message/box', + method: 'get' + }); +} diff --git a/src/api/system/message/types.ts b/src/api/system/message/types.ts new file mode 100644 index 0000000..18a6591 --- /dev/null +++ b/src/api/system/message/types.ts @@ -0,0 +1,17 @@ +export interface MessageVO extends BaseEntity { + messageId: number | string; + category: string; + type: string; + source: string; + title: string; + message: string; + content?: string; + data?: Record | null; + path?: string; +} + +export interface MessageBoxVO { + systemList: MessageVO[]; + noticeList: MessageVO[]; + workflowList: MessageVO[]; +} diff --git a/src/api/system/notice/index.ts b/src/api/system/notice/index.ts new file mode 100644 index 0000000..e729ded --- /dev/null +++ b/src/api/system/notice/index.ts @@ -0,0 +1,46 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { NoticeForm, NoticeQuery, NoticeVO } from './types'; +// 查询公告列表 +export function listNotice(query: NoticeQuery): AxiosPromise> { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }); +} + +// 查询公告详细 +export function getNotice(noticeId: string | number): AxiosPromise { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }); +} + +// 新增公告 +export function addNotice(data: NoticeForm) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }); +} + +// 修改公告 +export function updateNotice(data: NoticeForm) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }); +} + +// 删除公告 +export function delNotice(noticeId: string | number | Array) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }); +} diff --git a/src/api/system/notice/types.ts b/src/api/system/notice/types.ts new file mode 100644 index 0000000..abfd5b2 --- /dev/null +++ b/src/api/system/notice/types.ts @@ -0,0 +1,26 @@ +export interface NoticeVO extends BaseEntity { + noticeId: number; + noticeTitle: string; + noticeType: string; + noticeContent: string; + status: string; + remark: string; + createByName: string; +} + +export interface NoticeQuery extends PageQuery { + noticeTitle: string; + createByName: string; + status: string; + noticeType: string; +} + +export interface NoticeForm { + noticeId: number | string | undefined; + noticeTitle: string; + noticeType: string; + noticeContent: string; + status: string; + remark: string; + createByName: string; +} diff --git a/src/api/system/oss/index.ts b/src/api/system/oss/index.ts new file mode 100644 index 0000000..f7192ab --- /dev/null +++ b/src/api/system/oss/index.ts @@ -0,0 +1,38 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { OssQuery, OssUploadVO, OssVO } from './types'; + +// 查询OSS对象存储列表 +export function listOss(query: OssQuery): AxiosPromise> { + return request({ + url: '/resource/oss/list', + method: 'get', + params: query + }); +} + +// 查询OSS对象基于id串 +export function listByIds(ossId: string | number): AxiosPromise { + return request({ + url: '/resource/oss/listByIds/' + ossId, + method: 'get' + }); +} + +// 上传OSS对象存储 +export function uploadOss(data: FormData): AxiosPromise { + return request({ + url: '/resource/oss/upload', + method: 'post', + data + }); +} + +// 删除OSS对象存储 +export function delOss(ossId: string | number | Array) { + return request({ + url: '/resource/oss/' + ossId, + method: 'delete' + }); +} diff --git a/src/api/system/oss/types.ts b/src/api/system/oss/types.ts new file mode 100644 index 0000000..befb3ab --- /dev/null +++ b/src/api/system/oss/types.ts @@ -0,0 +1,42 @@ +export interface OssVO extends BaseEntity { + ossId: string | number; + fileName: string; + originalName: string; + fileSuffix: string; + url: string; + createByName: string; + service: string; +} + +export interface OssUploadVO { + url: string; + fileName: string; + ossId: string; +} + +export interface OssQuery extends PageQuery { + fileName: string; + originalName: string; + fileSuffix: string; + createTime: string; + service: string; + orderByColumn: string; + isAsc: string; +} +export interface OssForm { + file: undefined | string; +} + +export interface SysOssExt { + bizType?: string; + fileSize?: number; + contentType?: string; + source?: string; + uploadIp?: string; + remark?: string; + tags?: string[]; + refId?: string; + refType?: string; + isTemp?: boolean; + md5?: string; +} diff --git a/src/api/system/ossConfig/index.ts b/src/api/system/ossConfig/index.ts new file mode 100644 index 0000000..1c999e1 --- /dev/null +++ b/src/api/system/ossConfig/index.ts @@ -0,0 +1,61 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { OssConfigForm, OssConfigQuery, OssConfigVO } from './types'; + +// 查询对象存储配置列表 +export function listOssConfig(query: OssConfigQuery): AxiosPromise> { + return request({ + url: '/resource/oss/config/list', + method: 'get', + params: query + }); +} + +// 查询对象存储配置详细 +export function getOssConfig(ossConfigId: string | number): AxiosPromise { + return request({ + url: '/resource/oss/config/' + ossConfigId, + method: 'get' + }); +} + +// 新增对象存储配置 +export function addOssConfig(data: OssConfigForm) { + return request({ + url: '/resource/oss/config', + method: 'post', + data: data + }); +} + +// 修改对象存储配置 +export function updateOssConfig(data: OssConfigForm) { + return request({ + url: '/resource/oss/config', + method: 'put', + data: data + }); +} + +// 删除对象存储配置 +export function delOssConfig(ossConfigId: string | number | Array) { + return request({ + url: '/resource/oss/config/' + ossConfigId, + method: 'delete' + }); +} + +// 对象存储状态修改 +export function changeOssConfigStatus(ossConfigId: string | number, status: string, configKey: string) { + const data = { + ossConfigId, + status, + configKey + }; + return request({ + url: '/resource/oss/config/changeStatus', + method: 'put', + data: data + }); +} diff --git a/src/api/system/ossConfig/types.ts b/src/api/system/ossConfig/types.ts new file mode 100644 index 0000000..870b2ee --- /dev/null +++ b/src/api/system/ossConfig/types.ts @@ -0,0 +1,38 @@ +export interface OssConfigVO extends BaseEntity { + ossConfigId: number | string; + configKey: string; + accessKey: string; + secretKey: string; + bucketName: string; + prefix: string; + endpoint: string; + domainUrl: string; + isHttps: string; + region: string; + status: string; + ext1: string; + remark: string; + accessPolicy: string; +} + +export interface OssConfigQuery extends PageQuery { + configKey: string; + bucketName: string; + status: string; +} + +export interface OssConfigForm { + ossConfigId: string | number | undefined; + configKey: string; + accessKey: string; + secretKey: string; + bucketName: string; + prefix: string; + endpoint: string; + domainUrl: string; + isHttps: string; + accessPolicy: string; + region: string; + status: string; + remark: string; +} diff --git a/src/api/system/post/index.ts b/src/api/system/post/index.ts new file mode 100644 index 0000000..2b52f06 --- /dev/null +++ b/src/api/system/post/index.ts @@ -0,0 +1,70 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { DeptTreeVO } from '../dept/types'; +import type { PostForm, PostQuery, PostVO } from './types'; + +// 查询岗位列表 +export function listPost(query: PostQuery): AxiosPromise> { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }); +} + +// 查询岗位详细 +export function getPost(postId: string | number): AxiosPromise { + return request({ + url: '/system/post/' + postId, + method: 'get' + }); +} + +// 获取岗位选择框列表 +export function optionselect(deptId?: number | string, postIds?: (number | string)[]): AxiosPromise { + return request({ + url: '/system/post/optionselect', + method: 'get', + params: { + postIds: postIds, + deptId: deptId + } + }); +} + +// 新增岗位 +export function addPost(data: PostForm) { + return request({ + url: '/system/post', + method: 'post', + data: data + }); +} + +// 修改岗位 +export function updatePost(data: PostForm) { + return request({ + url: '/system/post', + method: 'put', + data: data + }); +} + +// 删除岗位 +export function delPost(postId: string | number | (string | number)[]) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }); +} + +/** + * 查询部门下拉树结构 + */ +export const deptTreeSelect = (): AxiosPromise => { + return request({ + url: '/system/post/deptTree', + method: 'get' + }); +}; diff --git a/src/api/system/post/types.ts b/src/api/system/post/types.ts new file mode 100644 index 0000000..45a0540 --- /dev/null +++ b/src/api/system/post/types.ts @@ -0,0 +1,31 @@ +export interface PostVO extends BaseEntity { + postId: number | string; + deptId: number | string; + postCode: string; + postName: string; + postCategory: string; + deptName: string; + postSort: number; + status: string; + remark: string; +} + +export interface PostForm { + postId: number | string | undefined; + deptId: number | string | undefined; + postCode: string; + postName: string; + postCategory: string; + postSort: number; + status: string; + remark: string; +} + +export interface PostQuery extends PageQuery { + deptId: number | string; + belongDeptId: number | string; + postCode: string; + postName: string; + postCategory: string; + status: string; +} diff --git a/src/api/system/role/index.ts b/src/api/system/role/index.ts new file mode 100644 index 0000000..1e4a937 --- /dev/null +++ b/src/api/system/role/index.ts @@ -0,0 +1,160 @@ +import type { UserQuery, UserVO } from '@/api/system/user/types'; +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { RoleDeptTree, RoleQuery, RoleVO } from './types'; + +export const listRole = (query: RoleQuery): AxiosPromise> => { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }); +}; + +/** + * 通过roleIds查询角色 + * @param roleIds + */ +export const optionSelect = (roleIds: (number | string)[]): AxiosPromise => { + return request({ + url: '/system/role/optionselect?roleIds=' + roleIds, + method: 'get' + }); +}; + +/** + * 查询角色详细 + */ +export const getRole = (roleId: string | number): AxiosPromise => { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }); +}; + +/** + * 新增角色 + */ +export const addRole = (data: any) => { + return request({ + url: '/system/role', + method: 'post', + data: data + }); +}; + +/** + * 修改角色基础信息 + * @param data + */ +export const updateRole = (data: any) => { + return request({ + url: '/system/role', + method: 'put', + data: data + }); +}; + +/** + * 修改角色权限(菜单权限 + 数据权限) + */ +export const updateRolePermission = (data: any) => { + return request({ + url: '/system/role/permission', + method: 'put', + data: data + }); +}; + +/** + * 角色状态修改 + */ +export const changeRoleStatus = (roleId: string | number, status: string) => { + const data = { + roleId, + status + }; + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }); +}; + +/** + * 删除角色 + */ +export const delRole = (roleId: Array | string | number) => { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }); +}; + +/** + * 查询角色已授权用户列表 + */ +export const allocatedUserList = (query: UserQuery): AxiosPromise> => { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query + }); +}; + +/** + * 查询角色未授权用户列表 + */ +export const unallocatedUserList = (query: UserQuery): AxiosPromise> => { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query + }); +}; + +/** + * 取消用户授权角色 + */ +export const authUserCancel = (data: any) => { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data + }); +}; + +/** + * 批量取消用户授权角色 + */ +export const authUserCancelAll = (data: any) => { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data + }); +}; + +/** + * 授权用户选择 + */ +export const authUserSelectAll = (data: any) => { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data + }); +}; +// 根据角色ID查询部门树结构 +export const deptTreeSelect = (roleId: string | number): AxiosPromise => { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get' + }); +}; + +export default { + optionSelect, + listRole +}; diff --git a/src/api/system/role/types.ts b/src/api/system/role/types.ts new file mode 100644 index 0000000..7dbb6ff --- /dev/null +++ b/src/api/system/role/types.ts @@ -0,0 +1,52 @@ +/** + * 菜单树形结构类型 + */ +export interface DeptTreeOption { + id: string; + label: string; + parentId: string; + weight: number; + children?: DeptTreeOption[]; +} + +export interface RoleDeptTree { + checkedKeys: string[]; + depts: DeptTreeOption[]; +} + +export interface RoleVO extends BaseEntity { + roleId: string | number; + roleName: string; + roleKey: string; + roleSort: number; + dataScope: string; + menuCheckStrictly: boolean; + deptCheckStrictly: boolean; + status: string; + delFlag: string; + remark?: any; + flag: boolean; + menuIds?: Array; + deptIds?: Array; + admin: boolean; +} + +export interface RoleQuery extends PageQuery { + roleName: string; + roleKey: string; + status: string; +} + +export interface RoleForm { + roleName: string; + roleKey: string; + roleSort: number; + status: string; + menuCheckStrictly: boolean; + deptCheckStrictly: boolean; + remark: string; + dataScope?: string; + roleId: string | undefined; + menuIds: Array; + deptIds: Array; +} diff --git a/src/api/system/social/auth.ts b/src/api/system/social/auth.ts new file mode 100644 index 0000000..7ca2433 --- /dev/null +++ b/src/api/system/social/auth.ts @@ -0,0 +1,24 @@ +import request from '@/utils/request'; + +// 获取跳转URL +export function authRouterUrl(source: string) { + return request({ + url: '/auth/binding/' + source, + method: 'get' + }); +} + +// 解绑账号 +export function authUnlock(authId: string) { + return request({ + url: '/auth/unlock/' + authId, + method: 'delete' + }); +} +//获取授权列表 +export function getAuthList() { + return request({ + url: '/system/social/list', + method: 'get' + }); +} diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts new file mode 100644 index 0000000..d02e6e5 --- /dev/null +++ b/src/api/system/user/index.ts @@ -0,0 +1,229 @@ +import type { RoleVO } from '@/api/system/role/types'; +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import { parseStrEmpty } from '@/utils/ruoyi'; +import type { DeptTreeVO } from './../dept/types'; +import type { UserForm, UserInfoVO, UserProfileForm, UserQuery, UserVO } from './types'; + +/** + * 查询用户列表 + * @param query + */ +export const listUser = (query: UserQuery): AxiosPromise> => { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }); +}; + +/** + * 通过用户ids查询用户 + * @param userIds + */ +export const optionSelect = (userIds: (number | string)[]): AxiosPromise => { + return request({ + url: '/system/user/optionselect?userIds=' + userIds, + method: 'get' + }); +}; + +/** + * 获取用户详情 + * @param userId + */ +export const getUser = (userId?: string | number): AxiosPromise => { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get' + }); +}; + +/** + * 新增用户 + */ +export const addUser = (data: UserForm) => { + return request({ + url: '/system/user', + method: 'post', + data: data + }); +}; + +/** + * 修改用户 + */ +export const updateUser = (data: UserForm) => { + return request({ + url: '/system/user', + method: 'put', + data: data + }); +}; + +/** + * 删除用户 + * @param userId 用户ID + */ +export const delUser = (userId: Array | string | number) => { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }); +}; + +/** + * 用户密码重置 + * @param userId 用户ID + * @param password 密码 + */ +export const resetUserPwd = (userId: string | number, password: string) => { + const data = { + userId, + password + }; + return request({ + url: '/system/user/resetPwd', + method: 'put', + headers: { + isEncrypt: true, + repeatSubmit: false + }, + data: data + }); +}; + +/** + * 用户状态修改 + * @param userId 用户ID + * @param status 用户状态 + */ +export const changeUserStatus = (userId: number | string, status: string) => { + const data = { + userId, + status + }; + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }); +}; + +/** + * 解锁用户 + * @param userId 用户ID + */ +export const unlockUser = (userId: number | string) => { + return request({ + url: '/system/user/unlock/' + userId, + method: 'get' + }); +}; + +/** + * 查询用户个人信息 + */ +export const getUserProfile = (): AxiosPromise => { + return request({ + url: '/system/user/profile', + method: 'get' + }); +}; + +/** + * 修改用户个人信息 + * @param data 用户信息 + */ +export const updateUserProfile = (data: UserProfileForm) => { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }); +}; + +/** + * 用户密码重置 + * @param oldPassword 旧密码 + * @param newPassword 新密码 + */ +export const updateUserPwd = (oldPassword: string, newPassword: string) => { + const data = { + oldPassword, + newPassword + }; + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + headers: { + isEncrypt: true, + repeatSubmit: false + }, + data: data + }); +}; + +/** + * 查询授权角色 + * @param userId 用户ID + */ +export const getAuthRole = (userId: string | number): AxiosPromise<{ user: UserVO; roles: RoleVO[] }> => { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get' + }); +}; + +/** + * 保存授权角色 + * @param data 用户ID + */ +export const updateAuthRole = (data: { userId: string; roleIds: string }) => { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data + }); +}; + +/** + * 查询当前部门的所有用户信息 + * @param deptId + */ +export const listUserByDeptId = (deptId: string | number): AxiosPromise => { + return request({ + url: '/system/user/list/dept/' + deptId, + method: 'get' + }); +}; + +/** + * 查询部门下拉树结构 + */ +export const deptTreeSelect = (): AxiosPromise => { + return request({ + url: '/system/user/deptTree', + method: 'get' + }); +}; + +export default { + listUser, + getUser, + optionSelect, + addUser, + updateUser, + delUser, + resetUserPwd, + changeUserStatus, + unlockUser, + getUserProfile, + updateUserProfile, + updateUserPwd, + getAuthRole, + updateAuthRole, + deptTreeSelect, + listUserByDeptId +}; diff --git a/src/api/system/user/types.ts b/src/api/system/user/types.ts new file mode 100644 index 0000000..c060d87 --- /dev/null +++ b/src/api/system/user/types.ts @@ -0,0 +1,101 @@ +import type { PostVO } from '@/api/system/post/types'; +import type { RoleVO } from '@/api/system/role/types'; + +/** + * 用户信息 + */ +export interface UserInfo { + user: UserVO; + roles: string[]; + permissions: string[]; +} + +/** + * 用户查询对象类型 + */ +export interface UserQuery extends PageQuery { + userName?: string; + nickName?: string; + phoneNumber?: string; + status?: string; + deptId?: string | number; + roleId?: string | number; + userIds?: string | number | (string | number)[] | undefined; +} + +/** + * 用户返回对象 + */ +export interface UserVO extends BaseEntity { + userId: string | number; + tenantId: string; + deptId: number; + userName: string; + nickName: string; + userType: string; + email: string; + phoneNumber: string; + gender: string; + avatar?: string | number; + avatarUrl?: string; + status: string; + delFlag: string; + loginIp: string; + loginDate: string; + remark: string; + deptName: string; + /** 详情接口可能返回嵌套部门 */ + dept?: { deptName?: string }; + roles: RoleVO[]; + roleIds: any; + postIds: any; + roleId: any; + admin: boolean; +} + +/** + * 用户表单类型 + */ +export interface UserForm { + id?: string; + userId?: string; + deptId?: number; + userName: string; + nickName?: string; + password: string; + phoneNumber?: string; + email?: string; + gender?: string; + status: string; + remark?: string; + avatar?: string | number; + postIds: string[]; + roleIds: string[]; +} + +/** + * 个人资料表单类型 + */ +export interface UserProfileForm { + nickName?: string; + phoneNumber?: string; + email?: string; + gender?: string; + avatar?: string | number; +} + +export interface UserInfoVO { + user: UserVO; + roles: RoleVO[]; + roleIds: string[]; + posts: PostVO[]; + postIds: string[]; + roleGroup: string; + postGroup: string; +} + +export interface ResetPwdForm { + oldPassword: string; + newPassword: string; + confirmPassword: string; +} diff --git a/src/api/tool/gen/index.ts b/src/api/tool/gen/index.ts new file mode 100644 index 0000000..193ee94 --- /dev/null +++ b/src/api/tool/gen/index.ts @@ -0,0 +1,81 @@ +import type { PageResult } from '@/api/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; +import type { DbTableForm, DbTableQuery, DbTableVO, GenTableDetailPayload, TableQuery, TableVO } from './types'; + +export type { GenTableDetailPayload } from './types'; + +// 查询生成表数据 +export const listTable = (query: TableQuery): AxiosPromise> => { + return request({ + url: '/tool/gen/list', + method: 'get', + params: query + }); +}; +// 查询db数据库列表 +export const listDbTable = (query: DbTableQuery): AxiosPromise> => { + return request({ + url: '/tool/gen/db/list', + method: 'get', + params: query + }); +}; + +// 查询表详细信息 +export const getGenTable = (tableId: string | number): AxiosPromise => { + return request({ + url: '/tool/gen/' + tableId, + method: 'get' + }); +}; + +// 修改代码生成信息 +export const updateGenTable = (data: DbTableForm): AxiosPromise => { + return request({ + url: '/tool/gen', + method: 'put', + data: data + }); +}; + +// 导入表 +export const importTable = (data: { tables: string; dataName: string }): AxiosPromise => { + return request({ + url: '/tool/gen/importTable', + method: 'post', + params: data + }); +}; + +// 预览生成代码 +export const previewTable = (tableId: string | number) => { + return request({ + url: '/tool/gen/preview/' + tableId, + method: 'get' + }); +}; + +// 删除表数据 +export const delTable = (tableId: string | number | Array) => { + return request({ + url: '/tool/gen/' + tableId, + method: 'delete' + }); +}; + +// 同步数据库 +export const synchDb = (tableId: string | number) => { + return request({ + url: '/tool/gen/synchDb/' + tableId, + method: 'get' + }); +}; + +// 获取数据源名称 +export const getDataNames = () => { + return request({ + url: '/tool/gen/getDataNames', + method: 'get' + }); +}; diff --git a/src/api/tool/gen/types.ts b/src/api/tool/gen/types.ts new file mode 100644 index 0000000..fcfb224 --- /dev/null +++ b/src/api/tool/gen/types.ts @@ -0,0 +1,215 @@ +export interface TableVO extends BaseEntity { + createDept: number | string; + tableId: string | number; + dataName: string; + tableName: string; + tableComment: string; + className: string; + tplCategory: string; + frontendType: string; + packageName: string; + moduleName: string; + businessName: string; + functionName: string; + functionAuthor: string; + pkColumn?: any; + columns?: any; + options?: any; + remark?: any; + treeCode?: any; + treeParentCode?: any; + treeName?: any; + menuIds?: any; + parentMenuId?: any; + parentMenuName?: any; + enableExport?: boolean; + enableStatus?: boolean; + statusField?: string; + enableUnique?: boolean; + uniqueFields?: string[]; + enableSort?: boolean; + sortField?: string; + treeRootValue?: string; + treeAncestorsField?: string; + treeOrderField?: string; + tree: boolean; + crud: boolean; +} + +export interface TableQuery extends PageQuery { + tableName: string; + tableComment: string; + dataName: string; +} + +export interface DbColumnVO extends BaseEntity { + createDept?: any; + columnId?: any; + tableId?: any; + columnName?: any; + columnComment?: any; + columnType?: any; + javaType?: any; + javaField?: any; + isPk?: any; + isIncrement?: any; + isRequired?: any; + isInsert?: any; + isEdit?: any; + isList?: any; + isQuery?: any; + queryType?: any; + htmlType?: any; + dictType?: any; + sort?: any; + increment: boolean; + capJavaField?: any; + usableColumn: boolean; + superColumn: boolean; + list: boolean; + pk: boolean; + insert: boolean; + edit: boolean; + query: boolean; + required: boolean; +} + +export interface DbTableVO { + createDept?: any; + tableId?: any; + tableName: string; + tableComment: string; + className?: any; + tplCategory?: any; + frontendType?: string; + packageName?: any; + moduleName?: any; + businessName?: any; + functionName?: any; + functionAuthor?: any; + pkColumn?: any; + columns: DbColumnVO[]; + options?: any; + remark?: any; + treeCode?: any; + treeParentCode?: any; + treeName?: any; + menuIds?: any; + parentMenuId?: any; + parentMenuName?: any; + enableExport?: boolean; + enableStatus?: boolean; + statusField?: string; + enableUnique?: boolean; + uniqueFields?: string[]; + enableSort?: boolean; + sortField?: string; + treeRootValue?: string; + treeAncestorsField?: string; + treeOrderField?: string; + tree: boolean; + crud: boolean; +} + +export interface DbTableQuery extends PageQuery { + dataName: string; + tableName: string; + tableComment: string; +} + +/** + * 代码生成表详情接口 data 结构 + * - info:当前表 GenTable + * - rows:字段列表 GenTableColumn[] + */ +export interface GenTableDetailPayload { + info: DbTableVO; + rows: DbColumnVO[]; +} + +export interface DbColumnForm extends BaseEntity { + createDept: number; + columnId: string; + tableId: string; + columnName: string; + columnComment: string; + columnType: string; + javaType: string; + javaField: string; + isPk: string; + isIncrement: string; + isRequired: string; + isInsert?: any; + isEdit: string; + isList: string; + isQuery?: any; + queryType: string; + htmlType: string; + dictType: string; + sort: number; + increment: boolean; + capJavaField: string; + usableColumn: boolean; + superColumn: boolean; + list: boolean; + pk: boolean; + insert: boolean; + edit: boolean; + query: boolean; + required: boolean; +} + +export interface DbParamForm { + treeCode?: any; + treeName?: any; + treeParentCode?: any; + parentMenuId: string; + enableExport?: boolean; + enableStatus?: boolean; + statusField?: string; + enableUnique?: boolean; + uniqueFields?: string[]; + enableSort?: boolean; + sortField?: string; + treeRootValue?: string; + treeAncestorsField?: string; + treeOrderField?: string; +} + +export interface DbTableForm extends BaseEntity { + createDept?: any; + tableId: string | string; + tableName: string; + tableComment: string; + className: string; + tplCategory: string; + frontendType: string; + packageName: string; + moduleName: string; + businessName: string; + functionName: string; + functionAuthor: string; + pkColumn?: any; + columns: DbColumnForm[]; + options: string; + remark?: any; + treeCode?: any; + treeParentCode?: any; + treeName?: any; + menuIds?: any; + parentMenuId: string; + parentMenuName?: any; + enableExport?: boolean; + enableStatus?: boolean; + statusField?: string; + enableUnique?: boolean; + uniqueFields?: string[]; + enableSort?: boolean; + sortField?: string; + treeRootValue?: string; + treeAncestorsField?: string; + treeOrderField?: string; + tree: boolean; + crud: boolean; + params: DbParamForm; +} diff --git a/src/api/types.ts b/src/api/types.ts new file mode 100644 index 0000000..b578f5c --- /dev/null +++ b/src/api/types.ts @@ -0,0 +1,51 @@ +/** + * 注册 + */ +export type RegisterForm = { + username: string; + password: string; + confirmPassword?: string; + code?: string; + uuid?: string; + userType?: string; +}; + +/** + * 登录请求 + */ +export interface LoginData { + username?: string; + password?: string; + rememberMe?: boolean; + socialCode?: string; + socialState?: string; + source?: string; + code?: string; + uuid?: string; + clientId: string; + grantType: string; +} + +/** + * 登录响应 + */ +export interface LoginResult { + access_token: string; +} + +/** + * 验证码返回 + */ +export interface VerifyCodeResult { + captchaEnabled: boolean; + uuid?: string; + img?: string; +} + +/** + * 分页返回结果 + */ +export interface PageResult { + total: number; + rows: T[]; +} diff --git a/src/api/workflow/category/index.ts b/src/api/workflow/category/index.ts new file mode 100644 index 0000000..68795e3 --- /dev/null +++ b/src/api/workflow/category/index.ts @@ -0,0 +1,76 @@ +import type { CategoryForm, CategoryQuery, CategoryTreeVO, CategoryVO } from '@/api/workflow/category/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 查询流程分类列表 + * @param query + * @returns {*} + */ + +export const listCategory = (query?: CategoryQuery): AxiosPromise => { + return request({ + url: '/workflow/category/list', + method: 'get', + params: query + }); +}; + +/** + * 查询流程分类详细 + * @param categoryId + */ +export const getCategory = (categoryId: string | number): AxiosPromise => { + return request({ + url: '/workflow/category/' + categoryId, + method: 'get' + }); +}; + +/** + * 新增流程分类 + * @param data + */ +export const addCategory = (data: CategoryForm) => { + return request({ + url: '/workflow/category', + method: 'post', + data: data + }); +}; + +/** + * 修改流程分类 + * @param data + */ +export const updateCategory = (data: CategoryForm) => { + return request({ + url: '/workflow/category', + method: 'put', + data: data + }); +}; + +/** + * 删除流程分类 + * @param categoryId + */ +export const delCategory = (categoryId: string | number | Array) => { + return request({ + url: '/workflow/category/' + categoryId, + method: 'delete' + }); +}; + +/** + * 获取流程分类树列表 + * @param query 流程实例id + * @returns + */ +export const categoryTree = (query?: CategoryForm): AxiosPromise => { + return request({ + url: `/workflow/category/categoryTree`, + method: 'get', + params: query + }); +}; diff --git a/src/api/workflow/category/types.ts b/src/api/workflow/category/types.ts new file mode 100644 index 0000000..99a53a6 --- /dev/null +++ b/src/api/workflow/category/types.ts @@ -0,0 +1,67 @@ +export interface CategoryTreeVO { + id: number | string; + label: string; + parentId: number | string; + weight: number; + children: CategoryTreeVO[]; +} +export interface CategoryVO { + /** + * 流程分类ID + */ + categoryId: string | number; + + /** + * 父级id + */ + parentId: string | number; + + /** + * 流程分类名称 + */ + categoryName: string; + + /** + * 显示顺序 + */ + orderNum: number; + + /** + * 创建时间 + */ + createTime: string; + + /** + * 子对象 + */ + children: CategoryVO[]; +} + +export interface CategoryForm extends BaseEntity { + /** + * 流程分类ID + */ + categoryId?: string | number; + + /** + * 流程分类名称 + */ + categoryName?: string; + + /** + * 父流程分类id + */ + parentId?: string | number; + + /** + * 显示顺序 + */ + orderNum?: number; +} + +export interface CategoryQuery { + /** + * 流程分类名称 + */ + categoryName?: string; +} diff --git a/src/api/workflow/definition/index.ts b/src/api/workflow/definition/index.ts new file mode 100644 index 0000000..7419fca --- /dev/null +++ b/src/api/workflow/definition/index.ts @@ -0,0 +1,176 @@ +import type { PageResult } from '@/api/types'; +import type { + definitionXmlVO, + FlowDefinitionForm, + FlowDefinitionQuery, + FlowDefinitionVo +} from '@/api/workflow/definition/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 获取流程定义列表 + * @param query 流程实例id + * @returns + */ +export const listDefinition = (query: FlowDefinitionQuery): AxiosPromise> => { + return request({ + url: `/workflow/definition/list`, + method: 'get', + params: query + }); +}; + +/** + * 查询未发布的流程定义列表 + * @param query 流程实例id + * @returns + */ +export const unPublishList = (query: FlowDefinitionQuery): AxiosPromise> => { + return request({ + url: `/workflow/definition/unPublishList`, + method: 'get', + params: query + }); +}; + +/** + * 通过流程定义id获取xml + * @param definitionId 流程定义id + * @returns + */ +export const definitionXml = (definitionId: string): AxiosPromise => { + return request({ + url: `/workflow/definition/definitionXml/${definitionId}`, + method: 'get' + }); +}; + +/** + * 删除流程定义 + * @param id 流程定义id + * @returns + */ +export const deleteDefinition = (id: string | string[]) => { + return request({ + url: `/workflow/definition/${id}`, + method: 'delete' + }); +}; + +/** + * 挂起/激活 + * @param definitionId 流程定义id + * @param activityStatus 状态 + * @returns + */ +export const active = (definitionId: string, activityStatus: boolean) => { + return request({ + url: `/workflow/definition/active/${definitionId}`, + method: 'put', + params: { + active: activityStatus + } + }); +}; + +/** + * 通过zip或xml部署流程定义 + * @returns + */ +export function importDef(data: any) { + return request({ + url: '/workflow/definition/importDef', + method: 'post', + data: data, + headers: { + repeatSubmit: false + } + }); +} + +/** + * 发布流程定义 + * @param id 流程定义id + * @returns + */ +export const publish = (id: string) => { + return request({ + url: `/workflow/definition/publish/${id}`, + method: 'put' + }); +}; + +/** + * 取消发布流程定义 + * @param id 流程定义id + * @returns + */ +export const unPublish = (id: string) => { + return request({ + url: `/workflow/definition/unPublish/${id}`, + method: 'put' + }); +}; + +/** + * 获取流程定义xml字符串 + * @param id 流程定义id + * @returns + */ +export const xmlString = (id: string) => { + return request({ + url: `/workflow/definition/xmlString/${id}`, + method: 'get' + }); +}; + +/** + * 新增 + * @param data 参数 + * @returns + */ +export const add = (data: FlowDefinitionForm) => { + return request({ + url: `/workflow/definition`, + method: 'post', + data: data + }); +}; + +/** + * 修改 + * @param data 参数 + * @returns + */ +export const edit = (data: FlowDefinitionForm) => { + return request({ + url: `/workflow/definition`, + method: 'put', + data: data + }); +}; + +/** + * 查询详情 + * @param id 参数 + * @returns + */ +export const getInfo = (id: number | string) => { + return request({ + url: `/workflow/definition/${id}`, + method: 'get' + }); +}; + +/** + * 复制流程定义 + * @param id 流程定义id + * @returns + */ +export const copy = (id: string) => { + return request({ + url: `/workflow/definition/copy/${id}`, + method: 'post' + }); +}; diff --git a/src/api/workflow/definition/types.ts b/src/api/workflow/definition/types.ts new file mode 100644 index 0000000..93d081e --- /dev/null +++ b/src/api/workflow/definition/types.ts @@ -0,0 +1,34 @@ +export interface FlowDefinitionQuery extends PageQuery { + flowCode?: string; + flowName?: string; + category: string | number; + isPublish?: number; +} + +export interface FlowDefinitionVo { + id: string; + flowName: string; + flowCode: string; + formPath: string; + version: string; + isPublish: number; + activityStatus: number; + createTime: Date; + updateTime: Date; +} + +export interface FlowDefinitionForm { + id: string; + flowName: string; + flowCode: string; + category: string; + ext: string; + formPath: string; + formCustom: string; + modelValue: string; +} + +export interface definitionXmlVO { + xml: string[]; + xmlStr: string; +} diff --git a/src/api/workflow/instance/index.ts b/src/api/workflow/instance/index.ts new file mode 100644 index 0000000..4f53a9a --- /dev/null +++ b/src/api/workflow/instance/index.ts @@ -0,0 +1,126 @@ +import type { PageResult } from '@/api/types'; +import type { FlowInstanceQuery, FlowInstanceVO } from '@/api/workflow/instance/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 查询运行中实例列表 + * @param query + * @returns {*} + */ +export const pageByRunning = (query: FlowInstanceQuery): AxiosPromise> => { + return request({ + url: '/workflow/instance/pageByRunning', + method: 'get', + params: query + }); +}; + +/** + * 查询已完成实例列表 + * @param query + * @returns {*} + */ +export const pageByFinish = (query: FlowInstanceQuery): AxiosPromise> => { + return request({ + url: '/workflow/instance/pageByFinish', + method: 'get', + params: query + }); +}; + +/** + * 通过业务id获取历史流程图 + */ +export const flowHisTaskList = (businessId: string | number) => { + return request({ + url: `/workflow/instance/flowHisTaskList/${businessId}?t=${Math.random()}`, + method: 'get' + }); +}; + +/** + * 分页查询当前登录人单据 + * @param query + * @returns {*} + */ +export const pageByCurrent = (query: FlowInstanceQuery): AxiosPromise> => { + return request({ + url: '/workflow/instance/pageByCurrent', + method: 'get', + params: query + }); +}; + +/** + * 撤销流程 + * @param data 参数 + * @returns + */ +export const cancelProcessApply = (data: any) => { + return request({ + url: `/workflow/instance/cancelProcessApply`, + method: 'put', + data: data + }); +}; + +/** + * 获取流程变量 + * @param instanceId 实例id + * @returns + */ +export const instanceVariable = (instanceId: string | number) => { + return request({ + url: `/workflow/instance/instanceVariable/${instanceId}`, + method: 'get' + }); +}; + +/** + * 删除 + * @param instanceIds 流程实例id + * @returns + */ +export const deleteByInstanceIds = (instanceIds: Array | string | number) => { + return request({ + url: `/workflow/instance/deleteByInstanceIds/${instanceIds}`, + method: 'delete' + }); +}; + +/** + * 删除历史流程实例 + * @param instanceIds + */ +export const deleteHisByInstanceIds = (instanceIds: Array | string | number) => { + return request({ + url: `/workflow/instance/deleteHisByInstanceIds/${instanceIds}`, + method: 'delete' + }); +}; + +/** + * 作废流程 + * @param data 参数 + * @returns + */ +export const invalid = (data: any) => { + return request({ + url: `/workflow/instance/invalid`, + method: 'post', + data: data + }); +}; +/** + * 修改流程变量 + * @param data 参数 + * @returns + */ +export const updateVariable = (data: any) => { + return request({ + url: `/workflow/instance/updateVariable`, + method: 'put', + data: data + }); +}; diff --git a/src/api/workflow/instance/types.ts b/src/api/workflow/instance/types.ts new file mode 100644 index 0000000..a40a508 --- /dev/null +++ b/src/api/workflow/instance/types.ts @@ -0,0 +1,28 @@ +import type { FlowTaskVO } from '@/api/workflow/task/types'; + +export interface FlowInstanceQuery extends PageQuery { + category?: string | number; + nodeName?: string; + flowCode?: string; + flowName?: string; + createByIds?: Array; + businessId?: string; +} + +export interface FlowInstanceVO extends BaseEntity { + id: string | number; + definitionId: string; + flowName: string; + flowCode: string; + version: string; + businessId: string; + activityStatus: number; + tenantId: string; + createTime: string; + createBy: string; + flowStatus: string; + flowStatusName: string; + flowTaskList: FlowTaskVO[]; + businessCode: string; + businessTitle: string; +} diff --git a/src/api/workflow/leave/index.ts b/src/api/workflow/leave/index.ts new file mode 100644 index 0000000..749ea1b --- /dev/null +++ b/src/api/workflow/leave/index.ts @@ -0,0 +1,76 @@ +import type { PageResult } from '@/api/types'; +import type { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 查询请假列表 + * @param query + * @returns {*} + */ + +export const listLeave = (query?: LeaveQuery): AxiosPromise> => { + return request({ + url: '/workflow/leave/list', + method: 'get', + params: query + }); +}; + +/** + * 查询请假详细 + * @param id + */ +export const getLeave = (id: string | number): AxiosPromise => { + return request({ + url: '/workflow/leave/' + id, + method: 'get' + }); +}; + +/** + * 新增请假 + * @param data + */ +export const addLeave = (data: LeaveForm): AxiosPromise => { + return request({ + url: '/workflow/leave', + method: 'post', + data: data + }); +}; + +/** + * 提交请假并发起流程 + * @param data + */ +export const submitAndFlowStart = (data: LeaveForm): AxiosPromise => { + return request({ + url: '/workflow/leave/submitAndFlowStart', + method: 'post', + data: data + }); +}; + +/** + * 修改请假 + * @param data + */ +export const updateLeave = (data: LeaveForm): AxiosPromise => { + return request({ + url: '/workflow/leave', + method: 'put', + data: data + }); +}; + +/** + * 删除请假 + * @param id + */ +export const delLeave = (id: string | number | Array) => { + return request({ + url: '/workflow/leave/' + id, + method: 'delete' + }); +}; diff --git a/src/api/workflow/leave/types.ts b/src/api/workflow/leave/types.ts new file mode 100644 index 0000000..c8e58a7 --- /dev/null +++ b/src/api/workflow/leave/types.ts @@ -0,0 +1,26 @@ +export interface LeaveVO { + id: string | number; + applyCode?: string; + leaveType: string; + startDate: string; + endDate: string; + leaveDays: number; + remark: string; + status?: string; +} + +export interface LeaveForm extends BaseEntity { + id?: string | number; + applyCode?: string; + leaveType?: string; + startDate?: string; + endDate?: string; + leaveDays?: number; + remark?: string; + status?: string; +} + +export interface LeaveQuery extends PageQuery { + startLeaveDays?: number; + endLeaveDays?: number; +} diff --git a/src/api/workflow/spel/index.ts b/src/api/workflow/spel/index.ts new file mode 100644 index 0000000..2a8efd5 --- /dev/null +++ b/src/api/workflow/spel/index.ts @@ -0,0 +1,64 @@ +import type { PageResult } from '@/api/types'; +import type { SpelForm, SpelQuery, SpelVO } from '@/api/workflow/spel/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 查询流程spel表达式定义列表 + * @param query + * @returns {*} + */ + +export const listSpel = (query?: SpelQuery): AxiosPromise> => { + return request({ + url: '/workflow/spel/list', + method: 'get', + params: query + }); +}; + +/** + * 查询流程spel表达式定义详细 + * @param id + */ +export const getSpel = (id: string | number): AxiosPromise => { + return request({ + url: '/workflow/spel/' + id, + method: 'get' + }); +}; + +/** + * 新增流程spel表达式定义 + * @param data + */ +export const addSpel = (data: SpelForm) => { + return request({ + url: '/workflow/spel', + method: 'post', + data: data + }); +}; + +/** + * 修改流程spel表达式定义 + * @param data + */ +export const updateSpel = (data: SpelForm) => { + return request({ + url: '/workflow/spel', + method: 'put', + data: data + }); +}; + +/** + * 删除流程spel表达式定义 + * @param id + */ +export const delSpel = (id: string | number | Array) => { + return request({ + url: '/workflow/spel/' + id, + method: 'delete' + }); +}; diff --git a/src/api/workflow/spel/types.ts b/src/api/workflow/spel/types.ts new file mode 100644 index 0000000..53b6c41 --- /dev/null +++ b/src/api/workflow/spel/types.ts @@ -0,0 +1,105 @@ +export interface SpelVO { + /** + * 主键id + */ + id: string | number; + + /** + * 组件名称 + */ + componentName: string; + + /** + * 方法名 + */ + methodName: string; + + /** + * 参数 + */ + methodParams: string; + + /** + * 预览spel值 + */ + viewSpel: string; + + /** + * 状态(0正常 1停用) + */ + status: string; + + /** + * 备注 + */ + remark?: string; +} + +export interface SpelForm extends BaseEntity { + /** + * 主键id + */ + id?: string | number; + + /** + * 组件名称 + */ + componentName?: string; + + /** + * 方法名 + */ + methodName?: string; + + /** + * 参数 + */ + methodParams?: string; + + /** + * 预览spel值 + */ + viewSpel?: string; + + /** + * 状态(0正常 1停用) + */ + status?: string; + + /** + * 备注 + */ + remark?: string; +} + +export interface SpelQuery extends PageQuery { + /** + * 组件名称 + */ + componentName?: string; + + /** + * 方法名 + */ + methodName?: string; + + /** + * 参数 + */ + methodParams?: string; + + /** + * 预览spel值 + */ + viewSpel?: string; + + /** + * 状态(0正常 1停用) + */ + status?: string; + + /** + * 日期范围参数 + */ + params?: any; +} diff --git a/src/api/workflow/task/index.ts b/src/api/workflow/task/index.ts new file mode 100644 index 0000000..f4f47b4 --- /dev/null +++ b/src/api/workflow/task/index.ts @@ -0,0 +1,207 @@ +import type { PageResult } from '@/api/types'; +import type { FlowTaskVO, TaskOperationBo, TaskQuery } from '@/api/workflow/task/types'; +import type { AxiosPromise } from '@/utils/api-types'; +import request from '@/utils/request'; + +/** + * 查询待办列表 + * @param query + * @returns {*} + */ +export const pageByTaskWait = (query: TaskQuery): AxiosPromise> => { + return request({ + url: '/workflow/task/pageByTaskWait', + method: 'get', + params: query + }); +}; + +/** + * 查询已办列表 + * @param query + * @returns {*} + */ +export const pageByTaskFinish = (query: TaskQuery): AxiosPromise> => { + return request({ + url: '/workflow/task/pageByTaskFinish', + method: 'get', + params: query + }); +}; + +/** + * 查询当前用户的抄送列表 + * @param query + * @returns {*} + */ +export const pageByTaskCopy = (query: TaskQuery): AxiosPromise> => { + return request({ + url: '/workflow/task/pageByTaskCopy', + method: 'get', + params: query + }); +}; + +/** + * 查询全部待办任务 + * @param query + * @returns {*} + */ +export const pageByAllTaskWait = (query: TaskQuery): AxiosPromise> => { + return request({ + url: '/workflow/task/pageByAllTaskWait', + method: 'get', + params: query + }); +}; + +/** + * 查询全部已办任务 + * @param query + * @returns {*} + */ +export const pageByAllTaskFinish = (query: TaskQuery): AxiosPromise> => { + return request({ + url: '/workflow/task/pageByAllTaskFinish', + method: 'get', + params: query + }); +}; + +/** + * 启动流程 + * @param data + * @returns {*} + */ +export const startWorkFlow = (data: object): any => { + return request({ + url: '/workflow/task/startWorkFlow', + method: 'post', + data: data + }); +}; + +/** + * 办理流程 + * @param data + * @returns {*} + */ +export const completeTask = (data: object) => { + return request({ + url: '/workflow/task/completeTask', + method: 'post', + data: data + }); +}; + +/** + * 任务驳回 + * @param data + * @returns {*} + */ +export const backProcess = (data: any): any => { + return request({ + url: '/workflow/task/backProcess', + method: 'post', + data: data + }); +}; + +/** + * 获取当前任务 + * @param taskId + * @returns + */ +export const getTask = (taskId: string) => { + return request({ + url: '/workflow/task/getTask/' + taskId, + method: 'get' + }); +}; + +/** + * 修改任务办理人 + * @param taskIdList + * @param userId + * @returns + */ +export const updateAssignee = (taskIdList: Array, userId: string) => { + return request({ + url: `/workflow/task/updateAssignee/${userId}`, + method: 'put', + data: taskIdList + }); +}; + +/** + * 终止任务 + * @returns + */ +export const terminationTask = (data: any) => { + return request({ + url: `/workflow/task/terminationTask`, + method: 'post', + data: data + }); +}; + +/** + * 获取可驳回得任务节点 + * @returns + */ +export const getBackTaskNode = (taskId: string | number, nodeCode: string) => { + return request({ + url: `/workflow/task/getBackTaskNode/${taskId}/${nodeCode}`, + method: 'get' + }); +}; + +/** + * 任务操作 操作类型,委派 delegateTask、转办 transferTask、加签 addSignature、减签 reductionSignature + * @returns + */ +export const taskOperation = (data: TaskOperationBo, operation: string) => { + return request({ + url: `/workflow/task/taskOperation/${operation}`, + method: 'post', + data: data + }); +}; + +/** + * 获取当前任务办理人 + * @param taskId 任务id + * @returns + */ +export const currentTaskAllUser = (taskId: string | number) => { + return request({ + url: `/workflow/task/currentTaskAllUser/${taskId}`, + method: 'get' + }); +}; + +/** + * 获取下一节点写 + * @param data参数 + * @returns + */ +export const getNextNodeList = (data: any): any => { + return request({ + url: '/workflow/task/getNextNodeList', + method: 'post', + data: data + }); +}; + +/** + * 催办任务 + * @param data参数 + * @returns + */ +export const urgeTask = (data: any): any => { + return request({ + url: '/workflow/task/urgeTask', + method: 'post', + data: data + }); +}; diff --git a/src/api/workflow/task/types.ts b/src/api/workflow/task/types.ts new file mode 100644 index 0000000..1d550ec --- /dev/null +++ b/src/api/workflow/task/types.ts @@ -0,0 +1,60 @@ +export interface TaskQuery extends PageQuery { + nodeName?: string; + flowCode?: string; + flowName?: string; + createByIds?: Array; +} + +export interface ParticipantVo { + groupIds?: string[] | number[]; + candidate: string[] | number[]; + candidateName: string[]; + claim: boolean; +} +export interface FlowTaskVO { + id: string | number; + createTime?: Date; + updateTime?: Date; + tenantId?: string; + definitionId?: string; + instanceId: string; + flowName: string; + businessId: string; + nodeCode: string; + nodeName: string; + flowCode: string; + flowStatus: string; + formCustom: string; + formPath: string; + nodeType: number; + nodeRatio: string | number; + version?: string; + applyNode?: boolean; + buttonList?: ButtonList[]; + copyList?: FlowCopyVo[]; + varList?: Map; + businessCode: string; + businessTitle: string; +} + +export interface ButtonList { + code: string; + show: boolean; +} +export interface FlowCopyVo { + userId: string | number; + nickName: string; +} + +export interface TaskOperationBo { + //委派/转办人的用户ID(必填,准对委派/转办人操作) + userId?: string; + //加签/减签人的用户ID列表(必填,针对加签/减签操作) + userIds?: string[]; + //任务ID(必填) + taskId: string | number; + //消息类型 + messageType?: string[]; + //意见或备注信息(可选) + message?: string; +} diff --git a/src/api/workflow/workflowCommon/index.ts b/src/api/workflow/workflowCommon/index.ts new file mode 100644 index 0000000..3aad766 --- /dev/null +++ b/src/api/workflow/workflowCommon/index.ts @@ -0,0 +1,17 @@ +import type { RouterJumpVo } from '@/api/workflow/workflowCommon/types'; +import tab from '@/plugins/tab'; +import router from '@/router'; + +export default { + routerJump(routerJumpVo: RouterJumpVo) { + tab.closePage(router.currentRoute.value); + router.push({ + path: routerJumpVo.formPath, + query: { + id: routerJumpVo.businessId, + type: routerJumpVo.type, + taskId: routerJumpVo.taskId + } + }); + } +}; diff --git a/src/api/workflow/workflowCommon/types.ts b/src/api/workflow/workflowCommon/types.ts new file mode 100644 index 0000000..4815554 --- /dev/null +++ b/src/api/workflow/workflowCommon/types.ts @@ -0,0 +1,14 @@ +export interface RouterJumpVo { + businessId: string; + taskId: string | number; + type: string; + formCustom: string; + formPath: string; +} + +export interface StartProcessBo { + businessId: string | number; + flowCode: string; + variables: any; + bizExt: any; +} diff --git a/src/assets/401_images/401.gif b/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/src/assets/401_images/401.gif differ diff --git a/src/assets/404_images/404.png b/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/src/assets/404_images/404.png differ diff --git a/src/assets/404_images/404_cloud.png b/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/src/assets/404_images/404_cloud.png differ diff --git a/src/assets/icons/svg/404.svg b/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/bug.svg b/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/build.svg b/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/button.svg b/src/assets/icons/svg/button.svg new file mode 100644 index 0000000..d14d580 --- /dev/null +++ b/src/assets/icons/svg/button.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/svg/caret-back.svg b/src/assets/icons/svg/caret-back.svg new file mode 100644 index 0000000..9bae722 --- /dev/null +++ b/src/assets/icons/svg/caret-back.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/caret-forward.svg b/src/assets/icons/svg/caret-forward.svg new file mode 100644 index 0000000..1ec3f7d --- /dev/null +++ b/src/assets/icons/svg/caret-forward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/cascader.svg b/src/assets/icons/svg/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/src/assets/icons/svg/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/category.svg b/src/assets/icons/svg/category.svg new file mode 100644 index 0000000..df92526 --- /dev/null +++ b/src/assets/icons/svg/category.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/chart.svg b/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/checkbox.svg b/src/assets/icons/svg/checkbox.svg new file mode 100644 index 0000000..013fd3a --- /dev/null +++ b/src/assets/icons/svg/checkbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/clipboard.svg b/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/code.svg b/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..5f9c5ab --- /dev/null +++ b/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/color.svg b/src/assets/icons/svg/color.svg new file mode 100644 index 0000000..44a81aa --- /dev/null +++ b/src/assets/icons/svg/color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/company.svg b/src/assets/icons/svg/company.svg new file mode 100644 index 0000000..fcf1394 --- /dev/null +++ b/src/assets/icons/svg/company.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/component.svg b/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..29c3458 --- /dev/null +++ b/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/dashboard.svg b/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/date-range.svg b/src/assets/icons/svg/date-range.svg new file mode 100644 index 0000000..fda571e --- /dev/null +++ b/src/assets/icons/svg/date-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/date.svg b/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..52dc73e --- /dev/null +++ b/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/dict.svg b/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/documentation.svg b/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/download.svg b/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/drag.svg b/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/druid.svg b/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/edit.svg b/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/education.svg b/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/email.svg b/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/example.svg b/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/excel.svg b/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/exit-fullscreen.svg b/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/eye-open.svg b/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/eye.svg b/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/finish.svg b/src/assets/icons/svg/finish.svg new file mode 100644 index 0000000..4685c23 --- /dev/null +++ b/src/assets/icons/svg/finish.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/form.svg b/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/fullscreen.svg b/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/gitee.svg b/src/assets/icons/svg/gitee.svg new file mode 100644 index 0000000..6324608 --- /dev/null +++ b/src/assets/icons/svg/gitee.svg @@ -0,0 +1 @@ + diff --git a/src/assets/icons/svg/github.svg b/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/guide.svg b/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/icon.svg b/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/input.svg b/src/assets/icons/svg/input.svg new file mode 100644 index 0000000..ab91381 --- /dev/null +++ b/src/assets/icons/svg/input.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/international.svg b/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/job.svg b/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/language.svg b/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/link.svg b/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/list.svg b/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/lock.svg b/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/log.svg b/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/logininfo.svg b/src/assets/icons/svg/logininfo.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/src/assets/icons/svg/logininfo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/maxkey.svg b/src/assets/icons/svg/maxkey.svg new file mode 100644 index 0000000..f8f8a7d --- /dev/null +++ b/src/assets/icons/svg/maxkey.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/icons/svg/message.svg b/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/model.svg b/src/assets/icons/svg/model.svg new file mode 100644 index 0000000..0c57d70 --- /dev/null +++ b/src/assets/icons/svg/model.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/money.svg b/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/monitor.svg b/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/my-copy.svg b/src/assets/icons/svg/my-copy.svg new file mode 100644 index 0000000..49f69fa --- /dev/null +++ b/src/assets/icons/svg/my-copy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/my-task.svg b/src/assets/icons/svg/my-task.svg new file mode 100644 index 0000000..1f1ea44 --- /dev/null +++ b/src/assets/icons/svg/my-task.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/nested.svg b/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/number.svg b/src/assets/icons/svg/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/src/assets/icons/svg/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/online.svg b/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/password.svg b/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/pdf.svg b/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/people.svg b/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/peoples.svg b/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/phone.svg b/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/post.svg b/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/process-definition.svg b/src/assets/icons/svg/process-definition.svg new file mode 100644 index 0000000..202d200 --- /dev/null +++ b/src/assets/icons/svg/process-definition.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/qq.svg b/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/question.svg b/src/assets/icons/svg/question.svg new file mode 100644 index 0000000..cf75bd4 --- /dev/null +++ b/src/assets/icons/svg/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/radio.svg b/src/assets/icons/svg/radio.svg new file mode 100644 index 0000000..0cde345 --- /dev/null +++ b/src/assets/icons/svg/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/rate.svg b/src/assets/icons/svg/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/src/assets/icons/svg/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/redis-list.svg b/src/assets/icons/svg/redis-list.svg new file mode 100644 index 0000000..98a15b2 --- /dev/null +++ b/src/assets/icons/svg/redis-list.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/redis.svg b/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/row.svg b/src/assets/icons/svg/row.svg new file mode 100644 index 0000000..0780992 --- /dev/null +++ b/src/assets/icons/svg/row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/search.svg b/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/select.svg b/src/assets/icons/svg/select.svg new file mode 100644 index 0000000..d628382 --- /dev/null +++ b/src/assets/icons/svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/server.svg b/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..eb287e3 --- /dev/null +++ b/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/shopping.svg b/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/size.svg b/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/skill.svg b/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/slider.svg b/src/assets/icons/svg/slider.svg new file mode 100644 index 0000000..fbe4f39 --- /dev/null +++ b/src/assets/icons/svg/slider.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/star.svg b/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/swagger.svg b/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/switch.svg b/src/assets/icons/svg/switch.svg new file mode 100644 index 0000000..0ba61e3 --- /dev/null +++ b/src/assets/icons/svg/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/system.svg b/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..5992593 --- /dev/null +++ b/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/tab.svg b/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/table.svg b/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/textarea.svg b/src/assets/icons/svg/textarea.svg new file mode 100644 index 0000000..2709f29 --- /dev/null +++ b/src/assets/icons/svg/textarea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/theme.svg b/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/time-range.svg b/src/assets/icons/svg/time-range.svg new file mode 100644 index 0000000..13c1202 --- /dev/null +++ b/src/assets/icons/svg/time-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/time.svg b/src/assets/icons/svg/time.svg new file mode 100644 index 0000000..b376e32 --- /dev/null +++ b/src/assets/icons/svg/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/tool.svg b/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..48e0e35 --- /dev/null +++ b/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/topiam.svg b/src/assets/icons/svg/topiam.svg new file mode 100644 index 0000000..e7ea057 --- /dev/null +++ b/src/assets/icons/svg/topiam.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/icons/svg/tree-table.svg b/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/tree.svg b/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/upload.svg b/src/assets/icons/svg/upload.svg new file mode 100644 index 0000000..bae49c0 --- /dev/null +++ b/src/assets/icons/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/user.svg b/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/validCode.svg b/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/waiting.svg b/src/assets/icons/svg/waiting.svg new file mode 100644 index 0000000..2c2042d --- /dev/null +++ b/src/assets/icons/svg/waiting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/wechat.svg b/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/workflow.svg b/src/assets/icons/svg/workflow.svg new file mode 100644 index 0000000..2f7423a --- /dev/null +++ b/src/assets/icons/svg/workflow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/icons/svg/zip.svg b/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/images/dark.svg b/src/assets/images/dark.svg new file mode 100644 index 0000000..f646bd7 --- /dev/null +++ b/src/assets/images/dark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/light.svg b/src/assets/images/light.svg new file mode 100644 index 0000000..ab7cc08 --- /dev/null +++ b/src/assets/images/light.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/images/login-background.jpg b/src/assets/images/login-background.jpg new file mode 100644 index 0000000..fa6408b Binary files /dev/null and b/src/assets/images/login-background.jpg differ diff --git a/src/assets/images/profile.jpg b/src/assets/images/profile.jpg new file mode 100644 index 0000000..f4fde57 Binary files /dev/null and b/src/assets/images/profile.jpg differ diff --git a/src/assets/logo/logo.png b/src/assets/logo/logo.png new file mode 100644 index 0000000..3f919d8 Binary files /dev/null and b/src/assets/logo/logo.png differ diff --git a/src/assets/styles/base/_document.scss b/src/assets/styles/base/_document.scss new file mode 100644 index 0000000..49f11f4 --- /dev/null +++ b/src/assets/styles/base/_document.scss @@ -0,0 +1,90 @@ +/* Global document styles and lightweight utility helpers. */ + +// --- 视口与排版根 --- +body { + height: 100%; + margin: 0; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + background: var(--app-shell-bg); + font-family: + 'MiSans', 'HarmonyOS Sans SC', 'PingFang SC', 'Source Han Sans SC', 'Noto Sans SC', 'Hiragino Sans GB', + 'Microsoft YaHei', sans-serif; +} + +label { + font-weight: 600; +} + +html { + height: 100%; + box-sizing: border-box; +} + +html.dark .svg-icon, +html.dark svg { + fill: var(--el-text-color-regular); +} + +#app { + height: 100%; +} + +html, +body, +#app { + min-height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: ' '; + clear: both; + height: 0; + } +} + +.h1, +.h2, +.h3, +.h4, +.h5, +.h6, +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} diff --git a/src/assets/styles/components/_card-shell.scss b/src/assets/styles/components/_card-shell.scss new file mode 100644 index 0000000..927bf7d --- /dev/null +++ b/src/assets/styles/components/_card-shell.scss @@ -0,0 +1,67 @@ +/* 业务列表/卡片页:分页、树、列表、el-card 等(与 vendors/element-plus 通用覆盖叠加,入口顺序见 index.scss)。 + * 属「页面模式」而非 EP 公共主题,故放在 components。 */ + +// --- 历史「表单分区标题」样式 --- +.form-header { + font-size: 15px; + color: var(--el-color-primary); + border-bottom: 1px solid var(--app-surface-border); + margin: 8px 10px 25px 10px; + padding-bottom: 5px; +} + +// --- 树形容器描边(暗色覆盖在 _search-panel) --- +.tree-border { + margin-top: 8px; + border: 1px solid var(--app-surface-border); + background: var(--app-surface-bg); + border-radius: 22px; + width: 100%; + padding: 12px; +} + +@media (max-width: 768px) { + .pagination-container .el-pagination > .el-pagination__jump { + display: none !important; + } + + .pagination-container .el-pagination > .el-pagination__sizes { + display: none !important; + } +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +// --- el-card 本业务页外观(与 EP vendors 叠加) --- +.el-card__header { + padding: 14px 16px 10px !important; + min-height: auto; + background: transparent; + border-bottom: 1px solid var(--app-surface-border); +} + +.el-card__body { + padding: 16px !important; +} + +.el-card { + border-radius: var(--app-radius-base); + box-shadow: var(--app-shadow-sm); + border-color: var(--app-surface-border); + overflow: hidden; + transition: + box-shadow 0.2s ease, + border-color 0.2s ease; + background: var(--app-surface-bg); +} + +.el-card:hover { + box-shadow: var(--app-shadow-md); + border-color: var(--app-accent-soft); +} + +.card-box { + margin-bottom: 10px; +} diff --git a/src/assets/styles/components/_legacy-utilities.scss b/src/assets/styles/components/_legacy-utilities.scss new file mode 100644 index 0000000..bce0dc8 --- /dev/null +++ b/src/assets/styles/components/_legacy-utilities.scss @@ -0,0 +1,76 @@ +/* 工具类 */ + +.pt5 { + padding-top: 5px; +} + +.pr5 { + padding-right: 5px; +} + +.pl5 { + padding-left: 5px; +} + +.pb5 { + padding-bottom: 5px; +} + +.mt5 { + margin-top: 5px; +} + +.mr5 { + margin-right: 5px; +} + +.mb5 { + margin-bottom: 5px; +} + +.mb8 { + margin-bottom: 8px; +} + +.ml5 { + margin-left: 5px; +} + +.mt10 { + margin-top: 10px; +} + +.mr10 { + margin-right: 10px; +} + +.mb10 { + margin-bottom: 10px; +} + +.ml10 { + margin-left: 10px; +} + +.mt20 { + margin-top: 20px; +} + +.mr20 { + margin-right: 20px; +} + +.mb20 { + margin-bottom: 20px; +} + +.ml20 { + margin-left: 20px; +} + +.el-dialog.scrollbar .el-dialog__body { + overflow: auto; + overflow-x: hidden; + max-height: 70vh; + padding: 14px 22px 4px; +} diff --git a/src/assets/styles/components/_misc.scss b/src/assets/styles/components/_misc.scss new file mode 100644 index 0000000..15e3a9f --- /dev/null +++ b/src/assets/styles/components/_misc.scss @@ -0,0 +1,9 @@ +.link-type, +.link-type:focus { + color: var(--el-color-primary); + cursor: pointer; + + &:hover { + color: var(--app-accent-strong); + } +} diff --git a/src/assets/styles/components/_page-shell.scss b/src/assets/styles/components/_page-shell.scss new file mode 100644 index 0000000..e9b1dc3 --- /dev/null +++ b/src/assets/styles/components/_page-shell.scss @@ -0,0 +1,200 @@ +/* Shared vertical page shell spacing helpers. */ + +.p-2 { + display: flex; + flex-direction: column; + gap: 12px; +} + +.app-main > .p-2 { + padding: 0 !important; + margin: 0 !important; +} + +.p-2 > .el-row, +.p-2 > .el-card, +.p-2 > .search-wrap, +.p-2 > div, +.p-2 > section { + margin-bottom: 0 !important; +} + +// --- CRUD / 树表页用混入(在 .vue 内 @use 后 @include) --- + +@mixin action-link-buttons($background: rgba(53, 109, 255, 0.08)) { + .data-table { + :deep(.el-button.is-link) { + width: 32px; + height: 32px; + border-radius: 10px; + background: $background; + } + } +} + +@mixin toolbar-responsive($mobile-breakpoint: 900px) { + @media (max-width: $mobile-breakpoint) { + .toolbar-shell { + align-items: flex-start; + } + } +} + +@mixin content-stack($gap: 12px) { + .content-main { + display: flex; + flex-direction: column; + gap: $gap; + } +} + +@mixin dept-tree-panel($padding-top: 6px) { + .dept-tree { + padding-top: $padding-top; + } +} + +@mixin collapsible-tree-layout($mobile-breakpoint: 900px) { + .content-grid, + .selector-layout { + align-items: stretch; + } + + .tree-panel-col, + .tree-content-col { + min-width: 0; + transition: + max-width 0.24s ease, + flex-basis 0.24s ease; + } + + .tree-panel-col.is-collapsed { + max-width: 56px; + flex: 0 0 56px; + } + + .tree-content-col.is-tree-collapsed { + max-width: calc(100% - 56px); + flex: 0 0 calc(100% - 56px); + } + + .tree-panel-shell, + .side-panel { + height: 100%; + } + + .tree-panel-shell { + --tree-panel-max-height: 620px; + } + + .tree-panel-shell :deep(.el-card__header) { + display: block; + padding: 12px 16px !important; + } + + .tree-panel-shell :deep(.el-card__body) { + display: flex; + flex-direction: column; + height: var(--tree-panel-max-height); + min-height: 0; + max-height: var(--tree-panel-max-height); + overflow: hidden; + } + + .tree-panel-header { + cursor: pointer; + user-select: none; + } + + .tree-panel-header::after { + display: block; + width: 9px; + min-width: 9px; + height: 9px; + margin-left: auto; + flex-shrink: 0; + transform-origin: center; + transform: rotate(135deg); + } + + .tree-panel-header.is-collapsed { + justify-content: center; + } + + .side-panel.is-collapsed :deep(.el-card__body) { + display: none; + } + + .dept-tree, + .selector-tree { + flex: 1 1 auto; + min-height: 180px; + max-height: 100%; + overflow-y: auto; + overflow-x: hidden; + padding-right: 4px; + scrollbar-width: thin; + } + + .dept-tree::-webkit-scrollbar, + .selector-tree::-webkit-scrollbar { + width: 6px; + } + + .dept-tree::-webkit-scrollbar-thumb, + .selector-tree::-webkit-scrollbar-thumb { + border-radius: 999px; + background: var(--app-text-muted); + opacity: 0.55; + } + + .dept-tree::-webkit-scrollbar-track, + .selector-tree::-webkit-scrollbar-track { + background: transparent; + } + + .tree-panel-shell.is-collapsed :deep(.el-card__header) { + padding: 12px 0 !important; + } + + .tree-panel-shell.is-collapsed .tree-panel-header::after { + margin-left: 0; + } + + .tree-panel-shell.is-collapsed .tree-panel-header::after { + transform: rotate(-45deg); + } + + @media (max-width: $mobile-breakpoint) { + .tree-panel-col, + .tree-content-col { + max-width: 100%; + flex: 0 0 100%; + } + + .tree-panel-shell { + --tree-panel-max-height: 420px; + } + + .side-panel.is-collapsed { + height: auto; + } + } +} + +@mixin table-crud-page($mobile-breakpoint: 900px, $background: rgba(53, 109, 255, 0.08)) { + @include action-link-buttons($background); + @include toolbar-responsive($mobile-breakpoint); +} + +@mixin tree-table-crud-page( + $mobile-breakpoint: 900px, + $gap: 12px, + $tree-padding-top: 6px, + $background: rgba(53, 109, 255, 0.08) +) { + @include content-stack($gap); + @include dept-tree-panel($tree-padding-top); + @include collapsible-tree-layout($mobile-breakpoint); + @include table-crud-page($mobile-breakpoint, $background); +} diff --git a/src/assets/styles/components/_query-form.scss b/src/assets/styles/components/_query-form.scss new file mode 100644 index 0000000..8e54e20 --- /dev/null +++ b/src/assets/styles/components/_query-form.scss @@ -0,0 +1,136 @@ +/* Shared search and filter form layout. */ + +.query-form { + display: flex; + flex-wrap: wrap; + gap: 10px 14px; + align-items: center; +} + +.query-form .el-form-item { + flex: 0 0 auto; + margin-bottom: 0; + margin-right: 0; + display: flex; + align-items: center; + min-width: 0; +} + +.query-form .el-form-item__content { + min-width: 0; +} + +.query-form.el-form--inline .el-form-item__label { + flex: 0 0 auto; + width: auto; + min-width: 68px; + overflow: visible; + white-space: nowrap; + word-break: keep-all; + line-break: strict; + justify-content: flex-end; +} + +.query-form .el-form-item:last-child { + margin-left: auto; + align-self: center; +} + +.query-form .el-form-item:last-child .el-form-item__content { + display: inline-flex; + align-items: center; + justify-content: flex-end; + gap: 6px; + flex-wrap: nowrap; +} + +.query-form .el-form-item:last-child .el-button { + margin-left: 0 !important; + flex-shrink: 0; +} + +.query-form .el-form-item:last-child .el-button + .el-button { + margin-left: 0 !important; +} + +.query-form .el-form-item[style*='width: 308px'] { + width: auto !important; + min-width: 376px; +} + +.query-form .el-form-item[style*='width: 308px'] .el-form-item__content { + flex: 0 0 308px; + min-width: 308px; +} + +.query-form .el-form-item[style*='width: 308px'] .el-date-editor.el-range-editor { + width: 308px; + min-width: 308px; +} + +.query-form .el-input__wrapper, +.query-form .el-select__wrapper, +.query-form .el-textarea__inner, +.query-form .el-date-editor, +.query-form .el-range-editor, +.query-form .el-cascader .el-input__wrapper, +.query-form .el-input-number, +.query-form .el-input-number__decrease, +.query-form .el-input-number__increase { + border-radius: var(--app-radius-md) !important; +} + +.query-form .el-button { + border-radius: var(--app-radius-md) !important; +} + +html.dark .query-form .el-button--primary { + --el-button-bg-color: rgba(37, 99, 235, 0.28); + --el-button-border-color: rgba(96, 165, 250, 0.62); + --el-button-text-color: #dbeafe; + --el-button-hover-bg-color: rgba(59, 130, 246, 0.36); + --el-button-hover-border-color: rgba(147, 197, 253, 0.78); + --el-button-hover-text-color: #eff6ff; + --el-button-active-bg-color: rgba(29, 78, 216, 0.44); + --el-button-active-border-color: rgba(96, 165, 250, 0.86); + --el-button-active-text-color: #eff6ff; +} + +@media (max-width: 900px) { + .query-form { + gap: 10px 12px; + } +} + +@media (max-width: 640px) { + .query-form { + display: flex; + flex-direction: column; + align-items: stretch; + } + + .query-form .el-form-item { + width: 100%; + } + + .query-form .el-form-item[style*='width: 308px'] { + min-width: 0; + } + + .query-form .el-form-item[style*='width: 308px'] .el-form-item__content { + flex: 1 1 auto; + min-width: 0; + } + + .query-form .el-input, + .query-form .el-select, + .query-form .el-date-editor, + .query-form .el-cascader, + .query-form .el-input-number { + width: 100% !important; + } + + .query-form .el-form-item:last-child { + margin-left: 0; + } +} diff --git a/src/assets/styles/components/_search-panel.scss b/src/assets/styles/components/_search-panel.scss new file mode 100644 index 0000000..2a17b1a --- /dev/null +++ b/src/assets/styles/components/_search-panel.scss @@ -0,0 +1,91 @@ +/* Search panel, table actions, and responsive panel shells. */ + +// --- 表格工具列链接按钮(全局) --- +.data-table .el-button.is-link { + width: 28px !important; + height: 28px !important; + min-width: 28px !important; + padding: 0 !important; + border-radius: var(--app-radius-md) !important; + background: var(--app-accent-soft) !important; +} + +.data-table .el-button.is-link + .el-button.is-link { + margin-left: 4px !important; +} + +.search-wrap { + margin-bottom: 0 !important; +} + +.search-panel, +.table-panel { + transform: translateZ(0); + backface-visibility: hidden; +} + +.search-panel { + isolation: isolate; + + .el-card__header { + display: block; + padding: 12px 16px !important; + } + + .el-card__body { + padding-top: 16px !important; + overflow: hidden; + max-height: 560px; + opacity: 1; + transition: + max-height 0.32s cubic-bezier(0.22, 1, 0.36, 1), + opacity 0.24s ease, + padding-top 0.24s ease, + padding-bottom 0.24s ease; + } +} + +.search-panel.is-collapsed { + .el-card__body { + max-height: 0; + opacity: 0; + padding-top: 0 !important; + padding-bottom: 0 !important; + pointer-events: none; + } +} + +.search-panel-toggle { + cursor: pointer; + user-select: none; + transition: color 0.24s ease; + + &:hover { + color: var(--app-accent-strong); + } + + &::after { + content: ''; + margin-left: auto; + width: 9px; + height: 9px; + border-right: 2px solid currentColor; + border-bottom: 2px solid currentColor; + color: var(--app-text-muted); + transform: rotate(-135deg); + transition: + transform 0.24s ease, + color 0.24s ease; + } +} + +.search-panel.is-collapsed .search-panel-toggle::after { + transform: rotate(45deg); +} + +html.dark { + .tree-border { + background: var(--app-surface-bg); + border-color: var(--app-surface-border); + } +} diff --git a/src/assets/styles/components/_selector-dialog.scss b/src/assets/styles/components/_selector-dialog.scss new file mode 100644 index 0000000..07930eb --- /dev/null +++ b/src/assets/styles/components/_selector-dialog.scss @@ -0,0 +1,79 @@ +/* Reusable scoped mixins for selector dialogs backed by cards and vxe tables. */ + +/* 为多个根选择器统一设置 gap */ +@mixin shell-gap($selectors...) { + @each $selector in $selectors { + #{$selector} { + gap: 12px; + } + } +} + +/* 卡片区域撑满弹窗 */ +@mixin card-shell { + .selector-card { + height: 100%; + } +} + +/* 按弹窗根 class 收紧 body 顶内边距 */ +@mixin dialog-body-padding($dialog-class) { + .#{$dialog-class} :deep(.el-dialog__body) { + padding-top: 12px; + } +} + +/* 头部标题与已选 tag 换行、窄屏左对齐 */ +@mixin selector-header-tags($mobile-breakpoint: 768px) { + .selector-header { + align-items: flex-start; + } + + .selector-tags { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 6px; + max-width: min(100%, 520px); + } + + .selector-tags :deep(.el-tag) { + margin: 0; + } + + @media (max-width: $mobile-breakpoint) { + .selector-tags { + justify-content: flex-start; + max-width: 100%; + } + } +} + +/* vxe 表格圆角与表头色,与全局表格 token 一致 */ +@mixin selector-table { + .selector-table { + border-radius: 10px; + overflow: hidden; + } + + .selector-table :deep(.vxe-table--render-default) { + border-radius: 10px; + color: var(--app-text-title); + } + + .selector-table :deep(.vxe-header--column) { + background: var(--tableHeaderBg); + color: var(--tableHeaderTextColor); + font-weight: 600; + } + + .selector-table :deep(.vxe-body--column), + .selector-table :deep(.vxe-header--column) { + border-color: var(--app-surface-border); + } + + .selector-table :deep(.vxe-body--row.row--hover), + .selector-table :deep(.vxe-body--row:hover) { + background-color: rgba(53, 109, 255, 0.05); + } +} diff --git a/src/assets/styles/components/_table-toolbar.scss b/src/assets/styles/components/_table-toolbar.scss new file mode 100644 index 0000000..33534ad --- /dev/null +++ b/src/assets/styles/components/_table-toolbar.scss @@ -0,0 +1,84 @@ +/* Table headers and toolbar action layouts. */ + +.top-right-btn { + margin-left: auto; +} + +.panel-heading { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.panel-heading h3, +.table-heading h3 { + margin: 0; + font-size: 15px; + letter-spacing: 0; + color: var(--app-text-title); +} + +.panel-kicker { + display: none; + color: var(--app-accent-strong); + font-size: 11px; + font-weight: 600; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.toolbar-shell { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; + flex-wrap: wrap; +} + +.table-heading p { + margin: 4px 0 0; + font-size: 13px; + color: var(--app-text-muted); + line-height: 1.5; +} + +.toolbar-actions { + display: flex; + align-items: center; + flex-wrap: nowrap; + gap: 8px; + margin-left: auto; + white-space: nowrap; + overflow-x: auto; + overflow-y: hidden; + padding-bottom: 2px; +} + +.toolbar-actions::-webkit-scrollbar { + height: 0; +} + +.toolbar-actions > * { + flex-shrink: 0; +} + +.toolbar-actions .top-right-btn { + margin-left: 0; + flex-shrink: 0; +} + +.toolbar-actions .el-button + .el-button, +.toolbar-actions .el-dropdown + .el-button, +.toolbar-actions .el-button + .el-dropdown, +.toolbar-actions .el-dropdown + .el-dropdown, +.toolbar-actions .top-right-btn + .el-button, +.toolbar-actions .el-button + .top-right-btn, +.toolbar-actions .top-right-btn + .el-dropdown, +.toolbar-actions .el-dropdown + .top-right-btn { + margin-left: 0 !important; +} + +.table-panel .toolbar-actions .el-button:not(.is-circle):not(.is-link) { + border-radius: var(--app-radius-md) !important; +} diff --git a/src/assets/styles/components/_theme-helpers.scss b/src/assets/styles/components/_theme-helpers.scss new file mode 100644 index 0000000..13b73bf --- /dev/null +++ b/src/assets/styles/components/_theme-helpers.scss @@ -0,0 +1,49 @@ +/* Legacy theme helper classes and color accents. */ + +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20b2aa; + border-color: #20b2aa; + color: #ffffff; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48d1cc; + border-color: #48d1cc; + color: #ffffff; +} + +.el-button--cyan { + background-color: #20b2aa; + border-color: #20b2aa; + color: #ffffff; +} + +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss new file mode 100644 index 0000000..96bc9cb --- /dev/null +++ b/src/assets/styles/index.scss @@ -0,0 +1,26 @@ +// ============================================================================= +// 全局样式入口(加载顺序即最终 CSS 层叠顺序,后载入的同优先级规则会覆盖先载入的) +// ============================================================================= +@use './tokens/sass-vars' as *; +@use './tokens/theme'; +@use './tokens/theme-dark'; +@use './mixin.scss'; +@use './transition.scss'; +@use './base/document'; +@use './layout/surface'; +@use './layout/sidebar/shell' as sidebar-shell; +@use './layout/sidebar/menu'; +@use './layout/sidebar/collapsed'; +@use './layout/sidebar/responsive'; +@use './layout/sidebar/popper' as sidebar-popper; +@use './components/legacy-utilities'; +@use './components/misc'; +@use './components/page-shell'; +@use './components/card-shell'; +@use './components/theme-helpers'; +@use './components/table-toolbar'; +@use './components/query-form'; +@use './components/search-panel'; + +@use './vendors/element-plus/index' as element-custom; +@use 'animate.css'; diff --git a/src/assets/styles/layout/_surface.scss b/src/assets/styles/layout/_surface.scss new file mode 100644 index 0000000..ad6f38b --- /dev/null +++ b/src/assets/styles/layout/_surface.scss @@ -0,0 +1,24 @@ +/* 主内容区面板(.app-container、.panel、.search):不依赖 Element 类名,与 components 中 EP 类补丁区分。 */ + +// --- 整块内容容器(常见于路由视图外包一层) --- +.app-container { +} + +// --- 可复用面板条(hover 抬升阴影,非 el-card) --- +.panel, +.search { + margin-bottom: 0.75rem; + border-radius: var(--app-radius-base); + border: 1px solid var(--app-surface-border); + background-color: var(--app-surface-bg); + padding: 0.75rem; + box-shadow: 0 0 0 rgba(0, 0, 0, 0); + transition: + box-shadow 0.2s ease, + border-color 0.2s ease; + + &:hover { + box-shadow: var(--app-shadow-sm); + border-color: rgba(64, 158, 255, 0.2); + } +} diff --git a/src/assets/styles/layout/sidebar/_collapsed.scss b/src/assets/styles/layout/sidebar/_collapsed.scss new file mode 100644 index 0000000..c7e572a --- /dev/null +++ b/src/assets/styles/layout/sidebar/_collapsed.scss @@ -0,0 +1,141 @@ +/* 桌面端折叠侧栏:窄宽、仅图标、子菜单箭头隐藏与 tooltip 触发区域(类名 .hideSidebar)。 */ + +#app { + .hideSidebar { + .sidebar-container { + width: 58px !important; + } + + .main-container { + margin-left: 70px; + } + + .sidebar-shell { + padding-left: 4px; + padding-right: 4px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + height: 40px; + width: 40px !important; + min-width: 40px !important; + margin: 3px auto !important; + display: flex; + align-items: center; + justify-content: center !important; + box-sizing: border-box; + + &.is-active { + background-color: var(--side-menu-active-bg) !important; + color: var(--side-menu-active-text) !important; + box-shadow: inset 3px 0 0 var(--side-menu-active-line); + } + + .el-tooltip { + padding: 0 !important; + } + + .el-menu-tooltip__trigger { + width: 100%; + height: 100%; + display: inline-flex !important; + align-items: center; + justify-content: center; + line-height: 1; + } + + .svg-icon { + margin-right: 0 !important; + } + } + + & .el-sub-menu { + overflow: hidden; + border-radius: var(--app-radius-md); + + .el-sub-menu__title, + .el-sub-menu__title.el-tooltip__trigger { + border-radius: var(--app-radius-md); + height: 40px; + padding: 0 !important; + display: flex; + align-items: center; + justify-content: center !important; + line-height: 1; + } + + &.is-active .el-sub-menu__title, + &.is-active .el-sub-menu__title.el-tooltip__trigger { + background-color: var(--side-menu-active-bg) !important; + box-shadow: inset 3px 0 0 var(--side-menu-active-line); + } + + & > .el-sub-menu__title { + padding: 0 !important; + } + } + + .el-menu--collapse { + > div > .el-sub-menu { + width: 40px !important; + min-width: 40px !important; + margin: 3px auto !important; + } + + > div > .el-sub-menu > .el-sub-menu__title, + > div > .el-sub-menu > .el-sub-menu__title.el-tooltip__trigger { + width: 100% !important; + min-width: 100% !important; + margin: 0 !important; + box-sizing: border-box; + transform: none; + position: relative; + } + + .is-active .svg-icon { + fill: currentColor; + } + + > div > .el-sub-menu > .el-sub-menu__title .svg-icon { + position: absolute; + left: 50%; + top: 50%; + width: 16px; + height: 16px; + transform: translate(-50%, -50%); + display: block; + margin: 0 !important; + margin-right: 0 !important; + } + + > div > .el-sub-menu > .el-sub-menu__title { + justify-content: center !important; + + .el-sub-menu__icon-arrow { + display: none !important; + width: 0 !important; + margin: 0 !important; + overflow: hidden !important; + } + + & > span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + + & > i { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } +} diff --git a/src/assets/styles/layout/sidebar/_menu.scss b/src/assets/styles/layout/sidebar/_menu.scss new file mode 100644 index 0000000..b863261 --- /dev/null +++ b/src/assets/styles/layout/sidebar/_menu.scss @@ -0,0 +1,113 @@ +/* 侧栏内 el-menu:一级/嵌套项的 hover、active、浅色与 theme-dark 分支(与 _shell 中变量定义配合)。 */ + +@use '../../tokens/sass-vars' as *; + +#app { + .sidebar-container { + .theme-dark .submenu-title-noDropdown, + .theme-dark .el-sub-menu__title { + border-radius: var(--app-radius-md); + margin: 3px 8px; + height: 40px; + + &:hover { + background-color: var(--side-menu-hover-bg) !important; + color: var(--side-menu-hover-text) !important; + } + } + + .submenu-title-noDropdown, + .el-sub-menu__title { + border-radius: var(--app-radius-md); + margin: 3px 8px; + height: 40px; + color: var(--side-menu-text) !important; + + &:hover { + background-color: var(--side-menu-hover-bg) !important; + color: var(--side-menu-hover-text) !important; + } + } + + & .theme-dark .is-active > .el-sub-menu__title { + color: var(--side-menu-active-text) !important; + } + + & .nest-menu .el-sub-menu > .el-sub-menu__title, + & .el-sub-menu .el-menu-item { + min-width: calc($base-sidebar-width - 16px) !important; + border-radius: var(--app-radius-md); + height: 40px; + margin: 3px 8px; + background: transparent !important; + + &:not(.is-active):hover { + background-color: var(--side-menu-hover-bg) !important; + color: var(--side-menu-hover-text) !important; + } + } + + & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title, + & .theme-dark .el-sub-menu .el-menu-item { + border-radius: var(--app-radius-md); + height: 40px; + margin: 3px 8px; + background: transparent !important; + + &.is-active { + background: var(--side-menu-active-bg) !important; + color: var(--side-menu-active-text) !important; + box-shadow: + inset 3px 0 0 var(--side-menu-active-line), + inset 0 0 0 1px var(--side-menu-active-border); + } + + &:not(.is-active):hover { + background-color: var(--side-menu-hover-bg) !important; + color: var(--side-menu-hover-text) !important; + } + } + + & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title, + & .theme-dark .el-menu-item { + border-radius: var(--app-radius-md); + height: 40px; + margin: 3px 8px; + background: transparent !important; + + &.is-active { + background: var(--side-menu-active-bg) !important; + color: var(--side-menu-active-text) !important; + box-shadow: + inset 3px 0 0 var(--side-menu-active-line), + inset 0 0 0 1px var(--side-menu-active-border); + } + + &:not(.is-active):hover { + background-color: var(--side-menu-hover-bg) !important; + color: var(--side-menu-hover-text) !important; + } + } + + & .nest-menu .el-sub-menu > .el-sub-menu__title, + & .el-menu-item { + border-radius: var(--app-radius-md); + height: 40px; + margin: 3px 8px; + background: transparent !important; + + &.is-active { + background: var(--side-menu-active-bg) !important; + color: var(--side-menu-active-text) !important; + box-shadow: + inset 3px 0 0 var(--side-menu-active-line), + inset 0 0 0 1px var(--side-menu-active-border); + } + + &:not(.is-active):hover { + background-color: var(--side-menu-hover-bg) !important; + color: var(--side-menu-hover-text) !important; + } + } + } +} diff --git a/src/assets/styles/layout/sidebar/_popper.scss b/src/assets/styles/layout/sidebar/_popper.scss new file mode 100644 index 0000000..03468dc --- /dev/null +++ b/src/assets/styles/layout/sidebar/_popper.scss @@ -0,0 +1,22 @@ +/* 折叠后弹出子菜单(el-popper.is-pure):圆角与弹出层内 menu 项样式。 */ + +.el-menu--vertical { + & > .el-menu { + .svg-icon { + margin-right: 16px; + } + } +} + +.el-popper.is-pure { + border-radius: var(--app-radius-md); + box-shadow: var(--app-shadow-md); + + .el-menu--popup { + border-radius: var(--app-radius-md); + } + + .el-menu-item { + border-radius: var(--app-radius-sm); + } +} diff --git a/src/assets/styles/layout/sidebar/_responsive.scss b/src/assets/styles/layout/sidebar/_responsive.scss new file mode 100644 index 0000000..a845c43 --- /dev/null +++ b/src/assets/styles/layout/sidebar/_responsive.scss @@ -0,0 +1,31 @@ +/* 移动端侧栏:抽屉式滑出、隐藏时 translate;.withoutAnimation 关闭过渡(与布局组件 class 约定一致)。 */ + +@use '../../tokens/sass-vars' as *; + +#app { + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform 0.28s; + width: $base-sidebar-width !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(calc(-#{$base-sidebar-width} - 12px), 0, 0); + } + } + } + + .withoutAnimation { + .main-container, + .sidebar-container { + transition: none; + } + } +} diff --git a/src/assets/styles/layout/sidebar/_shell.scss b/src/assets/styles/layout/sidebar/_shell.scss new file mode 100644 index 0000000..9b0eb07 --- /dev/null +++ b/src/assets/styles/layout/sidebar/_shell.scss @@ -0,0 +1,132 @@ +/* 侧栏外壳:固定定位宽度、主区左边距、滚动条与 el-menu 根级样式;与 _menu/_collapsed/_responsive 分工。 */ + +@use '../../tokens/sass-vars' as *; + +#app { + // --- 主内容区相对侧栏的偏移 --- + .main-container { + height: 100%; + transition: margin-left 0.28s; + margin-left: calc(#{$base-sidebar-width} + 12px); + position: relative; + background: transparent; + } + + .sidebarHide { + margin-left: 0 !important; + } + + // --- 侧栏固定列:宽度过渡、主题下滚动条内菜单色变量 --- + .sidebar-container { + -webkit-transition: width 0.28s; + transition: width 0.28s; + width: $base-sidebar-width !important; + background: transparent; + height: calc(100vh - 24px); + position: fixed; + font-size: 0; + top: 12px; + left: 12px; + z-index: 1001; + overflow: hidden; + box-sizing: border-box; + padding: 0; + border-right: none; + -webkit-box-shadow: none; + box-shadow: none; + + .horizontal-collapse-transition { + transition: + 0s width ease-in-out, + 0s padding-left ease-in-out, + 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0; + } + + .el-scrollbar { + height: 100%; + } + + .el-scrollbar.theme-light { + --side-menu-text: #1f2937; + --side-menu-hover-bg: rgba(64, 158, 255, 0.08); + --side-menu-hover-text: #111827; + --side-menu-active-bg: rgba(64, 158, 255, 0.12); + --side-menu-active-text: #409eff; + --side-menu-active-border: rgba(64, 158, 255, 0.16); + --side-menu-active-line: #409eff; + } + + .el-scrollbar.theme-dark { + --side-menu-text: #e5edf8; + --side-menu-hover-bg: rgba(255, 255, 255, 0.08); + --side-menu-hover-text: #ffffff; + --side-menu-active-bg: rgba(64, 158, 255, 0.22); + --side-menu-active-text: #ffffff; + --side-menu-active-border: rgba(96, 165, 250, 0.24); + --side-menu-active-line: #60a5fa; + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 10px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + background: transparent !important; + } + + .el-menu--inline { + background: transparent !important; + } + + .el-menu-item, + .menu-title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + .el-menu-item, + .el-sub-menu__title, + .el-menu-item .svg-icon, + .el-sub-menu__title .svg-icon, + .el-sub-menu__icon-arrow { + color: var(--side-menu-text) !important; + fill: currentColor; + } + + .el-menu-item, + .el-sub-menu__title { + position: relative; + font-weight: 500; + } + + .el-menu-item .el-menu-tooltip__trigger { + display: inline-block !important; + } + } + + .el-menu--collapse .el-menu .el-sub-menu { + min-width: $base-sidebar-width !important; + } +} diff --git a/src/assets/styles/mixin.scss b/src/assets/styles/mixin.scss new file mode 100644 index 0000000..f62e4da --- /dev/null +++ b/src/assets/styles/mixin.scss @@ -0,0 +1,65 @@ +// 清除浮动(嵌套用) +@mixin clearfix { + &:after { + content: ''; + display: table; + clear: both; + } +} + +// WebKit 滚动条配色(用于可滚动容器) +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: var(--app-surface-border); + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: var(--app-text-muted); + border-radius: 20px; + } +} + +// 铺满父级的相对定位层 +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +// 百分比宽度水平居中块 +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +// CSS 三角箭头(极少用,保留兼容) +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/src/assets/styles/tokens/_exports.scss b/src/assets/styles/tokens/_exports.scss new file mode 100644 index 0000000..98bfce9 --- /dev/null +++ b/src/assets/styles/tokens/_exports.scss @@ -0,0 +1,21 @@ +/* CSS module exports preserved for JS/Vue consumers. */ + +@use './sass-vars' as *; + +:export { + menuColor: $base-menu-color; + menuLightColor: $base-menu-light-color; + menuColorActive: $base-menu-color-active; + menuBackground: $base-menu-background; + menuLightBackground: $base-menu-light-background; + subMenuBackground: $base-sub-menu-background; + subMenuHover: $base-sub-menu-hover; + sideBarWidth: $base-sidebar-width; + logoTitleColor: $base-logo-title-color; + logoLightTitleColor: $base-logo-light-title-color; + primaryColor: $color-primary; + successColor: $color-success; + dangerColor: $color-danger; + infoColor: $color-info; + warningColor: $color-warning; +} diff --git a/src/assets/styles/tokens/_sass-vars.scss b/src/assets/styles/tokens/_sass-vars.scss new file mode 100644 index 0000000..da76574 --- /dev/null +++ b/src/assets/styles/tokens/_sass-vars.scss @@ -0,0 +1,42 @@ +/* Sass variables consumed by legacy modules and CSS module exports. */ + +$blue: #324157; +$light-blue: #3a71a8; +$red: #c03639; +$pink: #e65d6e; +$green: #30b08f; +$tiffany: #4ab7bd; +$yellow: #fec171; +$panGreen: #30b08f; + +$base-menu-color: var(--menuColor); +$base-menu-hover: var(--menuHover); +$base-menu-color-active: var(--menuActiveText); +$base-menu-background: var(--menuBg); +$base-logo-title-color: #ffffff; + +$base-menu-light-color: rgba(0, 0, 0, 0.7); +$base-menu-light-background: #ffffff; +$base-logo-light-title-color: #001529; + +$base-sub-menu-background: var(--subMenuBg); +$base-sub-menu-hover: var(--subMenuHover); +$base-sub-menu-title-hover: var(--subMenuTitleHover); + +$fixed-header-bg: var(--fixedHeaderBg); +$table-header-bg: var(--tableHeaderBg); +$table-header-text-color: var(--tableHeaderTextColor); + +$color-primary: #409eff; +$color-success: #67c23a; +$color-warning: #e6a23c; +$color-danger: #f56c6c; +$color-info: #909399; + +$--color-primary: $color-primary; +$--color-success: $color-success; +$--color-warning: $color-warning; +$--color-danger: $color-danger; +$--color-info: $color-info; + +$base-sidebar-width: 200px; diff --git a/src/assets/styles/tokens/_theme-dark.scss b/src/assets/styles/tokens/_theme-dark.scss new file mode 100644 index 0000000..354e62b --- /dev/null +++ b/src/assets/styles/tokens/_theme-dark.scss @@ -0,0 +1,229 @@ +/* 暗色主题:在 html.dark 上覆写与浅色同名的业务/app 变量 */ + +html.dark { + --menuBg: #111827; + --menuColor: #e5edf8; + --menuActiveText: #f8fbff; + --menuHover: rgba(85, 129, 255, 0.2); + + --subMenuBg: #111827; + --subMenuActiveText: #f8fbff; + --subMenuHover: rgba(85, 129, 255, 0.18); + --subMenuTitleHover: rgba(148, 163, 184, 0.12); + + --fixedHeaderBg: #0f172a; + --tableHeaderBg: var(--el-bg-color); + --tableHeaderTextColor: var(--el-text-color); + + // --- 表格:暗色下选中行背景 --- + --el-table-current-row-bg-color: rgba(53, 109, 255, 0.14); + --el-table-row-hover-bg-color: rgba(148, 163, 184, 0.07); + + // --- 全局:暗色下修正 primary 浅底色(表格行选中、树节点等) --- + --el-color-primary-light-9: rgba(53, 109, 255, 0.14); + + .el-tree-node__content { + --el-color-primary-light-9: rgba(53, 109, 255, 0.14); + } + + .el-button:not(.el-button--primary):not(.el-button--success):not(.el-button--warning):not(.el-button--danger):not(.el-button--info):not(.is-text):not( + .is-link + ) { + --el-button-bg-color: rgba(15, 23, 42, 0.44); + --el-button-border-color: rgba(96, 165, 250, 0.38); + --el-button-text-color: #dbeafe; + --el-button-hover-bg-color: rgba(96, 165, 250, 0.16); + --el-button-hover-border-color: rgba(96, 165, 250, 0.58); + --el-button-hover-text-color: #bfdbfe; + --el-button-active-bg-color: rgba(59, 130, 246, 0.22); + --el-button-active-border-color: rgba(96, 165, 250, 0.72); + --el-button-active-text-color: #dbeafe; + } + + // --- 各类型按钮(含 plain)在暗色下的 EP CSS 变量 --- + .el-button--primary { + --el-button-bg-color: #2b6bd3; + --el-button-border-color: #3a7be8; + --el-button-text-color: #eef4ff; + --el-button-hover-bg-color: #3a7be8; + --el-button-hover-border-color: #3a7be8; + --el-button-active-bg-color: #255fb8; + --el-button-active-border-color: #255fb8; + } + + .el-button--primary.is-plain { + --el-button-bg-color: rgba(43, 107, 211, 0.12); + --el-button-border-color: rgba(58, 123, 232, 0.5); + --el-button-text-color: #dbe8ff; + --el-button-hover-text-color: #dbe8ff; + --el-button-hover-bg-color: rgba(58, 123, 232, 0.2); + --el-button-hover-border-color: rgba(58, 123, 232, 0.7); + --el-button-active-text-color: #dbe8ff; + --el-button-active-bg-color: rgba(43, 107, 211, 0.28); + --el-button-active-border-color: rgba(43, 107, 211, 0.8); + } + + .el-button--success { + --el-button-bg-color: #1f8a5a; + --el-button-border-color: #29a46d; + --el-button-text-color: #eefaf4; + --el-button-hover-bg-color: #29a46d; + --el-button-hover-border-color: #29a46d; + --el-button-active-bg-color: #1b784f; + --el-button-active-border-color: #1b784f; + } + + .el-button--success.is-plain { + --el-button-bg-color: rgba(31, 138, 90, 0.12); + --el-button-border-color: rgba(41, 164, 109, 0.5); + --el-button-text-color: #dbf6e8; + --el-button-hover-text-color: #dbf6e8; + --el-button-hover-bg-color: rgba(41, 164, 109, 0.2); + --el-button-hover-border-color: rgba(41, 164, 109, 0.7); + --el-button-active-text-color: #dbf6e8; + --el-button-active-bg-color: rgba(31, 138, 90, 0.28); + --el-button-active-border-color: rgba(31, 138, 90, 0.8); + } + + .el-button--warning { + --el-button-bg-color: #b87922; + --el-button-border-color: #d6953b; + --el-button-text-color: #fff7e6; + --el-button-hover-bg-color: #d6953b; + --el-button-hover-border-color: #d6953b; + --el-button-active-bg-color: #a56c1d; + --el-button-active-border-color: #a56c1d; + } + + .el-button--warning.is-plain { + --el-button-bg-color: rgba(184, 121, 34, 0.12); + --el-button-border-color: rgba(214, 149, 59, 0.5); + --el-button-text-color: #ffecc8; + --el-button-hover-text-color: #ffecc8; + --el-button-hover-bg-color: rgba(214, 149, 59, 0.2); + --el-button-hover-border-color: rgba(214, 149, 59, 0.7); + --el-button-active-text-color: #ffecc8; + --el-button-active-bg-color: rgba(184, 121, 34, 0.28); + --el-button-active-border-color: rgba(184, 121, 34, 0.8); + } + + .el-button--danger { + --el-button-bg-color: #b24a4a; + --el-button-border-color: #d16060; + --el-button-text-color: #ffecec; + --el-button-hover-bg-color: #d16060; + --el-button-hover-border-color: #d16060; + --el-button-active-bg-color: #9c3f3f; + --el-button-active-border-color: #9c3f3f; + } + + .el-button--danger.is-plain { + --el-button-bg-color: rgba(178, 74, 74, 0.12); + --el-button-border-color: rgba(209, 96, 96, 0.5); + --el-button-text-color: #ffd6d6; + --el-button-hover-text-color: #ffd6d6; + --el-button-hover-bg-color: rgba(209, 96, 96, 0.2); + --el-button-hover-border-color: rgba(209, 96, 96, 0.7); + --el-button-active-text-color: #ffd6d6; + --el-button-active-bg-color: rgba(178, 74, 74, 0.28); + --el-button-active-border-color: rgba(178, 74, 74, 0.8); + } + + .el-button--info { + --el-button-bg-color: #4b5563; + --el-button-border-color: #667085; + --el-button-text-color: #f3f4f6; + --el-button-hover-bg-color: #667085; + --el-button-hover-border-color: #667085; + --el-button-active-bg-color: #3f4753; + --el-button-active-border-color: #3f4753; + } + + .el-button--info.is-plain { + --el-button-bg-color: rgba(75, 85, 99, 0.16); + --el-button-border-color: rgba(102, 112, 133, 0.55); + --el-button-text-color: #e5e7eb; + --el-button-hover-text-color: #e5e7eb; + --el-button-hover-bg-color: rgba(102, 112, 133, 0.22); + --el-button-hover-border-color: rgba(102, 112, 133, 0.75); + --el-button-active-text-color: #e5e7eb; + --el-button-active-bg-color: rgba(75, 85, 99, 0.3); + --el-button-active-border-color: rgba(75, 85, 99, 0.85); + } + + // --- Switch / Tag 暗色适配 --- + .el-switch { + --el-switch-on-color: var(--el-color-primary-dark-6); + --el-switch-border-color: var(--el-color-primary-light-2); + } + + .el-tag--primary { + --el-tag-bg-color: var(--el-color-primary-dark-6); + --el-tag-border-color: var(--el-color-primary-light-2); + } + + .el-tag--success { + --el-tag-bg-color: rgba(31, 138, 90, 0.18); + --el-tag-border-color: rgba(41, 164, 109, 0.6); + --el-tag-text-color: #c7f2df; + } + + .el-tag--warning { + --el-tag-bg-color: rgba(184, 121, 34, 0.18); + --el-tag-border-color: rgba(214, 149, 59, 0.6); + --el-tag-text-color: #ffe6bb; + } + + .el-tag--danger { + --el-tag-bg-color: rgba(178, 74, 74, 0.18); + --el-tag-border-color: rgba(209, 96, 96, 0.6); + --el-tag-text-color: #ffd0d0; + } + + .el-tag--info { + --el-tag-bg-color: rgba(75, 85, 99, 0.18); + --el-tag-border-color: rgba(102, 112, 133, 0.6); + --el-tag-text-color: #e5e7eb; + } + + // --- tags-view 与 app 壳变量(暗色) --- + --tags-view-active-bg: var(--el-color-primary-dark-6); + --tags-view-active-border-color: var(--el-color-primary-light-2); + + --app-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.28); + --app-shadow-md: 0 12px 28px rgba(0, 0, 0, 0.28); + --app-shadow-lg: 0 20px 44px rgba(0, 0, 0, 0.32); + --app-surface-bg: #111827; + --app-surface-border: rgba(71, 85, 105, 0.5); + --app-shell-bg: #060b16; + --app-shell-spot-1: rgba(69, 120, 255, 0.18); + --app-shell-spot-2: rgba(45, 212, 191, 0.14); + --app-sidebar-border: rgba(148, 163, 184, 0.1); + --app-navbar-bg: #111827; + --app-navbar-border: rgba(71, 85, 105, 0.48); + --app-navbar-shadow: 0 1px 2px rgba(0, 0, 0, 0.22); + --app-accent-soft: rgba(64, 158, 255, 0.14); + --app-accent-strong: #60a5fa; + --app-text-title: #f3f7ff; + --app-text-muted: #8ea0bd; + --app-overlay-mask: rgba(2, 6, 23, 0.58); + --app-elevated-soft-bg: rgba(148, 163, 184, 0.08); + --app-elevated-close-bg: rgba(148, 163, 184, 0.18); + --app-elevated-close-hover-bg: rgba(96, 165, 250, 0.18); + --el-bg-color-page: var(--app-shell-bg); + + // --- vxe-table 暗色(与 vxe 业务表并存) --- + --vxe-font-color: #98989e; + --vxe-primary-color: #2c7ecf; + --vxe-icon-background-color: #98989e; + --vxe-table-font-color: #98989e; + --vxe-table-resizable-color: #95969a; + --vxe-table-header-background-color: #28282a; + --vxe-table-body-background-color: #151518; + --vxe-table-background-color: #4a5663; + --vxe-table-border-width: 1px; + --vxe-table-border-color: #37373a; + --vxe-toolbar-background-color: #37373a; + + --brder-color: #37373a; +} diff --git a/src/assets/styles/tokens/_theme.scss b/src/assets/styles/tokens/_theme.scss new file mode 100644 index 0000000..4562c1f --- /dev/null +++ b/src/assets/styles/tokens/_theme.scss @@ -0,0 +1,67 @@ +/* 浅色主题:仅声明 CSS 自定义属性,无具体选择器样式。 */ + +:root { + // --- 侧栏与顶栏表格区域 --- + --menuBg: #ffffff; + --menuColor: #1f2937; + --menuActiveText: #111827; + --menuHover: rgba(64, 158, 255, 0.12); + + --subMenuBg: #ffffff; + --subMenuActiveText: #111827; + --subMenuHover: rgba(64, 158, 255, 0.12); + --subMenuTitleHover: rgba(64, 158, 255, 0.08); + + --el-menu-base-level-padding: 12px; + --el-menu-level-padding: 8px; + --el-menu-item-height: 50px; + + --fixedHeaderBg: #ffffff; + --tableHeaderBg: #f8fafc; + --tableHeaderTextColor: #475569; + + --brder-color: #e8e8e8; + + --tags-view-active-bg: var(--el-color-primary); + --tags-view-active-border-color: var(--el-color-primary); + + // --- 应用壳:圆角、阴影、表面、导航、强调色与文案色 --- + --app-radius-sm: max(0px, calc(var(--app-radius-base) - 8px)); + --app-radius-md: max(0px, calc(var(--app-radius-base) - 4px)); + --app-radius-lg: var(--app-radius-base); + --app-radius-base: 14px; + + --app-space-base: 12px; + + --app-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.05); + --app-shadow-md: 0 8px 24px rgba(15, 23, 42, 0.06); + --app-shadow-lg: 0 14px 36px rgba(15, 23, 42, 0.1); + + --app-font-size-base: 14px; + + --app-surface-bg: #ffffff; + --app-surface-border: #e5e7eb; + --app-shell-bg: #f5f7fa; + --app-shell-spot-1: rgba(64, 158, 255, 0.08); + --app-shell-spot-2: rgba(64, 158, 255, 0.04); + --app-sidebar-border: #e5e7eb; + --app-navbar-bg: #ffffff; + --app-navbar-border: #e5e7eb; + --app-navbar-shadow: 0 1px 2px rgba(15, 23, 42, 0.05); + --app-accent-soft: rgba(64, 158, 255, 0.08); + --app-accent-strong: #409eff; + --app-text-title: #1f2937; + --app-text-muted: #6b7280; + --app-overlay-mask: rgba(15, 23, 42, 0.22); + --app-elevated-soft-bg: #f8fafc; + --app-elevated-close-bg: rgba(148, 163, 184, 0.14); + --app-elevated-close-hover-bg: rgba(64, 158, 255, 0.14); + + // --- 与 Element Plus 主题变量的桥接(页面背景、默认圆角/阴影) --- + --el-border-radius-base: var(--app-radius-md); + --el-border-radius-small: var(--app-radius-sm); + --el-border-radius-round: 999px; + --el-box-shadow-light: var(--app-shadow-sm); + --el-box-shadow: var(--app-shadow-md); + --el-bg-color-page: var(--app-shell-bg); +} diff --git a/src/assets/styles/transition.scss b/src/assets/styles/transition.scss new file mode 100644 index 0000000..e6d409f --- /dev/null +++ b/src/assets/styles/transition.scss @@ -0,0 +1,52 @@ +// 全局 Vue transition 类名(fade / fade-transform / breadcrumb),与业务模块无关 + +// --- 淡入淡出 --- +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter-from, +.fade-leave-active { + opacity: 0; +} + +// --- 带水平位移的淡入淡出(常用于主视图切换) --- +/* fade-transform */ +.fade-transform--move, +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all 0.5s; +} + +.fade-transform-enter-from { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +// --- 面包屑列表项进出场 --- +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all 0.5s; +} + +.breadcrumb-enter-from, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all 0.5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss new file mode 100644 index 0000000..d9af614 --- /dev/null +++ b/src/assets/styles/variables.module.scss @@ -0,0 +1,5 @@ +@forward './tokens/sass-vars'; + +@use './tokens/theme'; +@use './tokens/theme-dark'; +@use './tokens/exports'; diff --git a/src/assets/styles/vendors/element-plus/_card.scss b/src/assets/styles/vendors/element-plus/_card.scss new file mode 100644 index 0000000..f8e2a08 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_card.scss @@ -0,0 +1,13 @@ +.el-card { + overflow: hidden; +} + +.el-card .el-card__header { + border-top-left-radius: var(--app-radius-base); + border-top-right-radius: var(--app-radius-base); +} + +.el-card .el-card__body { + border-bottom-left-radius: var(--app-radius-base); + border-bottom-right-radius: var(--app-radius-base); +} diff --git a/src/assets/styles/vendors/element-plus/_dialog.scss b/src/assets/styles/vendors/element-plus/_dialog.scss new file mode 100644 index 0000000..1767884 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_dialog.scss @@ -0,0 +1,109 @@ +/* 弹窗:遮罩模糊、居中 el-dialog 头身脚与关闭按钮;底部 .upload-container 为上传区宽度占位。 */ + +.el-overlay { + overflow: hidden; + background: var(--app-overlay-mask); + backdrop-filter: blur(10px); + + .el-overlay-dialog { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + padding: 20px; + + .el-dialog { + margin: 0 auto !important; + border-radius: var(--app-radius-base); + box-shadow: var(--app-shadow-md); + overflow: hidden; + border: 1px solid var(--app-surface-border); + background: + linear-gradient(180deg, rgba(255, 255, 255, 0.02), rgba(255, 255, 255, 0)) padding-box, + var(--app-surface-bg); + + .el-dialog__header { + position: relative; + padding: 18px 20px 14px; + box-sizing: border-box; + border-bottom: 1px solid var(--app-surface-border); + margin-right: 0; + background: var(--app-elevated-soft-bg); + } + + .el-dialog__title { + font-size: 16px; + font-weight: 600; + line-height: 1.3; + color: var(--app-text-title); + letter-spacing: 0; + } + + .el-dialog__headerbtn { + position: absolute; + top: 50%; + right: 16px; + display: inline-flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + padding: 0; + margin: 0; + border: 0; + border-radius: 10px; + background: var(--app-elevated-close-bg); + transform: translateY(-50%); + transition: + background-color 0.2s ease, + transform 0.2s ease, + color 0.2s ease; + + .el-dialog__close { + color: var(--app-text-muted); + font-size: 16px; + } + + &:hover { + background: var(--app-elevated-close-hover-bg); + transform: translateY(calc(-50% - 1px)); + + .el-dialog__close { + color: var(--app-accent-strong); + } + } + } + + .el-dialog__body { + padding: 18px 20px 12px !important; + } + + .el-dialog__footer { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 8px; + padding: 14px 20px 20px; + border-top: 1px solid var(--app-surface-border); + } + } + } +} + +.el-dialog__body { + max-height: calc(90vh - 111px) !important; + overflow-y: auto; + overflow-x: hidden; +} + +.el-dialog__footer .el-button { + margin-left: 0 !important; +} + +.el-dialog__footer .dialog-footer { + display: flex; + width: 100%; + justify-content: flex-end; + gap: 12px; +} diff --git a/src/assets/styles/vendors/element-plus/_dropdown.scss b/src/assets/styles/vendors/element-plus/_dropdown.scss new file mode 100644 index 0000000..dc8eff9 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_dropdown.scss @@ -0,0 +1,63 @@ +/* 下拉菜单面板项样式;日期范围分隔符;折叠菜单隐藏箭头;dropdown-link 主色。 */ + +.el-dropdown-menu { + padding: 8px; + border-radius: 14px; + border: 1px solid var(--app-surface-border); + background: var(--app-surface-bg); + box-shadow: var(--app-shadow-md); + + .el-dropdown-menu__item { + min-height: 36px; + margin: 2px 0; + border-radius: 10px; + color: var(--app-text-title); + transition: + background-color 0.2s ease, + color 0.2s ease; + + &:hover, + &:focus { + background: var(--app-accent-soft); + color: var(--app-accent-strong); + } + } + + .el-dropdown-menu__item.is-disabled { + opacity: 0.5; + } + + .el-dropdown-menu__item--divided { + margin-top: 8px; + position: relative; + border-top: none; + + &::before { + left: 2px; + right: 2px; + top: -6px; + width: auto; + background: var(--app-surface-border); + } + } + + a { + display: block; + } +} + +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +.el-range-separator { + box-sizing: content-box; +} + +.el-menu--collapse > div > .el-sub-menu > .el-sub-menu__title .el-sub-menu__icon-arrow { + display: none; +} + +.el-dropdown .el-dropdown-link { + color: var(--el-color-primary) !important; +} diff --git a/src/assets/styles/vendors/element-plus/_element-tokens.scss b/src/assets/styles/vendors/element-plus/_element-tokens.scss new file mode 100644 index 0000000..f694bed --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_element-tokens.scss @@ -0,0 +1,3 @@ +:root { + --el-font-size-base: var(--app-font-size-base); +} diff --git a/src/assets/styles/vendors/element-plus/_feedback.scss b/src/assets/styles/vendors/element-plus/_feedback.scss new file mode 100644 index 0000000..fcc76b9 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_feedback.scss @@ -0,0 +1,226 @@ +/* 反馈:MessageBox 卡片化;Message / Notification / Alert 圆角与暗色下 success 等背景微调。 */ + +.el-message-box .el-message-box__message { + word-break: break-word; +} + +.el-message-box { + width: min(440px, calc(100vw - 32px)); + padding: 0; + overflow: hidden; + border: 1px solid var(--app-surface-border); + background: var(--app-surface-bg); + border-radius: 20px; + box-shadow: var(--app-shadow-md); + + .el-message-box__header { + padding: 18px 20px 0; + } + + .el-message-box__title { + font-size: 17px; + font-weight: 600; + color: var(--app-text-title); + } + + .el-message-box__headerbtn { + top: 14px; + right: 16px; + width: 32px; + height: 32px; + border-radius: 10px; + background: var(--app-elevated-close-bg); + transition: + background-color 0.2s ease, + transform 0.2s ease; + + .el-message-box__close { + color: var(--app-text-muted); + font-size: 16px; + } + + &:hover { + background: var(--app-elevated-close-hover-bg); + transform: translateY(-1px); + + .el-message-box__close { + color: var(--app-accent-strong); + } + } + } + + .el-message-box__content { + padding: 16px 20px 18px; + } + + .el-message-box__container { + align-items: flex-start; + gap: 12px; + } + + .el-message-box__status { + position: static; + margin-top: 1px; + width: 30px; + height: 30px; + border-radius: 10px; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 18px !important; + background: var(--app-elevated-soft-bg); + } + + .el-message-box__message { + color: var(--app-text-title); + font-size: 14px; + line-height: 1.7; + } + + .el-message-box__btns { + display: flex; + justify-content: flex-end; + gap: 8px; + padding: 16px 20px 20px; + border-top: 1px solid var(--app-surface-border); + } + + .el-message-box__btns .el-button { + margin-left: 0; + min-width: 88px; + } + + .el-message-box__input { + padding-top: 2px; + } +} + +.el-message, +.el-notification, +.el-alert { + border-radius: var(--app-radius-md); + box-shadow: var(--app-shadow-sm); +} + +.el-message { + min-width: 320px; + padding: 12px 14px; + border: 1px solid var(--app-surface-border); + background: var(--app-surface-bg); + gap: 10px; + align-items: center; + + .el-message__icon { + font-size: 18px; + } + + .el-message__content { + font-size: 14px; + font-weight: 500; + line-height: 1.5; + color: var(--app-text-title); + } + + .el-message__closeBtn { + color: var(--app-text-muted); + transition: color 0.2s ease; + + &:hover { + color: var(--app-accent-strong); + } + } +} + +.el-message--success { + border-color: var(--el-color-success-light-7); + background: var(--el-color-success-light-9); +} + +.el-message--warning { + border-color: var(--el-color-warning-light-7); + background: var(--el-color-warning-light-9); +} + +.el-message--error { + border-color: var(--el-color-danger-light-7); + background: var(--el-color-danger-light-9); +} + +.el-message--info { + border-color: var(--el-color-primary-light-7); + background: var(--el-color-primary-light-9); +} + +.el-notification { + width: 340px; + padding: 16px 18px 16px 16px; + border: 1px solid var(--app-surface-border); + background: var(--app-surface-bg); + + .el-notification__group { + margin-left: 8px; + } + + .el-notification__title { + font-size: 15px; + font-weight: 600; + color: var(--app-text-title); + } + + .el-notification__content { + margin-top: 6px; + font-size: 13px; + line-height: 1.65; + color: var(--app-text-muted); + } + + .el-notification__closeBtn { + top: 18px; + color: var(--app-text-muted); + transition: color 0.2s ease; + + &:hover { + color: var(--app-accent-strong); + } + } +} + +.el-notification.right, +.el-notification.left { + margin-top: 12px; +} + +.el-notification--success { + border-color: var(--el-color-success-light-7); +} + +.el-notification--warning { + border-color: var(--el-color-warning-light-7); +} + +.el-notification--error { + border-color: var(--el-color-danger-light-7); +} + +.el-notification--info { + border-color: var(--el-color-primary-light-7); +} + +// --- 暗色下 Message 类型背景(与 tokens 中按钮色阶独立) --- +html.dark { + .el-message--success { + background: rgba(31, 138, 90, 0.18); + } + + .el-message--warning { + background: rgba(184, 121, 34, 0.18); + } + + .el-message--error { + background: rgba(178, 74, 74, 0.18); + } + + .el-message--info { + background: rgba(43, 107, 211, 0.18); + } +} diff --git a/src/assets/styles/vendors/element-plus/_form.scss b/src/assets/styles/vendors/element-plus/_form.scss new file mode 100644 index 0000000..e6f8a37 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_form.scss @@ -0,0 +1,73 @@ +/* 表单:inline 默认宽度;输入类圆角与 focus 环;按钮/tag/单选复选按钮组圆角。 */ + +.el-form .el-form-item__label { + font-weight: 1000; +} + +.el-form--inline .el-form-item__label { + width: 68px; +} + +.el-form--inline .el-select { + width: 240px; +} + +.el-form--inline .el-input { + width: 240px; +} + +.el-form--inline .el-date-editor { + width: 308px; +} + +.el-input__wrapper, +.el-textarea__inner, +.el-select__wrapper, +.el-date-editor, +.el-range-editor, +.el-input-number, +.el-input-number__decrease, +.el-input-number__increase { + border-radius: var(--app-radius-md); + transition: + box-shadow 0.2s ease, + border-color 0.2s ease, + background-color 0.2s ease; +} + +.el-input__wrapper.is-focus, +.el-textarea__inner:focus, +.el-select__wrapper.is-focus, +.el-date-editor.is-focus, +.el-range-editor.is-focus { + box-shadow: 0 0 0 2px var(--el-color-primary-light-8); +} + +.el-button { + border-radius: var(--app-radius-md); + transition: + box-shadow 0.2s ease, + background-color 0.2s ease, + border-color 0.2s ease; +} + +.el-button:not(.is-text):not(.is-link):hover { + box-shadow: var(--app-shadow-sm); +} + +.el-tag { + border-radius: var(--app-radius-md); +} + +.el-radio-button__inner, +.el-checkbox-button__inner { + border-radius: var(--app-radius-md) !important; + box-shadow: none !important; +} + +.el-radio-button:first-child .el-radio-button__inner, +.el-checkbox-button:first-child .el-checkbox-button__inner, +.el-radio-button:last-child .el-radio-button__inner, +.el-checkbox-button:last-child .el-checkbox-button__inner { + border-radius: var(--app-radius-md) !important; +} diff --git a/src/assets/styles/vendors/element-plus/_index.scss b/src/assets/styles/vendors/element-plus/_index.scss new file mode 100644 index 0000000..5955cb0 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_index.scss @@ -0,0 +1,12 @@ +@use 'element-plus/dist/index.css'; +@use 'overrides'; +@use './misc'; +@use './dialog'; +@use './dropdown'; +@use './form'; +@use './feedback'; +@use './popper'; +@use './shell'; +@use './navigation'; +@use './table'; +@use './card'; diff --git a/src/assets/styles/vendors/element-plus/_misc.scss b/src/assets/styles/vendors/element-plus/_misc.scss new file mode 100644 index 0000000..17a9a14 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_misc.scss @@ -0,0 +1,82 @@ +/* 零散 EP 覆盖:折叠、面包屑、上传、表格 .cell 与列辅助 class 等;主题级共用壳层见 _overrides。 */ + +// --- 折叠面板标题与内容缩进 --- +.el-collapse { + .collapse__title { + font-weight: 600; + padding: 0 8px; + font-size: 1.2em; + line-height: 1.1em; + } + + .el-collapse-item__content { + padding: 0 8px; + } +} + +// --- 水平分割线上下边距 --- +.el-divider--horizontal { + margin-bottom: 10px; + margin-top: 10px; +} + +// --- 面包屑链接字重 --- +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +// --- 隐藏原生 file input(配合 EP 上传 UI) --- +.el-upload { + input[type='file'] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +// --- 表格默认 .cell 内 tag 去右间距 --- +.cell { + .el-tag { + margin-right: 0px; + } +} + +// --- 列 class:窄内边距 / 固定宽操作列 / 状态列居中 --- +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} diff --git a/src/assets/styles/vendors/element-plus/_navigation.scss b/src/assets/styles/vendors/element-plus/_navigation.scss new file mode 100644 index 0000000..5074036 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_navigation.scss @@ -0,0 +1,56 @@ +/* 导航类组件:Tabs、Pagination、面包屑容 */ + +.el-tabs__header { + margin: 0 0 12px 0; +} + +.el-tabs__item { + border-radius: var(--app-radius-md); + margin: 0 2px; +} + +.el-tabs__nav-wrap::after { + background-color: var(--el-border-color-lighter); +} + +.el-pagination .btn-prev, +.el-pagination .btn-next, +.el-pagination .el-pager li { + border-radius: var(--app-radius-sm); + transition: + background-color 0.2s ease, + color 0.2s ease, + box-shadow 0.2s ease; +} + +.el-pagination { + gap: 6px; + flex-wrap: wrap; + color: var(--app-text-muted); +} + +.el-pagination .el-pagination__total, +.el-pagination .el-pagination__jump { + color: var(--app-text-muted); +} + +.el-pagination .btn-prev, +.el-pagination .btn-next, +.el-pagination .el-pager li, +.el-pagination .el-pagination__sizes .el-input__wrapper { + min-width: 32px; + height: 32px; + border: 1px solid var(--app-surface-border); + background: var(--app-surface-bg); +} + +.el-pagination .el-pager li.is-active { + box-shadow: var(--app-shadow-sm); +} + +.el-breadcrumb { + padding: 0; + background: transparent; + border: none; + border-radius: 0; +} diff --git a/src/assets/styles/vendors/element-plus/_overrides.scss b/src/assets/styles/vendors/element-plus/_overrides.scss new file mode 100644 index 0000000..41c8d81 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_overrides.scss @@ -0,0 +1,84 @@ +/* 跨多个 Element Plus 组件的共用覆盖(圆角、描边、阴影、主按钮、表格变量、输入框等)。 + * 注意:本文件在入口中晚于 components/_card-shell 载入,其中 .el-card 等与卡片壳层重叠的属性以本文件为准(例如阴影强度)。 */ + +// --- 卡片、弹层、抽屉及常见 popper 容器的圆角/边线/阴影 --- +.el-card, +.el-dialog, +.el-drawer, +.el-popover.el-popper, +.el-dropdown__popper .el-dropdown-menu, +.el-picker__popper, +.el-select__popper.el-popper { + border-radius: var(--app-radius-base); + border-color: var(--app-surface-border); + box-shadow: var(--app-shadow-md); +} + +// --- 实体「面板」背景 --- +.el-card, +.el-dialog, +.el-drawer { + background: var(--app-surface-bg); +} + +// --- 按钮默认字重 --- +.el-button { + font-weight: 500; +} + +// --- primary 品牌色(亮色;暗色见 tokens/_theme-dark) --- +.el-button--primary { + --el-button-bg-color: var(--app-accent-strong); + --el-button-border-color: var(--app-accent-strong); + --el-button-hover-bg-color: #4a7cff; + --el-button-hover-border-color: #4a7cff; + --el-button-active-bg-color: #2e5ce0; + --el-button-active-border-color: #2e5ce0; +} + +// --- 表格 CSS 变量接业务 token --- +.el-table { + --el-table-border-color: var(--app-surface-border); + --el-table-header-bg-color: var(--tableHeaderBg); + --el-table-tr-bg-color: var(--el-bg-color); + --el-table-bg-color: var(--el-bg-color); + --el-table-row-hover-bg-color: var(--el-fill-color-light); + --el-table-current-row-bg-color: var(--el-color-primary-light-9); + background: var(--el-bg-color); +} + +// 暗色模式:行背景、hover、选中全部用不透明色,防止固定列透视 +html.dark .el-table { + --el-table-tr-bg-color: var(--app-surface-bg); + --el-table-bg-color: var(--app-surface-bg); + --el-table-row-hover-bg-color: #2a3a55; + --el-table-current-row-bg-color: #1e3a6e; + --el-color-primary-light-9: #1e3a6e; + background: var(--app-surface-bg); +} + +// 固定列单元格基底背景与普通行保持一致(亮/暗均适用) +.el-table .el-table__cell { + background-color: var(--el-table-tr-bg-color); +} + +// hover 行的所有单元格(含固定列)覆写为不透明背景 +.el-table__body tr.hover-row > td.el-table__cell, +.el-table__body tr.hover-row.current-row > td.el-table__cell, +.el-table__body tr.hover-row.el-table__row--striped > td.el-table__cell, +.el-table__body tr.hover-row.el-table__row--striped.current-row > td.el-table__cell, +.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell { + background-color: var(--el-table-row-hover-bg-color) !important; +} + +// 选中行的所有单元格(含固定列)覆写为不透明背景 +.el-table__body tr.current-row > td.el-table__cell { + background-color: var(--el-table-current-row-bg-color) !important; +} + +// --- 输入类默认内描边(与 _form 中 focus 环配合) --- +.el-input__wrapper, +.el-textarea__inner, +.el-select__wrapper { + box-shadow: 0 0 0 1px var(--el-border-color) inset; +} diff --git a/src/assets/styles/vendors/element-plus/_popper.scss b/src/assets/styles/vendors/element-plus/_popper.scss new file mode 100644 index 0000000..070bd36 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_popper.scss @@ -0,0 +1,248 @@ +/* 各类浮层:Select/Cascader/DatePicker/Tooltip/Popover 等边框、列表项 hover、暗色箭头与日历格选中态。 */ + +.el-select__popper.el-popper, +.el-picker__popper.el-popper, +.el-autocomplete__popper.el-popper, +.el-cascader__dropdown, +.el-dropdown__popper.el-popper, +.el-popover.el-popper { + border: 1px solid var(--app-surface-border); + background: var(--app-surface-bg); + box-shadow: var(--app-shadow-md); +} + +.el-select__popper.el-popper, +.el-autocomplete__popper.el-popper, +.el-cascader__dropdown, +.el-dropdown__popper.el-popper, +.el-popover.el-popper { + padding: 8px; +} + +.el-select-dropdown__list, +.el-autocomplete-suggestion__list, +.el-cascader-menu { + padding: 0; +} + +.el-select-dropdown__item, +.el-autocomplete-suggestion li, +.el-cascader-node { + min-height: 38px; + margin: 2px 0; + border-radius: 10px; + color: var(--app-text-title); + line-height: 38px; + transition: + background-color 0.2s ease, + color 0.2s ease; +} + +.el-select-dropdown__item.hover, +.el-select-dropdown__item:hover, +.el-autocomplete-suggestion li:hover, +.el-cascader-node:not(.is-disabled):hover, +.el-cascader-node.in-active-path { + background: var(--app-accent-soft); + color: var(--app-accent-strong); +} + +.el-select-dropdown__item.is-selected, +.el-autocomplete-suggestion li.highlighted { + background: rgba(64, 158, 255, 0.12); + color: var(--app-accent-strong); + font-weight: 600; +} + +.el-select-dropdown__item.is-disabled, +.el-cascader-node.is-disabled { + opacity: 0.5; +} + +.el-tree-select__popper .el-tree-node__content { + display: flex; + align-items: center; +} + +.el-tree-select__popper .el-select-dropdown__item { + display: flex; + align-items: center; + flex: 1; + min-height: 20px; + height: 20px; + line-height: 20px; + margin: 0; + padding-left: 0; + background: transparent !important; +} + +.el-tree-select__popper .el-tree-node__expand-icon { + display: inline-flex; + align-items: center; + justify-content: center; + align-self: center; +} + +.el-autocomplete-suggestion li { + display: flex; + align-items: center; + padding: 0 12px; +} + +.el-autocomplete-suggestion li div { + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.el-cascader-menu { + min-width: 180px; + border-right-color: var(--app-surface-border); +} + +.el-cascader-node__prefix, +.el-cascader-node__postfix { + color: var(--app-text-muted); +} + +.el-popper.is-dark { + border-color: rgba(15, 23, 42, 0.72); + background: rgba(15, 23, 42, 0.92); + color: #f8fafc; +} + +.el-popper.is-dark .el-popper__arrow::before { + background: rgba(15, 23, 42, 0.92); + border-color: rgba(15, 23, 42, 0.72); +} + +.el-popper.is-light { + border-color: var(--app-surface-border); + background: var(--app-surface-bg); +} + +.el-popper.is-light .el-popper__arrow::before { + background: var(--app-surface-bg); + border-color: var(--app-surface-border); +} + +.el-picker__popper.el-popper { + padding: 12px; +} + +.el-date-picker, +.el-date-range-picker, +.el-time-panel, +.el-time-range-picker, +.el-picker-panel { + background: transparent; + color: var(--app-text-title); +} + +.el-picker-panel__body-wrapper, +.el-date-range-picker__content { + background: transparent; +} + +.el-picker-panel__icon-btn, +.el-date-picker__header-label, +.el-date-range-picker__header-label { + color: var(--app-text-title); +} + +.el-date-table td, +.el-month-table td .cell, +.el-year-table td .cell { + border-radius: 10px; +} + +.el-date-table td.today .el-date-table-cell__text, +.el-month-table td.today .cell, +.el-year-table td.today .cell { + color: var(--app-accent-strong); + font-weight: 700; +} + +.el-date-table td.available:hover, +.el-month-table td .cell:hover, +.el-year-table td .cell:hover { + color: var(--app-accent-strong); + background: var(--app-accent-soft); +} + +.el-date-table td.current:not(.disabled) .el-date-table-cell__text, +.el-month-table td.current:not(.disabled) .cell, +.el-year-table td.current:not(.disabled) .cell { + background: var(--app-accent-strong); + color: #fff; +} + +.el-date-editor .el-range-separator { + color: var(--app-text-muted); +} + +.el-time-panel__content::before, +.el-time-panel__content::after { + border-color: var(--app-surface-border); +} + +.el-scrollbar__thumb { + background: var(--app-text-muted); + opacity: 0.42; +} + +.el-scrollbar__thumb:hover { + background: rgba(100, 116, 139, 0.56); +} + +.el-popover, +.el-tooltip__popper, +.el-popper { + border-radius: var(--app-radius-md); + box-shadow: var(--app-shadow-sm); +} + +.el-popover { + padding: 10px 12px; +} + +.el-tooltip__popper { + max-width: 320px; + padding: 8px 10px; + font-size: 12px; + line-height: 1.5; +} + +.el-popconfirm { + max-width: 320px; +} + +.el-popconfirm__main { + gap: 10px; + line-height: 1.6; + color: var(--app-text-title); +} + +.el-popconfirm__action { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 14px; +} + +.el-popconfirm__action .el-button { + margin-left: 0 !important; +} + +html.dark { + .el-popper.is-dark { + border-color: rgba(30, 41, 59, 0.92); + background: rgba(15, 23, 42, 0.96); + } + + .el-popper.is-dark .el-popper__arrow::before { + background: rgba(15, 23, 42, 0.96); + border-color: rgba(30, 41, 59, 0.92); + } +} diff --git a/src/assets/styles/vendors/element-plus/_shell.scss b/src/assets/styles/vendors/element-plus/_shell.scss new file mode 100644 index 0000000..663d2fe --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_shell.scss @@ -0,0 +1,108 @@ +/* Drawers, loading masks, descriptions, trees, and empty states. */ + +.el-drawer { + border-radius: var(--app-radius-base); + box-shadow: var(--app-shadow-md); + border: 1px solid var(--app-surface-border); + overflow: hidden; +} + +.el-drawer__header { + margin-bottom: 0; + padding: 18px 20px 16px; + border-bottom: 1px solid var(--app-surface-border); + background: var(--app-elevated-soft-bg); + color: var(--app-text-title); +} + +.el-drawer__title { + font-size: 16px; + font-weight: 600; + color: var(--app-text-title); +} + +.el-drawer__body { + padding: 18px 20px 20px; +} + +.el-drawer__close-btn { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 10px; + background: var(--app-elevated-close-bg); + color: var(--app-text-muted); + transition: + background-color 0.2s ease, + color 0.2s ease, + transform 0.2s ease; + + &:hover { + background: var(--app-elevated-close-hover-bg); + color: var(--app-accent-strong); + transform: translateY(-1px); + } +} + +.el-loading-mask { + background: color-mix(in srgb, var(--app-surface-bg) 82%, transparent); + backdrop-filter: blur(4px); +} + +.el-loading-spinner .path { + stroke: var(--app-accent-strong); +} + +.el-descriptions { + --el-descriptions-table-border: var(--app-surface-border); +} + +.el-descriptions__body .el-descriptions__table { + border-radius: 12px; + overflow: hidden; +} + +.el-descriptions__label.el-descriptions__cell { + background: var(--app-elevated-soft-bg) !important; + color: var(--app-text-title); + font-weight: 600; +} + +.el-descriptions__content.el-descriptions__cell { + color: var(--app-text-title); +} + +.el-tree { + color: var(--app-text-title); + background: transparent; +} + +.el-tree-node__content { + min-height: 34px; + margin: 2px 0; + border-radius: 10px; + transition: + background-color 0.2s ease, + color 0.2s ease; +} + +.el-tree-node__content:hover, +.el-tree-node.is-current > .el-tree-node__content { + background: var(--app-accent-soft); +} + +.el-empty__image { + opacity: 0.9; +} + +.el-empty__description p { + color: var(--app-text-muted); +} + +.el-color-dropdown { + border-radius: var(--app-radius-base); + border: 1px solid var(--app-surface-border); + box-shadow: var(--app-shadow-md); +} diff --git a/src/assets/styles/vendors/element-plus/_table.scss b/src/assets/styles/vendors/element-plus/_table.scss new file mode 100644 index 0000000..c2e05c9 --- /dev/null +++ b/src/assets/styles/vendors/element-plus/_table.scss @@ -0,0 +1,100 @@ +/* Element Plus 表格:通用容器圆角、边线、overflow、行 hover、四角单元格、border/fit 清理(不含业务页表头 token,表头见 components/_legacy-utilities)。 */ +@use '../../tokens/sass-vars' as *; + +.el-table { + border-radius: var(--app-radius-md); + border: 1px solid var(--app-surface-border); + overflow: hidden; + + // 给右上角表头单元格增加右上角圆角 + thead tr:first-child th:last-child { + border-top-right-radius: var(--app-radius-md); + } + + // 给左下角单元格增加左下角圆角 + tbody tr:last-child td:first-child { + border-bottom-left-radius: var(--app-radius-md); + } + + // 给右下角单元格增加右下角圆角 + tbody tr:last-child td:last-child { + border-bottom-right-radius: var(--app-radius-md); + } +} + +.el-table__header-wrapper { + background: transparent; +} + +// 固定列 body 必须有不透明背景,否则横向滚动时数据透视 +.el-table__fixed-body-wrapper, +.el-table__fixed-right-body-wrapper { + background-color: var(--el-bg-color); +} + +html.dark { + .el-table__fixed-body-wrapper, + .el-table__fixed-right-body-wrapper { + background-color: var(--app-surface-bg); + } +} + +// 去掉额外的表格边线,使用表格border +.el-table--border:after, +.el-table--border:before, +.el-table--border .el-table__inner-wrapper:after, +.el-table--border .el-table__inner-wrapper:before, +.el-table__border-left-patch { + display: none; +} + +// 去掉表格右侧和底部边框,使用单元的变宽 +.el-table--fit { + border-bottom: 0; + border-right: 0; +} + +// 全高表格的外壳会延伸到分页区域,左边框改由实际表头/数据行首列绘制,避免少量数据时边框悬空。 +.el-table.is-full-height-table.el-table--border { + border-left: 0; + + > .el-table__inner-wrapper > .el-table__header-wrapper, + > .el-table__inner-wrapper > .el-table__body-wrapper, + > .el-table__inner-wrapper > .el-table__footer-wrapper { + tr > .el-table__cell:first-child { + border-left: var(--el-table-border); + } + } +} + +// --- 表格内操作列:固定宽按钮、下拉链接色 --- +.el-table .fixed-width .el-button--small { + padding-left: 0; + padding-right: 0; + width: inherit; +} + +.el-table .el-dropdown-link { + cursor: pointer; + color: var(--el-color-primary); + margin-left: 10px; +} + +.el-table { + .el-table__header-wrapper, + .el-table__fixed-header-wrapper { + th { + word-break: break-word; + background-color: $table-header-bg !important; + color: $table-header-text-color; + height: 40px !important; + font-size: 13px; + } + } + + .el-table__body-wrapper { + .el-button [class*='el-icon-'] + span { + margin-left: 1px; + } + } +} diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..791ae5f --- /dev/null +++ b/src/components/Breadcrumb/index.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/src/components/DictTag/index.vue b/src/components/DictTag/index.vue new file mode 100644 index 0000000..6e705a2 --- /dev/null +++ b/src/components/DictTag/index.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue new file mode 100644 index 0000000..d9c2371 --- /dev/null +++ b/src/components/Editor/index.vue @@ -0,0 +1,345 @@ + + + + + diff --git a/src/components/FileUpload/index.vue b/src/components/FileUpload/index.vue new file mode 100644 index 0000000..9b5393e --- /dev/null +++ b/src/components/FileUpload/index.vue @@ -0,0 +1,261 @@ + + + + + diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue new file mode 100644 index 0000000..9cda5f0 --- /dev/null +++ b/src/components/Hamburger/index.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue new file mode 100644 index 0000000..d676a6c --- /dev/null +++ b/src/components/IconSelect/index.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/src/components/IconSelect/requireIcons.ts b/src/components/IconSelect/requireIcons.ts new file mode 100644 index 0000000..9f06d69 --- /dev/null +++ b/src/components/IconSelect/requireIcons.ts @@ -0,0 +1,7 @@ +const icons: string[] = []; +const modules = import.meta.glob('./../../assets/icons/svg/*.svg'); +for (const path in modules) { + const p = path.split('assets/icons/svg/')[1].split('.svg')[0]; + icons.push(p); +} +export default icons; diff --git a/src/components/ImagePreview/index.vue b/src/components/ImagePreview/index.vue new file mode 100644 index 0000000..9beada2 --- /dev/null +++ b/src/components/ImagePreview/index.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/src/components/ImageUpload/index.vue b/src/components/ImageUpload/index.vue new file mode 100644 index 0000000..1df5a5a --- /dev/null +++ b/src/components/ImageUpload/index.vue @@ -0,0 +1,262 @@ + + + + + diff --git a/src/components/LangSelect/index.vue b/src/components/LangSelect/index.vue new file mode 100644 index 0000000..6245bdc --- /dev/null +++ b/src/components/LangSelect/index.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue new file mode 100644 index 0000000..f4e7f42 --- /dev/null +++ b/src/components/Pagination/index.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/src/components/ParentView/index.vue b/src/components/ParentView/index.vue new file mode 100644 index 0000000..98240ae --- /dev/null +++ b/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ + diff --git a/src/components/Process/MessageType.vue b/src/components/Process/MessageType.vue new file mode 100644 index 0000000..6e4d18e --- /dev/null +++ b/src/components/Process/MessageType.vue @@ -0,0 +1,107 @@ + + diff --git a/src/components/Process/UserNameDisplay.vue b/src/components/Process/UserNameDisplay.vue new file mode 100644 index 0000000..3791d2e --- /dev/null +++ b/src/components/Process/UserNameDisplay.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/components/Process/approvalButton.vue b/src/components/Process/approvalButton.vue new file mode 100644 index 0000000..a2221c4 --- /dev/null +++ b/src/components/Process/approvalButton.vue @@ -0,0 +1,76 @@ + + diff --git a/src/components/Process/approvalRecord.vue b/src/components/Process/approvalRecord.vue new file mode 100644 index 0000000..b420906 --- /dev/null +++ b/src/components/Process/approvalRecord.vue @@ -0,0 +1,164 @@ + + + diff --git a/src/components/Process/flowChart.vue b/src/components/Process/flowChart.vue new file mode 100644 index 0000000..7603375 --- /dev/null +++ b/src/components/Process/flowChart.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue new file mode 100644 index 0000000..35eaa12 --- /dev/null +++ b/src/layout/components/Navbar.vue @@ -0,0 +1,477 @@ + + + + + diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..b7d2366 --- /dev/null +++ b/src/layout/components/Settings/index.vue @@ -0,0 +1,451 @@ + + + + + diff --git a/src/layout/components/Sidebar/Link.vue b/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..fd75f35 --- /dev/null +++ b/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,40 @@ + + + diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..3f9f3ad --- /dev/null +++ b/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..3647e8d --- /dev/null +++ b/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,122 @@ + + + diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..4832734 --- /dev/null +++ b/src/layout/components/Sidebar/index.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/src/layout/components/SocialCallback/index.vue b/src/layout/components/SocialCallback/index.vue new file mode 100644 index 0000000..2d6653f --- /dev/null +++ b/src/layout/components/SocialCallback/index.vue @@ -0,0 +1,82 @@ + + + diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..da1bf70 --- /dev/null +++ b/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,148 @@ + + + + + diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..d687b0c --- /dev/null +++ b/src/layout/components/TagsView/index.vue @@ -0,0 +1,740 @@ + + + + + + + diff --git a/src/layout/components/TopBar/index.vue b/src/layout/components/TopBar/index.vue new file mode 100644 index 0000000..5783d84 --- /dev/null +++ b/src/layout/components/TopBar/index.vue @@ -0,0 +1,319 @@ + + + + + diff --git a/src/layout/components/TopBar/search.vue b/src/layout/components/TopBar/search.vue new file mode 100644 index 0000000..c035219 --- /dev/null +++ b/src/layout/components/TopBar/search.vue @@ -0,0 +1,473 @@ + + + + + diff --git a/src/layout/components/index.ts b/src/layout/components/index.ts new file mode 100644 index 0000000..47c83e1 --- /dev/null +++ b/src/layout/components/index.ts @@ -0,0 +1,4 @@ +export { default as AppMain } from './AppMain.vue'; +export { default as Navbar } from './Navbar.vue'; +export { default as Settings } from './Settings/index.vue'; +export { default as TagsView } from './TagsView/index.vue'; diff --git a/src/layout/components/notice/index.vue b/src/layout/components/notice/index.vue new file mode 100644 index 0000000..50fd262 --- /dev/null +++ b/src/layout/components/notice/index.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/src/layout/index.vue b/src/layout/index.vue new file mode 100644 index 0000000..f326e00 --- /dev/null +++ b/src/layout/index.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..e4d16c1 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,41 @@ +import HighLight from '@highlightjs/vue-plugin'; +import { ElDialog } from 'element-plus'; +import { createApp } from 'vue'; +import VxeUIPlugin, { VxeUI } from 'vxe-pc-ui'; +import VxeTablePlugin from 'vxe-table'; +import 'virtual:uno.css'; +import 'element-plus/theme-chalk/dark/css-vars.css'; +import '@/assets/styles/index.scss'; +import 'highlight.js/styles/atom-one-dark.css'; +import 'highlight.js/lib/common'; +import 'virtual:svg-icons-register'; +import 'vxe-pc-ui/lib/style.css'; +import 'vxe-table/lib/style.css'; +import i18n from '@/lang/index'; +import ElementIcons from '@/plugins/svgicon'; +import App from './App.vue'; +import directive from './directive'; +import plugins from './plugins/index'; +import './permission'; +import router from './router'; +import store from './store'; + +VxeUI.setConfig({ + zIndex: 999999 +}); + +ElDialog.props.closeOnClickModal.default = false; + +const app = createApp(App); + +app.use(HighLight); +app.use(ElementIcons); +app.use(store); +app.use(router); +app.use(i18n); +app.use(VxeUIPlugin); +app.use(VxeTablePlugin); +app.use(plugins); +directive(app); + +app.mount('#app'); diff --git a/src/permission.ts b/src/permission.ts new file mode 100644 index 0000000..9b19c2a --- /dev/null +++ b/src/permission.ts @@ -0,0 +1,81 @@ +import { to as tos } from 'await-to-js'; +import { ElMessage } from 'element-plus/es'; +import * as NProgressModule from 'nprogress'; +import 'nprogress/nprogress.css'; +import { usePermissionStore } from '@/store/modules/permission'; +import { useSettingsStore } from '@/store/modules/settings'; +import { useUserStore } from '@/store/modules/user'; +import { getToken } from '@/utils/auth'; +import { isHandledRequestError, isRelogin } from '@/utils/request'; +import { isHttp, isPathMatch } from '@/utils/validate'; +import router from './router'; + +const NProgress = ('default' in NProgressModule ? NProgressModule.default : NProgressModule) as typeof NProgressModule; + +NProgress.configure({ showSpinner: false }); +const whiteList = ['/login', '/register', '/social-callback', '/register*', '/register/*']; + +const isWhiteList = (path: string) => { + return whiteList.some(pattern => isPathMatch(pattern, path)); +}; + +router.beforeEach(async (to, from) => { + NProgress.start(); + if (getToken()) { + to.meta.title && useSettingsStore().setTitle(to.meta.title as string); + /* has token*/ + if (to.path === '/login') { + NProgress.done(); + return { path: '/' }; + } else if (isWhiteList(to.path)) { + return true; + } else { + if (useUserStore().roles.length === 0) { + isRelogin.show = true; + // 判断当前用户是否已拉取完user_info信息 + const [err] = await tos(useUserStore().getInfo()); + if (err) { + await useUserStore().logout(); + if (!isHandledRequestError(err)) { + ElMessage.error(err instanceof Error ? err.message : String(err)); + } + return { path: '/' }; + } else { + isRelogin.show = false; + const accessRoutes = await usePermissionStore().generateRoutes(); + // 根据roles权限生成可访问的路由表 + accessRoutes.forEach(route => { + if (!isHttp(route.path)) { + router.addRoute(route); // 动态添加可访问路由表 + } + }); + // hack方法 确保addRoutes已完成 + return { + path: to.path, + replace: true, + params: to.params, + query: to.query, + hash: to.hash, + name: to.name as string + }; + } + } else { + return true; + } + } + } else { + // 没有token + if (isWhiteList(to.path)) { + // 在免登录白名单,直接进入 + return true; + } else { + const redirect = encodeURIComponent(to.fullPath || '/'); + NProgress.done(); + return `/login?redirect=${redirect}`; // 否则全部重定向到登录页 + } + } +}); + +router.afterEach(() => { + NProgress.done(); +}); diff --git a/src/plugins/auth.ts b/src/plugins/auth.ts new file mode 100644 index 0000000..1ef2428 --- /dev/null +++ b/src/plugins/auth.ts @@ -0,0 +1,60 @@ +import { useUserStore } from '@/store/modules/user'; + +const authPermission = (permission: string): boolean => { + const all_permission = '*:*:*'; + const permissions: string[] = useUserStore().permissions; + if (permission && permission.length > 0) { + return permissions.some(v => { + return all_permission === v || v === permission; + }); + } else { + return false; + } +}; + +const authRole = (role: string): boolean => { + const super_admin = 'admin'; + const roles = useUserStore().roles; + if (role && role.length > 0) { + return roles.some(v => { + return super_admin === v || v === role; + }); + } else { + return false; + } +}; + +export default { + // 验证用户是否具备某权限 + hasPermi(permission: string): boolean { + return authPermission(permission); + }, + // 验证用户是否含有指定权限,只需包含其中一个 + hasPermiOr(permissions: string[]): boolean { + return permissions.some(item => { + return authPermission(item); + }); + }, + // 验证用户是否含有指定权限,必须全部拥有 + hasPermiAnd(permissions: string[]): boolean { + return permissions.every(item => { + return authPermission(item); + }); + }, + // 验证用户是否具备某角色 + hasRole(role: string): boolean { + return authRole(role); + }, + // 验证用户是否含有指定角色,只需包含其中一个 + hasRoleOr(roles: string[]): boolean { + return roles.some(item => { + return authRole(item); + }); + }, + // 验证用户是否含有指定角色,必须全部拥有 + hasRoleAnd(roles: string[]): boolean { + return roles.every(item => { + return authRole(item); + }); + } +}; diff --git a/src/plugins/cache.ts b/src/plugins/cache.ts new file mode 100644 index 0000000..e5ceead --- /dev/null +++ b/src/plugins/cache.ts @@ -0,0 +1,79 @@ +const sessionCache = { + set(key: string, value: any) { + if (!sessionStorage) { + return; + } + if (key != null && value != null) { + sessionStorage.setItem(key, value); + } + }, + get(key: string) { + if (!sessionStorage) { + return null; + } + if (key == null) { + return null; + } + return sessionStorage.getItem(key); + }, + setJSON(key: string, jsonValue: any) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)); + } + }, + getJSON(key: string) { + const value = this.get(key); + if (value != null) { + return JSON.parse(value); + } + return null; + }, + remove(key: string) { + sessionStorage.removeItem(key); + } +}; +const localCache = { + set(key: string, value: any) { + if (!localStorage) { + return; + } + if (key != null && value != null) { + localStorage.setItem(key, value); + } + }, + get(key: string) { + if (!localStorage) { + return null; + } + if (key == null) { + return null; + } + return localStorage.getItem(key); + }, + setJSON(key: string, jsonValue: any) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)); + } + }, + getJSON(key: string) { + const value = this.get(key); + if (value != null) { + return JSON.parse(value); + } + return null; + }, + remove(key: string) { + localStorage.removeItem(key); + } +}; + +export default { + /** + * 会话级缓存 + */ + session: sessionCache, + /** + * 本地缓存 + */ + local: localCache +}; diff --git a/src/plugins/download.ts b/src/plugins/download.ts new file mode 100644 index 0000000..099593a --- /dev/null +++ b/src/plugins/download.ts @@ -0,0 +1,74 @@ +import type { LoadingInstance } from 'element-plus'; +import axiosModule from 'axios'; +import errorCode from '@/utils/errorCode'; +import { extractErrorMessage, globalHeaders } from '@/utils/request'; +import { blobValidate } from '@/utils/ruoyi'; +import { saveBlob } from '@/utils/save'; + +const axios = axiosModule as any; +const baseURL = import.meta.env.VITE_APP_BASE_API; +let downloadLoadingInstance: LoadingInstance | undefined; +export default { + async oss(ossId: string | number) { + const url = baseURL + '/resource/oss/download/' + ossId; + downloadLoadingInstance = ElLoading.service({ + text: '正在下载数据,请稍候', + background: 'rgba(0, 0, 0, 0.7)' + }); + try { + const res = await axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: globalHeaders() + }); + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/octet-stream' }); + saveBlob(blob, decodeURIComponent(res.headers['download-filename'] as string)); + } else { + this.printErrMsg(res.data); + } + downloadLoadingInstance?.close(); + } catch (r) { + console.error(r); + const errMsg = await extractErrorMessage(r); + ElMessage.error(errMsg || '下载文件出现错误,请联系管理员!'); + downloadLoadingInstance?.close(); + } + }, + async zip(url: string, name: string) { + url = baseURL + url; + downloadLoadingInstance = ElLoading.service({ + text: '正在下载数据,请稍候', + background: 'rgba(0, 0, 0, 0.7)' + }); + try { + const res = await axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: globalHeaders() + }); + const isBlob = blobValidate(res.data); + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/zip' }); + saveBlob(blob, name); + } else { + this.printErrMsg(res.data); + } + downloadLoadingInstance?.close(); + } catch (r) { + console.error(r); + const errMsg = await extractErrorMessage(r); + ElMessage.error(errMsg || '下载文件出现错误,请联系管理员!'); + downloadLoadingInstance?.close(); + } + }, + async printErrMsg(data: any) { + const resText = await data.text(); + const rspObj = JSON.parse(resText); + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']; + ElMessage.error(errMsg); + } +}; diff --git a/src/plugins/index.ts b/src/plugins/index.ts new file mode 100644 index 0000000..cd61fb0 --- /dev/null +++ b/src/plugins/index.ts @@ -0,0 +1,18 @@ +import type { App } from 'vue'; +import auth from './auth'; +import cache from './cache'; +import download from './download'; +import modal from './modal'; +import tab from './tab'; + +/** + * 仅挂载无法在组合式中通过 import 替代的实例级能力(与业务模块解耦的壳层 API)。 + * 字典、时间、树、请求下载等请在 setup 中从 @/utils、@/api 显式导入。 + */ +export default function installPlugin(app: App) { + app.config.globalProperties.$tab = tab; + app.config.globalProperties.$modal = modal; + app.config.globalProperties.$cache = cache; + app.config.globalProperties.$download = download; + app.config.globalProperties.$auth = auth; +} diff --git a/src/plugins/modal.ts b/src/plugins/modal.ts new file mode 100644 index 0000000..546c8d4 --- /dev/null +++ b/src/plugins/modal.ts @@ -0,0 +1,81 @@ +import type { LoadingInstance, MessageBoxData } from 'element-plus'; + +let loadingInstance: LoadingInstance | undefined; +export default { + // 消息提示 + msg(content: any) { + ElMessage.info(content); + }, + // 错误消息 + msgError(content: any) { + ElMessage.error(content); + }, + // 成功消息 + msgSuccess(content: any) { + ElMessage.success(content); + }, + // 警告消息 + msgWarning(content: any) { + ElMessage.warning(content); + }, + // 弹出提示 + alert(content: any) { + ElMessageBox.alert(content, '系统提示'); + }, + // 错误提示 + alertError(content: any) { + ElMessageBox.alert(content, '系统提示', { type: 'error' }); + }, + // 成功提示 + alertSuccess(content: any) { + ElMessageBox.alert(content, '系统提示', { type: 'success' }); + }, + // 警告提示 + alertWarning(content: any) { + ElMessageBox.alert(content, '系统提示', { type: 'warning' }); + }, + // 通知提示 + notify(content: any) { + ElNotification.info(content); + }, + // 错误通知 + notifyError(content: any) { + ElNotification.error(content); + }, + // 成功通知 + notifySuccess(content: any) { + ElNotification.success(content); + }, + // 警告通知 + notifyWarning(content: any) { + ElNotification.warning(content); + }, + // 确认窗体 + confirm(content: any): Promise { + return ElMessageBox.confirm(content, '系统提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }); + }, + // 提交内容 + prompt(content: any) { + return ElMessageBox.prompt(content, '系统提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }); + }, + // 打开遮罩层 + loading(content: string) { + loadingInstance = ElLoading.service({ + lock: true, + text: content, + background: 'var(--app-overlay-mask)' + }); + }, + // 关闭遮罩层 + closeLoading() { + loadingInstance?.close(); + } +}; diff --git a/src/plugins/svgicon.ts b/src/plugins/svgicon.ts new file mode 100644 index 0000000..f2ce685 --- /dev/null +++ b/src/plugins/svgicon.ts @@ -0,0 +1,10 @@ +import type { App } from 'vue'; +import * as ElementPlusIconsVue from '@element-plus/icons-vue'; + +export default { + install: (app: App) => { + for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component); + } + } +}; diff --git a/src/plugins/tab.ts b/src/plugins/tab.ts new file mode 100644 index 0000000..cf97272 --- /dev/null +++ b/src/plugins/tab.ts @@ -0,0 +1,104 @@ +import type { RouteLocationMatched, RouteLocationNormalized, RouteLocationRaw } from 'vue-router'; +import router from '@/router'; +import { useTagsViewStore } from '@/store/modules/tagsView'; + +export default { + /** + * 刷新当前tab页签 + * @param obj 标签对象 + */ + async refreshPage(obj?: RouteLocationNormalized): Promise { + const { path, query, matched } = router.currentRoute.value; + // 防止在重定向过程中重复刷新 + if (path.startsWith('/redirect/')) { + return Promise.resolve(); + } + if (obj === undefined) { + matched.forEach((m: RouteLocationMatched) => { + if (m.components && m.components.default && m.components.default.name) { + if (!['Layout', 'ParentView'].includes(m.components.default.name)) { + obj = { + name: m.components.default.name, + path: path, + query: query, + matched: undefined, + fullPath: undefined, + hash: undefined, + params: undefined, + redirectedFrom: undefined, + meta: undefined + }; + } + } + }); + } + let query1: undefined | {} = {}; + let path1: undefined | string = ''; + if (obj) { + query1 = obj.query; + path1 = obj.path; + } + await useTagsViewStore().delCachedView(obj); + await router.replace({ + path: '/redirect' + path1, + query: query1 + }); + }, + // 关闭当前tab页签,打开新页签 + closeOpenPage(obj: RouteLocationRaw): void { + useTagsViewStore().delView(router.currentRoute.value); + if (obj !== undefined) { + router.push(obj); + } + }, + // 关闭指定tab页签 + async closePage( + obj?: RouteLocationNormalized + ): Promise<{ visitedViews: RouteLocationNormalized[]; cachedViews: string[] } | any> { + if (obj === undefined) { + // prettier-ignore + const { visitedViews } = await useTagsViewStore().delView( + router.currentRoute.value, + ); + const latestView = visitedViews.slice(-1)[0]; + if (latestView) { + return router.push(latestView.fullPath); + } + return router.push('/'); + } + return useTagsViewStore().delView(obj); + }, + // 关闭所有tab页签 + closeAllPage() { + return useTagsViewStore().delAllViews(); + }, + // 关闭左侧tab页签 + closeLeftPage(obj?: RouteLocationNormalized) { + return useTagsViewStore().delLeftTags(obj || router.currentRoute.value); + }, + // 关闭右侧tab页签 + closeRightPage(obj?: RouteLocationNormalized) { + return useTagsViewStore().delRightTags(obj || router.currentRoute.value); + }, + // 关闭其他tab页签 + closeOtherPage(obj?: RouteLocationNormalized) { + return useTagsViewStore().delOthersViews(obj || router.currentRoute.value); + }, + /** + * 打开tab页签 + * @param url 路由地址 + * @param title 标题 + * @param query 参数 + */ + openPage(url: string, title?: string, query?: any) { + const obj = { path: url, query: { ...query, title } }; + return router.push(obj); + }, + /** + * 修改tab页签 + * @param obj 标签对象 + */ + updatePage(obj: RouteLocationNormalized) { + return useTagsViewStore().updateVisitedView(obj); + } +}; diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..6a6facb --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,112 @@ +import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'; +/* Layout */ +import Layout from '@/layout/index.vue'; + +/** + * Note: 路由配置项 + * + * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 + * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 + * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 + * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 + * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 + * roles: ['admin', 'common'] // 访问路由的角色权限 + * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 + * meta : { + noCache: true // 如果设置为true,则不会被 缓存(默认 false) + title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 + icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg + breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 + activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 + } + */ + +// 公共路由 +export const constantRoutes: RouteRecordRaw[] = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect/index.vue') + } + ] + }, + { + path: '/social-callback', + hidden: true, + component: () => import('@/layout/components/SocialCallback/index.vue') + }, + { + path: '/login', + component: () => import('@/views/login.vue'), + hidden: true + }, + { + path: '/register', + component: () => import('@/views/register.vue'), + hidden: true + }, + { + path: '/:pathMatch(.*)*', + component: () => import('@/views/error/404.vue'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error/401.vue'), + hidden: true + }, + { + path: '', + component: Layout, + redirect: '/index', + children: [ + { + path: '/index', + component: () => import('@/views/index.vue'), + name: 'Index', + meta: { title: '首页', icon: 'dashboard', affix: true } + } + ] + }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile', + component: () => import('@/views/system/user/profile/index.vue'), + name: 'Profile', + meta: { title: '个人中心', icon: 'user' } + } + ] + } +]; + +// 动态路由,基于用户权限动态去加载 +export const dynamicRoutes: RouteRecordRaw[] = []; + +/** + * 创建路由 + */ +const router = createRouter({ + history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH), + routes: constantRoutes, + // 刷新时,滚动条位置还原 + scrollBehavior(to, from, savedPosition) { + if (savedPosition) { + return savedPosition; + } + return { top: 0 }; + } +}); + +export default router; diff --git a/src/settings.ts b/src/settings.ts new file mode 100644 index 0000000..c71d428 --- /dev/null +++ b/src/settings.ts @@ -0,0 +1,91 @@ +import { LanguageEnum } from '@/enums/LanguageEnum'; +import { NavTypeEnum } from '@/enums/NavTypeEnum'; + +const setting: DefaultSettings = { + /** + * 网页标题 + */ + title: import.meta.env.VITE_APP_TITLE, + + theme: '#409EFF', + + /** + * 侧边栏主题 深色主题theme-dark,浅色主题theme-light + */ + sideTheme: 'theme-dark', + /** + * 是否系统布局配置 + */ + showSettings: true, + + /** + * 默认布局 + */ + navType: NavTypeEnum.LEFT, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 持久化标签页 + */ + tagsViewPersist: false, + + /** + * 显示页签图标 + */ + tagsIcon: true, + + /** + * 是否固定头部 + */ + fixedHeader: true, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * 是否显示动态标题 + */ + dynamicTitle: true, + + /** + * 是否开启动画 开启随机 关闭渐进渐出 + */ + animationEnable: false, + + /** + * 是否暗黑模式 + */ + dark: false, + + /** + * 默认语言 + */ + language: LanguageEnum.zh_CN, + + /** + * 默认大小 + */ + size: 'default', + + /** + * 默认布局 + */ + layout: '', + + /** + * 页面圆角大小 + */ + radiusBase: 14, + + /** + * 表格全高内部滚动 + */ + fullHeightTable: true +}; +export default setting; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..02ae96a --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,5 @@ +import { createPinia } from 'pinia'; + +const store = createPinia(); + +export default store; diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts new file mode 100644 index 0000000..1678bd8 --- /dev/null +++ b/src/store/modules/app.ts @@ -0,0 +1,73 @@ +import { useStorage } from '@vueuse/core'; +import enUS from 'element-plus/es/locale/lang/en'; +import zhCN from 'element-plus/es/locale/lang/zh-cn'; +import { defineStore } from 'pinia'; +import { computed, reactive, ref } from 'vue'; + +export const useAppStore = defineStore('app', () => { + const sidebarStatus = useStorage('sidebarStatus', '1'); + const sidebar = reactive({ + opened: sidebarStatus.value ? !!+sidebarStatus.value : true, + withoutAnimation: false, + hide: false + }); + const device = ref('desktop'); + const size = useStorage<'large' | 'default' | 'small'>('size', 'default'); + + // 语言 + const language = useStorage('language', 'zh_CN'); + const languageObj: any = { + en_US: enUS, + zh_CN: zhCN + }; + const locale = computed(() => { + return languageObj[language.value]; + }); + + const toggleSideBar = (withoutAnimation: boolean) => { + if (sidebar.hide) { + return false; + } + + sidebar.opened = !sidebar.opened; + sidebar.withoutAnimation = withoutAnimation; + if (sidebar.opened) { + sidebarStatus.value = '1'; + } else { + sidebarStatus.value = '0'; + } + }; + + const closeSideBar = ({ withoutAnimation }: any): void => { + sidebarStatus.value = '0'; + sidebar.opened = false; + sidebar.withoutAnimation = withoutAnimation; + }; + const toggleDevice = (d: string): void => { + device.value = d; + }; + const setSize = (s: 'large' | 'default' | 'small'): void => { + size.value = s; + }; + const toggleSideBarHide = (status: boolean): void => { + sidebar.hide = status; + }; + + const changeLanguage = (val: string): void => { + language.value = val; + }; + + return { + device, + sidebar, + language, + locale, + size, + changeLanguage, + toggleSideBar, + closeSideBar, + toggleDevice, + setSize, + toggleSideBarHide + }; +}); diff --git a/src/store/modules/dict.ts b/src/store/modules/dict.ts new file mode 100644 index 0000000..742f900 --- /dev/null +++ b/src/store/modules/dict.ts @@ -0,0 +1,65 @@ +import { defineStore } from 'pinia'; + +export const useDictStore = defineStore('dict', () => { + const dict = ref>(new Map()); + + /** + * 获取字典 + * @param _key 字典key + */ + const getDict = (_key: string): DictDataOption[] | null => { + if (!_key) { + return null; + } + return dict.value.get(_key) || null; + }; + + /** + * 设置字典 + * @param _key 字典key + * @param _value 字典value + */ + const setDict = (_key: string, _value: DictDataOption[]) => { + if (!_key) { + return false; + } + try { + dict.value.set(_key, _value); + return true; + } catch (e) { + console.error('Error in setDict:', e); + return false; + } + }; + + /** + * 删除字典 + * @param _key + */ + const removeDict = (_key: string): boolean => { + if (!_key) { + return false; + } + try { + return dict.value.delete(_key); + } catch (e) { + console.error('Error in removeDict:', e); + return false; + } + }; + + /** + * 清空字典 + */ + const cleanDict = (): void => { + dict.value.clear(); + }; + + return { + dict, + getDict, + setDict, + removeDict, + cleanDict + }; +}); diff --git a/src/store/modules/notice.ts b/src/store/modules/notice.ts new file mode 100644 index 0000000..44dfc5a --- /dev/null +++ b/src/store/modules/notice.ts @@ -0,0 +1,96 @@ +import { defineStore } from 'pinia'; +import { computed, reactive } from 'vue'; + +export interface NoticeItem { + messageId?: string | number; + title?: string; + category?: string; + type?: string; + source?: string; + read: boolean; + message: string; + content?: string; + data?: Record | null; + path?: string; + timestamp?: number; + time: string; +} + +export const useNoticeStore = defineStore('notice', () => { + const state = reactive({ + notices: [] as NoticeItem[] + }); + + const unreadCount = computed(() => state.notices.filter(item => !item.read).length); + + const buildNoticeKey = (notice: NoticeItem) => { + if (notice.messageId !== undefined && notice.messageId !== null) { + return String(notice.messageId); + } + return [notice.type, notice.source, notice.timestamp, notice.message].join(':'); + }; + + const sortNotices = () => { + state.notices.sort((a, b) => Number(b.timestamp || 0) - Number(a.timestamp || 0)); + }; + + const setNotices = (notices: NoticeItem[]) => { + state.notices = [...notices]; + sortNotices(); + }; + + const addNotice = (notice: NoticeItem) => { + const key = buildNoticeKey(notice); + const index = state.notices.findIndex(item => buildNoticeKey(item) === key); + if (index > -1) { + state.notices[index] = { + ...state.notices[index], + ...notice + }; + } else { + state.notices.unshift(notice); + } + sortNotices(); + }; + + const markRead = (messageId?: string | number) => { + if (messageId === undefined || messageId === null) { + return; + } + const target = state.notices.find(item => String(item.messageId) === String(messageId)); + if (target) { + target.read = true; + } + }; + + const markReadBatch = (messageIds: Array) => { + const idSet = new Set(messageIds.map(item => String(item))); + state.notices.forEach(item => { + if (item.messageId !== undefined && item.messageId !== null && idSet.has(String(item.messageId))) { + item.read = true; + } + }); + }; + + const readAll = () => { + markReadBatch( + state.notices + .map(item => item.messageId) + .filter((item): item is string | number => item !== undefined && item !== null) + ); + }; + + const clearNotice = () => { + state.notices = []; + }; + return { + state, + unreadCount, + setNotices, + addNotice, + markRead, + markReadBatch, + readAll, + clearNotice + }; +}); diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts new file mode 100644 index 0000000..11016f0 --- /dev/null +++ b/src/store/modules/permission.ts @@ -0,0 +1,213 @@ +import type { RouteRecordRaw } from 'vue-router'; +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { getRouters } from '@/api/menu'; +import ParentView from '@/components/ParentView/index.vue'; +import InnerLink from '@/layout/components/InnerLink/index.vue'; +import Layout from '@/layout/index.vue'; +import auth from '@/plugins/auth'; +import router, { constantRoutes, dynamicRoutes } from '@/router'; +import store from '@/store'; +import { createCustomNameComponent } from '@/utils/createCustomNameComponent'; + +// 匹配views里面所有的.vue文件,预建查找表避免每次 O(n) 扫描 +const modules = import.meta.glob('./../../views/**/*.vue'); +const viewModuleMap = new Map Promise>(); +for (const path in modules) { + const viewsIndex = path.indexOf('/views/'); + if (viewsIndex === -1) continue; + const dir = path.substring(viewsIndex + 7, path.lastIndexOf('.vue')); + viewModuleMap.set(dir, modules[path] as () => Promise); +} +export const usePermissionStore = defineStore('permission', () => { + const routes = ref([]); + const addRoutes = ref([]); + const defaultRoutes = ref([]); + const topbarRouters = ref([]); + const sidebarRouters = ref([]); + + const getRoutes = (): RouteRecordRaw[] => { + return routes.value as RouteRecordRaw[]; + }; + const getDefaultRoutes = (): RouteRecordRaw[] => { + return defaultRoutes.value as RouteRecordRaw[]; + }; + const getSidebarRoutes = (): RouteRecordRaw[] => { + return sidebarRouters.value as RouteRecordRaw[]; + }; + const getTopbarRoutes = (): RouteRecordRaw[] => { + return topbarRouters.value as RouteRecordRaw[]; + }; + + const setRoutes = (newRoutes: RouteRecordRaw[]): void => { + addRoutes.value = newRoutes; + routes.value = constantRoutes.concat(newRoutes); + }; + const setDefaultRoutes = (routes: RouteRecordRaw[]): void => { + defaultRoutes.value = constantRoutes.concat(routes); + }; + const setTopbarRoutes = (routes: RouteRecordRaw[]): void => { + topbarRouters.value = routes; + }; + const setSidebarRouters = (routes: RouteRecordRaw[]): void => { + sidebarRouters.value = routes; + }; + const generateRoutes = async (): Promise => { + const res = await getRouters(); + const data = Array.isArray(res.data) ? res.data : []; + const sdata = structuredClone(data); + const rdata = structuredClone(data); + const defaultData = structuredClone(data); + const sidebarRoutes = filterAsyncRouter(sdata); + const rewriteRoutes = filterAsyncRouter(rdata, undefined, true); + const defaultRoutes = filterAsyncRouter(defaultData); + const asyncRoutes = filterDynamicRoutes(dynamicRoutes); + asyncRoutes.forEach(route => { + router.addRoute(route); + }); + setRoutes(rewriteRoutes); + setSidebarRouters(constantRoutes.concat(sidebarRoutes)); + setDefaultRoutes(sidebarRoutes); + setTopbarRoutes(defaultRoutes); + // 路由name重复检查 + duplicateRouteChecker(asyncRoutes, sidebarRoutes); + return rewriteRoutes; + }; + + /** + * 遍历后台传来的路由字符串,转换为组件对象 + * @param asyncRouterMap 后台传来的路由字符串 + * @param lastRouter 上一级路由 + * @param type 是否是重写路由 + */ + const filterAsyncRouter = ( + asyncRouterMap: RouteRecordRaw[], + lastRouter?: RouteRecordRaw, + type = false + ): RouteRecordRaw[] => { + return asyncRouterMap.filter(route => { + if (type && route.children) { + route.children = filterChildren(route.children, undefined); + } + // Layout ParentView 组件特殊处理 + if (route.component?.toString() === 'Layout') { + route.component = Layout; + } else if (route.component?.toString() === 'ParentView') { + route.component = ParentView; + } else if (route.component?.toString() === 'InnerLink') { + route.component = InnerLink; + } else { + route.component = loadView(route.component, route.name as string); + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type); + } else { + delete route.children; + delete route.redirect; + } + return true; + }); + }; + const filterChildren = (childrenMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw): RouteRecordRaw[] => { + let children: RouteRecordRaw[] = []; + childrenMap.forEach(el => { + el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path; + if (el.children && el.children.length && el.component?.toString() === 'ParentView') { + children = children.concat(filterChildren(el.children, el)); + } else { + children.push(el); + } + }); + return children; + }; + return { + routes, + topbarRouters, + sidebarRouters, + defaultRoutes, + + getRoutes, + getDefaultRoutes, + getSidebarRoutes, + getTopbarRoutes, + + setRoutes, + generateRoutes, + setSidebarRouters + }; +}); + +// 动态路由遍历,验证是否具备权限 +export const filterDynamicRoutes = (routes: RouteRecordRaw[]) => { + const res: RouteRecordRaw[] = []; + routes.forEach(route => { + if (route.permissions) { + if (auth.hasPermiOr(route.permissions)) { + res.push(route); + } + } else if (route.roles) { + if (auth.hasRoleOr(route.roles)) { + res.push(route); + } + } + }); + return res; +}; + +export const loadView = (view: any, name: string) => { + const loader = viewModuleMap.get(view); + if (loader) { + return createCustomNameComponent(loader, { name }); + } + return undefined; +}; + +// 非setup +export const usePermissionStoreHook = () => { + return usePermissionStore(store); +}; + +interface Route { + name?: string | symbol; + path: string; + children?: Route[]; +} + +/** + * 检查路由name是否重复 + * @param localRoutes 本地路由 + * @param routes 动态路由 + */ +function duplicateRouteChecker(localRoutes: Route[], routes: Route[]) { + // 展平 + function flatRoutes(routes: Route[]) { + const res: Route[] = []; + routes.forEach(route => { + if (route.children) { + res.push(...flatRoutes(route.children)); + } else { + res.push(route); + } + }); + return res; + } + + const allRoutes = flatRoutes([...localRoutes, ...routes]); + + const nameList: string[] = []; + allRoutes.forEach(route => { + const name = route.name?.toString() ?? ''; + if (!name) return; + if (nameList.includes(name)) { + const message = `路由名称: [${name}] 重复, 会造成 404`; + console.error(message); + ElNotification({ + title: '路由名称重复', + message, + type: 'error' + }); + return; + } + nameList.push(name); + }); +} diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts new file mode 100644 index 0000000..f4a0389 --- /dev/null +++ b/src/store/modules/settings.ts @@ -0,0 +1,60 @@ +import { useStorage } from '@vueuse/core'; +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { NavTypeEnum } from '@/enums/NavTypeEnum'; +import defaultSettings from '@/settings'; +import { useDynamicTitle } from '@/utils/dynamicTitle'; + +export const useSettingsStore = defineStore('setting', () => { + const storageSetting = useStorage('layout-setting', { + tagsView: defaultSettings.tagsView, + tagsViewPersist: defaultSettings.tagsViewPersist, + tagsIcon: defaultSettings.tagsIcon, + fixedHeader: defaultSettings.fixedHeader, + sidebarLogo: defaultSettings.sidebarLogo, + dynamicTitle: defaultSettings.dynamicTitle, + sideTheme: defaultSettings.sideTheme, + theme: defaultSettings.theme, + navType: defaultSettings.navType, + radiusBase: defaultSettings.radiusBase, + fullHeightTable: defaultSettings.fullHeightTable + }); + const title = ref(defaultSettings.title); + const theme = ref(storageSetting.value.theme); + const sideTheme = ref(storageSetting.value.sideTheme); + const showSettings = ref(defaultSettings.showSettings); + const tagsView = ref(storageSetting.value.tagsView); + const tagsViewPersist = ref(storageSetting.value.tagsViewPersist); + const tagsIcon = ref(storageSetting.value.tagsIcon); + const fixedHeader = ref(storageSetting.value.fixedHeader); + const sidebarLogo = ref(storageSetting.value.sidebarLogo); + const dynamicTitle = ref(storageSetting.value.dynamicTitle); + const animationEnable = ref(defaultSettings.animationEnable); + const dark = ref(defaultSettings.dark); + const navType = ref(storageSetting.value.navType || NavTypeEnum.LEFT); + const radiusBase = ref(storageSetting.value.radiusBase ?? defaultSettings.radiusBase); + const fullHeightTable = ref(storageSetting.value.fullHeightTable ?? defaultSettings.fullHeightTable); + + const setTitle = (value: string) => { + title.value = value; + useDynamicTitle(); + }; + return { + title, + theme, + sideTheme, + showSettings, + tagsView, + tagsViewPersist, + tagsIcon, + fixedHeader, + sidebarLogo, + dynamicTitle, + animationEnable, + dark, + navType, + radiusBase, + fullHeightTable, + setTitle + }; +}); diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts new file mode 100644 index 0000000..7ed523e --- /dev/null +++ b/src/store/modules/tagsView.ts @@ -0,0 +1,337 @@ +import type { LocationQuery, RouteLocationNormalized, RouteMeta } from 'vue-router'; +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { useSettingsStore } from './settings'; + +const PERSIST_KEY = 'tags-view-visited'; + +type PersistedTagView = { + path: string; + fullPath?: string; + name?: string | symbol | null; + title?: string; + query?: LocationQuery; + meta?: RouteMeta; +}; + +type TagView = RouteLocationNormalized & { + title?: string; +}; + +const isPersistEnabled = () => { + return useSettingsStore().tagsViewPersist; +}; + +const normalizeVisitedView = (view: RouteLocationNormalized): TagView => { + return Object.assign({}, view, { + title: view.meta?.title || 'no-name' + }); +}; + +const saveVisitedViews = (views: any[]) => { + if (!isPersistEnabled()) { + return; + } + + const payload: PersistedTagView[] = (views as TagView[]) + .filter(view => !view.meta?.affix) + .map(view => ({ + path: view.path, + fullPath: view.fullPath, + name: view.name, + title: (view as any).title || view.meta?.title || 'no-name', + query: view.query, + meta: view.meta + })); + + localStorage.setItem(PERSIST_KEY, JSON.stringify(payload)); +}; + +const loadVisitedViews = (): PersistedTagView[] => { + const cache = localStorage.getItem(PERSIST_KEY); + if (!cache) { + return []; + } + + try { + return JSON.parse(cache) as PersistedTagView[]; + } catch { + localStorage.removeItem(PERSIST_KEY); + return []; + } +}; + +const clearVisitedViews = () => { + localStorage.removeItem(PERSIST_KEY); +}; + +export const useTagsViewStore = defineStore('tagsView', () => { + const visitedViews = ref([]); + const cachedViews = ref([]); + const iframeViews = ref([]); + + const getVisitedViews = (): TagView[] => { + return visitedViews.value as TagView[]; + }; + const getIframeViews = (): TagView[] => { + return iframeViews.value as TagView[]; + }; + const getCachedViews = (): string[] => { + return cachedViews.value; + }; + + const addView = (view: RouteLocationNormalized) => { + addVisitedView(view); + addCachedView(view); + }; + + const addIframeView = (view: RouteLocationNormalized): void => { + if (iframeViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return; + iframeViews.value.push(normalizeVisitedView(view) as RouteLocationNormalized); + }; + + const delIframeView = (view: RouteLocationNormalized): Promise => { + return new Promise(resolve => { + iframeViews.value = iframeViews.value.filter((item: RouteLocationNormalized) => item.path !== view.path); + resolve(iframeViews.value.slice() as RouteLocationNormalized[]); + }); + }; + + const addVisitedView = (view: RouteLocationNormalized): void => { + if (visitedViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return; + visitedViews.value.push(normalizeVisitedView(view)); + saveVisitedViews(visitedViews.value); + }; + + const addAffixView = (view: RouteLocationNormalized): void => { + if (visitedViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return; + const insertIndex = visitedViews.value.findIndex(item => !item.meta?.affix); + const normalizedView = normalizeVisitedView(view); + if (insertIndex === -1) { + visitedViews.value.push(normalizedView); + } else { + visitedViews.value.splice(insertIndex, 0, normalizedView); + } + }; + + const loadPersistedViews = (): void => { + loadVisitedViews().forEach(view => { + if (visitedViews.value.some(item => item.path === view.path)) { + return; + } + + visitedViews.value.push({ + hash: '', + matched: [], + params: {}, + redirectedFrom: undefined, + path: view.path, + fullPath: view.fullPath || view.path, + query: view.query || {}, + name: view.name || undefined, + meta: view.meta || {}, + title: view.title || view.meta?.title || 'no-name' + } as TagView); + }); + }; + + const delView = ( + view: RouteLocationNormalized + ): Promise<{ + visitedViews: RouteLocationNormalized[]; + cachedViews: string[]; + }> => { + return new Promise(resolve => { + delVisitedView(view); + delCachedView(view); + resolve({ + visitedViews: visitedViews.value.slice() as RouteLocationNormalized[], + cachedViews: [...cachedViews.value] + }); + }); + }; + + const delVisitedView = (view: RouteLocationNormalized): Promise => { + return new Promise(resolve => { + for (const [i, v] of visitedViews.value.entries()) { + if (v.path === view.path) { + visitedViews.value.splice(i, 1); + break; + } + } + saveVisitedViews(visitedViews.value); + resolve(visitedViews.value.slice() as RouteLocationNormalized[]); + }); + }; + + const delCachedView = (view?: RouteLocationNormalized): Promise => { + let viewName = ''; + if (view) { + viewName = view.name as string; + } + return new Promise(resolve => { + const index = cachedViews.value.indexOf(viewName); + index > -1 && cachedViews.value.splice(index, 1); + resolve([...cachedViews.value]); + }); + }; + + const delOthersViews = ( + view: RouteLocationNormalized + ): Promise<{ + visitedViews: RouteLocationNormalized[]; + cachedViews: string[]; + }> => { + return new Promise(resolve => { + delOthersVisitedViews(view); + delOthersCachedViews(view); + resolve({ + visitedViews: visitedViews.value.slice() as RouteLocationNormalized[], + cachedViews: [...cachedViews.value] + }); + }); + }; + + const delOthersVisitedViews = (view: RouteLocationNormalized): Promise => { + return new Promise(resolve => { + visitedViews.value = visitedViews.value.filter((v: RouteLocationNormalized) => { + return v.meta?.affix || v.path === view.path; + }); + saveVisitedViews(visitedViews.value); + resolve(visitedViews.value.slice() as RouteLocationNormalized[]); + }); + }; + + const delOthersCachedViews = (view: RouteLocationNormalized): Promise => { + const viewName = view.name as string; + return new Promise(resolve => { + const index = cachedViews.value.indexOf(viewName); + if (index > -1) { + cachedViews.value = cachedViews.value.slice(index, index + 1); + } else { + cachedViews.value = []; + } + resolve([...cachedViews.value]); + }); + }; + + const delAllViews = (): Promise<{ + visitedViews: RouteLocationNormalized[]; + cachedViews: string[]; + }> => { + return new Promise(resolve => { + delAllVisitedViews(); + delAllCachedViews(); + resolve({ + visitedViews: visitedViews.value.slice() as RouteLocationNormalized[], + cachedViews: [...cachedViews.value] + }); + }); + }; + + const delAllVisitedViews = (): Promise => { + return new Promise(resolve => { + visitedViews.value = visitedViews.value.filter((tag: RouteLocationNormalized) => tag.meta?.affix); + clearVisitedViews(); + resolve(visitedViews.value.slice() as RouteLocationNormalized[]); + }); + }; + + const delAllCachedViews = (): Promise => { + return new Promise(resolve => { + cachedViews.value = []; + resolve([...cachedViews.value]); + }); + }; + + const updateVisitedView = (view: RouteLocationNormalized): void => { + for (let v of visitedViews.value) { + if (v.path === view.path) { + v = Object.assign(v, view); + break; + } + } + saveVisitedViews(visitedViews.value); + }; + + const delRightTags = (view: RouteLocationNormalized): Promise => { + return new Promise(resolve => { + const index = visitedViews.value.findIndex((v: RouteLocationNormalized) => v.path === view.path); + if (index === -1) { + resolve(visitedViews.value.slice() as RouteLocationNormalized[]); + return; + } + visitedViews.value = visitedViews.value.filter((item: RouteLocationNormalized, idx: number) => { + if (idx <= index || (item.meta && item.meta.affix)) { + return true; + } + const i = cachedViews.value.indexOf(item.name as string); + if (i > -1) { + cachedViews.value.splice(i, 1); + } + return false; + }); + saveVisitedViews(visitedViews.value); + resolve(visitedViews.value.slice() as RouteLocationNormalized[]); + }); + }; + + const delLeftTags = (view: RouteLocationNormalized): Promise => { + return new Promise(resolve => { + const index = visitedViews.value.findIndex((v: RouteLocationNormalized) => v.path === view.path); + if (index === -1) { + resolve(visitedViews.value.slice() as RouteLocationNormalized[]); + return; + } + visitedViews.value = visitedViews.value.filter((item: RouteLocationNormalized, idx: number) => { + if (idx >= index || (item.meta && item.meta.affix)) { + return true; + } + const i = cachedViews.value.indexOf(item.name as string); + if (i > -1) { + cachedViews.value.splice(i, 1); + } + return false; + }); + saveVisitedViews(visitedViews.value); + resolve(visitedViews.value.slice() as RouteLocationNormalized[]); + }); + }; + + const addCachedView = (view: RouteLocationNormalized): void => { + const viewName = view.name as string; + if (!viewName) return; + if (cachedViews.value.includes(viewName)) return; + if (!view.meta?.noCache) { + cachedViews.value.push(viewName); + } + }; + + return { + visitedViews, + cachedViews, + iframeViews, + + getVisitedViews, + getIframeViews, + getCachedViews, + + addVisitedView, + addAffixView, + addCachedView, + delVisitedView, + delCachedView, + updateVisitedView, + addView, + delView, + delAllViews, + delAllVisitedViews, + delAllCachedViews, + delOthersViews, + delRightTags, + delLeftTags, + addIframeView, + delIframeView, + loadPersistedViews + }; +}); diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts new file mode 100644 index 0000000..060a14b --- /dev/null +++ b/src/store/modules/user.ts @@ -0,0 +1,91 @@ +import { to } from 'await-to-js'; +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import type { UserInfo } from '@/api/system/user/types'; +import type { LoginData, LoginResult } from '@/api/types'; +import type { RuoYiAjaxResult } from '@/utils/api-types'; +import { getInfo as getUserInfo, login as loginApi, logout as logoutApi } from '@/api/login'; +import defAva from '@/assets/images/profile.jpg'; +import { getToken, removeToken, setToken } from '@/utils/auth'; + +export const useUserStore = defineStore('user', () => { + const token = ref(getToken()); + const name = ref(''); + const nickname = ref(''); + const userId = ref(''); + const avatar = ref(''); + const roles = ref>([]); // 用户角色编码集合 → 判断路由权限 + const permissions = ref>([]); // 用户权限编码集合 → 判断按钮权限 + + /** + * 登录 + * @param userInfo + * @returns + */ + const login = async (userInfo: LoginData): Promise => { + const [err, res] = await to(loginApi(userInfo)); + if (res) { + const data = (res as RuoYiAjaxResult).data; + if (!data?.access_token) { + return Promise.reject(err); + } + setToken(data.access_token); + token.value = data.access_token; + return Promise.resolve(); + } + return Promise.reject(err); + }; + + // 获取用户信息 + const getInfo = async (): Promise => { + const [err, res] = await to(getUserInfo()); + if (res) { + const data = (res as RuoYiAjaxResult).data; + if (!data?.user) { + return Promise.reject(err); + } + const user = data.user; + const profile = user.avatarUrl == '' || user.avatarUrl == null ? defAva : user.avatarUrl; + + if (data.roles && data.roles.length > 0) { + // 验证返回的roles是否是一个非空数组 + roles.value = data.roles; + permissions.value = data.permissions; + } else { + roles.value = ['ROLE_DEFAULT']; + } + name.value = user.userName; + nickname.value = user.nickName; + avatar.value = profile; + userId.value = user.userId; + return Promise.resolve(); + } + return Promise.reject(err); + }; + + // 注销 + const logout = async (): Promise => { + await logoutApi(); + token.value = ''; + roles.value = []; + permissions.value = []; + removeToken(); + }; + + const setAvatar = (value: string) => { + avatar.value = value; + }; + + return { + userId, + token, + nickname, + avatar, + roles, + permissions, + login, + getInfo, + logout, + setAvatar + }; +}); diff --git a/src/types/axios.d.ts b/src/types/axios.d.ts new file mode 100644 index 0000000..ab7f6f4 --- /dev/null +++ b/src/types/axios.d.ts @@ -0,0 +1,8 @@ +declare module 'axios' { + interface AxiosResponse { + code: number; + msg: string; + rows: T; + total: number; + } +} diff --git a/src/types/element.d.ts b/src/types/element.d.ts new file mode 100644 index 0000000..479b9c1 --- /dev/null +++ b/src/types/element.d.ts @@ -0,0 +1,35 @@ +import type * as ep from 'element-plus'; +declare global { + type ElTagType = 'primary' | 'success' | 'info' | 'warning' | 'danger'; + type ElFormInstance = ep.FormInstance; + type ElTableInstance = ep.TableInstance; + type ElUploadInstance = ep.UploadInstance; + type ElScrollbarInstance = ep.ScrollbarInstance; + type ElInputInstance = ep.InputInstance; + type ElInputNumberInstance = ep.InputNumberInstance; + type ElRadioInstance = ep.RadioInstance; + type ElRadioGroupInstance = ep.RadioGroupInstance; + type ElRadioButtonInstance = ep.RadioButtonInstance; + type ElCheckboxInstance = ep.CheckboxInstance; + type ElSwitchInstance = ep.SwitchInstance; + type ElCascaderInstance = ep.CascaderInstance; + type ElColorPickerInstance = ep.ColorPickerInstance; + type ElRateInstance = ep.RateInstance; + type ElSliderInstance = ep.SliderInstance; + + type ElTreeInstance = InstanceType; + type ElTreeSelectInstance = InstanceType; + type ElSelectInstance = InstanceType; + type ElCardInstance = InstanceType; + type ElDialogInstance = InstanceType; + type ElCheckboxGroupInstance = InstanceType; + type ElDatePickerInstance = InstanceType; + type ElTimePickerInstance = InstanceType; + type ElTimeSelectInstance = InstanceType; + + type TransferKey = ep.TransferKey; + type CheckboxValueType = ep.CheckboxValueType; + type ElFormRules = ep.FormRules; + type DateModelType = ep.DateModelType; + type UploadFile = ep.UploadFile; +} diff --git a/src/types/env.d.ts b/src/types/env.d.ts new file mode 100644 index 0000000..e0f60fe --- /dev/null +++ b/src/types/env.d.ts @@ -0,0 +1,32 @@ +declare module '*.vue' { + import { DefineComponent } from 'vue'; + const Component: DefineComponent<{}, {}, any>; + export default Component; +} + +declare module 'virtual:svg-icons-register' {} +declare module 'virtual:*' {} + +// 环境变量 +interface ImportMetaEnv { + VITE_APP_TITLE: string; + VITE_APP_PORT: number; + VITE_APP_BASE_API: string; + VITE_APP_BASE_URL: string; + VITE_APP_CONTEXT_PATH: string; + VITE_APP_MONITOR_ADMIN: string; + VITE_APP_SNAILJOB_ADMIN: string; + VITE_APP_SNAILAI_ADMIN: string; + VITE_APP_ENV: string; + VITE_APP_ENCRYPT: string; + VITE_APP_RSA_PUBLIC_KEY: string; + VITE_APP_RSA_PRIVATE_KEY: string; + VITE_APP_CLIENT_ID: string; + VITE_APP_MESSAGE_ENABLED: string; + VITE_APP_MESSAGE_TRANSPORT: string; + VITE_APP_MESSAGE_PATH: string; +} +interface ImportMeta { + readonly env: ImportMetaEnv; + // readonly glob: any; +} diff --git a/src/types/global.d.ts b/src/types/global.d.ts new file mode 100644 index 0000000..5cc45d1 --- /dev/null +++ b/src/types/global.d.ts @@ -0,0 +1,179 @@ +import type { ComponentInternalInstance as ComponentInstance } from 'vue'; +import { LanguageEnum } from '@/enums/LanguageEnum'; +import { NavTypeEnum } from '@/enums/NavTypeEnum'; + +declare global { + /** vue Instance */ + type ComponentInternalInstance = ComponentInstance; + + /** + * 界面字段隐藏属性 + */ + interface FieldOption { + key: number; + label: string; + visible: boolean; + children?: Array; + } + + /** + * 弹窗属性 + */ + interface DialogOption { + /** + * 弹窗标题 + */ + title?: string; + /** + * 是否显示 + */ + visible: boolean; + } + + interface UploadOption { + /** 设置上传的请求头部 */ + headers: { [key: string]: any }; + + /** 上传的地址 */ + url: string; + } + + /** + * 导入属性 + */ + interface ImportOption extends UploadOption { + /** 是否显示弹出层 */ + open: boolean; + /** 弹出层标题 */ + title: string; + /** 是否禁用上传 */ + isUploading: boolean; + + updateSupport: number; + + /** 其他参数 */ + [key: string]: any; + } + /** + * 字典数据 数据配置 + */ + interface DictDataOption { + label: string; + value: string; + elTagType?: ElTagType; + elTagClass?: string; + } + + interface BaseEntity { + createBy?: any; + createDept?: any; + createTime?: string; + updateBy?: any; + updateTime?: any; + } + + /** + * 分页数据 + * T : 表单数据 + * D : 查询参数 + */ + interface PageData { + form: T; + queryParams: D; + rules: ElFormRules; + } + /** + * 分页查询参数 + */ + interface PageQuery { + pageNum: number; + pageSize: number; + } + interface LayoutSetting { + /** + * 默认布局 + */ + navType: NavTypeEnum; + + /** + * 是否显示多标签导航 + */ + tagsView: boolean; + /** + * 是否持久化标签页 + */ + tagsViewPersist: boolean; + /** + * 显示页签图标 + */ + tagsIcon: boolean; + /** + * 是否固定头部 + */ + fixedHeader: boolean; + /** + * 是否显示侧边栏Logo + */ + sidebarLogo: boolean; + /** + * 是否显示动态标题 + */ + dynamicTitle: boolean; + /** + * 侧边栏主题 theme-dark | theme-light + */ + sideTheme: string; + /** + * 主题模式 + */ + theme: string; + /** + * 页面圆角大小 + */ + radiusBase: number; + + /** + * 表格全高内部滚动 + */ + fullHeightTable: boolean; + } + + interface DefaultSettings extends LayoutSetting { + /** + * 网页标题 + */ + title: string; + + /** + * 是否显示系统布局设置 + */ + showSettings: boolean; + + /** + * 导航栏布局 + */ + layout: string; + + /** + * 布局大小 + */ + size: 'large' | 'default' | 'small'; + + /** + * 语言 + */ + language: LanguageEnum; + + /** + * 是否启用动画效果 + */ + animationEnable: boolean; + /** + * 是否启用暗黑模式 + * + * true:暗黑模式 + * false: 明亮模式 + */ + dark: boolean; + } +} diff --git a/src/types/module.d.ts b/src/types/module.d.ts new file mode 100644 index 0000000..7c88d72 --- /dev/null +++ b/src/types/module.d.ts @@ -0,0 +1,31 @@ +import type { LanguageType } from '@/lang'; +import auth from '@/plugins/auth'; +import cache from '@/plugins/cache'; +import download from '@/plugins/download'; +import modal from '@/plugins/modal'; +import tab from '@/plugins/tab'; + +declare module 'vue' { + interface ComponentCustomProperties { + $modal: typeof modal; + $tab: typeof tab; + $download: typeof download; + $auth: typeof auth; + $cache: typeof cache; + /** + * i18n $t方法支持ts类型提示 + * @param key i18n key + */ + $t(key: ObjKeysToUnion): string; + } +} + +/** + * { a: 1, b: { ba: { baa: 1, bab: 2 }, bb: 2} } ---> a | b.ba.baa | b.ba.bab | b.bb + * https://juejin.cn/post/7280062870670606397 + */ +export type ObjKeysToUnion = T extends object + ? { + [K in keyof T]: ObjKeysToUnion; + }[keyof T] + : P; diff --git a/src/types/router.d.ts b/src/types/router.d.ts new file mode 100644 index 0000000..9f30636 --- /dev/null +++ b/src/types/router.d.ts @@ -0,0 +1,37 @@ +import { LocationQuery, type RouteMeta as VRouteMeta } from 'vue-router'; +declare module 'vue-router' { + interface RouteMeta extends VRouteMeta { + link?: string; + title?: string; + affix?: boolean; + noCache?: boolean; + activeMenu?: string; + icon?: string; + breadcrumb?: boolean; + } + + interface _RouteRecordBase { + hidden?: boolean | string | number; + permissions?: string[]; + roles?: string[]; + alwaysShow?: boolean; + query?: string; + ext?: string; + parentPath?: string; + } + + interface _RouteLocationBase { + children?: _RouteRecordBase[]; + path?: string; + title?: string; + } + + interface TagView { + fullPath?: string; + name?: string; + path?: string; + title?: string; + meta?: RouteMeta; + query?: LocationQuery; + } +} diff --git a/src/utils/api-types.ts b/src/utils/api-types.ts new file mode 100644 index 0000000..c866339 --- /dev/null +++ b/src/utils/api-types.ts @@ -0,0 +1,11 @@ +/** + * 与 `src/utils/request` 拦截器一致:resolve 的是若依风格 JSON `{ code, msg, data }`。 + * axios 1.13 + TS6 下避免从 `axios` 拉已移除的 AxiosPromise。 + */ +export interface RuoYiAjaxResult { + code?: number; + msg?: string; + data?: T; +} + +export type AxiosPromise = Promise>; diff --git a/src/utils/auth.ts b/src/utils/auth.ts new file mode 100644 index 0000000..db50ac9 --- /dev/null +++ b/src/utils/auth.ts @@ -0,0 +1,9 @@ +const TokenKey = 'Admin-Token'; + +const tokenStorage = useStorage(TokenKey, null); + +export const getToken = () => tokenStorage.value; + +export const setToken = (access_token: string) => (tokenStorage.value = access_token); + +export const removeToken = () => (tokenStorage.value = null); diff --git a/src/utils/createCustomNameComponent.tsx b/src/utils/createCustomNameComponent.tsx new file mode 100644 index 0000000..802ea13 --- /dev/null +++ b/src/utils/createCustomNameComponent.tsx @@ -0,0 +1,42 @@ +/** + * 后台返回的路由动态生成name 解决缓存问题 + * 感谢 @fourteendp + * 详见 https://github.com/vbenjs/vue-vben-admin/issues/3927 + */ +import { Component, defineComponent, h } from 'vue'; + +interface Options { + name?: string; +} + +export function createCustomNameComponent(loader: () => Promise, options: Options = {}): () => Promise { + const { name } = options; + let component: Component | null = null; + let wrappedComponent: Component | null = null; + + const load = async () => { + try { + const { default: loadedComponent } = await loader(); + component = loadedComponent; + } catch (error) { + console.error(`Cannot resolve component ${name}, error:`, error); + } + }; + + return async () => { + if (!component) { + await load(); + } + + if (!wrappedComponent) { + wrappedComponent = defineComponent({ + name, + render() { + return h(component as Component); + } + }); + } + + return Promise.resolve(wrappedComponent); + }; +} diff --git a/src/utils/crypto.ts b/src/utils/crypto.ts new file mode 100644 index 0000000..bd81531 --- /dev/null +++ b/src/utils/crypto.ts @@ -0,0 +1,78 @@ +import { ecb } from '@noble/ciphers/aes.js'; + +/** + * 随机生成32位的字符串 + * @returns {string} + */ +const generateRandomString = (): string => { + const array = new Uint8Array(32); + crypto.getRandomValues(array); + return Array.from(array, b => b.toString(16).padStart(2, '0')) + .join('') + .slice(0, 32); +}; + +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); + +const bytesToBase64 = (bytes: Uint8Array): string => { + let binary = ''; + for (const byte of bytes) { + binary += String.fromCharCode(byte); + } + return btoa(binary); +}; + +const base64ToBytes = (str: string): Uint8Array => { + const binary = atob(str); + const bytes = new Uint8Array(binary.length); + for (let i = 0; i < binary.length; i++) { + bytes[i] = binary.charCodeAt(i); + } + return bytes; +}; + +/** + * 随机生成aes 密钥 + * @returns {Uint8Array} + */ +export const generateAesKey = (): Uint8Array => { + return encoder.encode(generateRandomString()); +}; + +/** + * 加密base64 + * @returns {string} + */ +export const encryptBase64 = (bytes: Uint8Array): string => { + return bytesToBase64(bytes); +}; + +/** + * 解密base64 + */ +export const decryptBase64 = (str: string): Uint8Array => { + return base64ToBytes(str); +}; + +/** + * 使用密钥对数据进行加密 (AES/ECB/PKCS7Padding 与后端保持一致) + * @param message + * @param aesKey + * @returns {string} + */ +export const encryptWithAes = (message: string, aesKey: Uint8Array): string => { + const encrypted = ecb(aesKey).encrypt(encoder.encode(message)); + return bytesToBase64(encrypted); +}; + +/** + * 使用密钥对数据进行解密 + * @param message + * @param aesKey + * @returns {string} + */ +export const decryptWithAes = (message: string, aesKey: Uint8Array): string => { + const decrypted = ecb(aesKey).decrypt(base64ToBytes(message)); + return decoder.decode(decrypted); +}; diff --git a/src/utils/dict.ts b/src/utils/dict.ts new file mode 100644 index 0000000..b2bf87d --- /dev/null +++ b/src/utils/dict.ts @@ -0,0 +1,39 @@ +import { getDicts } from '@/api/system/dict/data'; +import { useDictStore } from '@/store/modules/dict'; + +const pendingRequests = new Map>(); + +/** + * 获取字典数据 + */ +export const useDict = (...args: string[]): { [key: string]: DictDataOption[] } => { + const res = reactive<{ [key: string]: DictDataOption[] }>({}); + + args.forEach(async dictType => { + res[dictType] = []; + const dicts = useDictStore().getDict(dictType); + if (dicts) { + res[dictType] = dicts; + } else { + if (!pendingRequests.has(dictType)) { + const request = getDicts(dictType) + .then(resp => { + const data = resp.data.map( + (p): DictDataOption => ({ + label: p.dictLabel, + value: p.dictValue, + elTagType: p.listClass, + elTagClass: p.cssClass + }) + ); + useDictStore().setDict(dictType, data); + return data; + }) + .finally(() => pendingRequests.delete(dictType)); + pendingRequests.set(dictType, request); + } + res[dictType] = await pendingRequests.get(dictType)!; + } + }); + return res; +}; diff --git a/src/utils/dynamicTitle.ts b/src/utils/dynamicTitle.ts new file mode 100644 index 0000000..8e23ef8 --- /dev/null +++ b/src/utils/dynamicTitle.ts @@ -0,0 +1,14 @@ +import defaultSettings from '@/settings'; +import { useSettingsStore } from '@/store/modules/settings'; + +/** + * 动态修改标题 + */ +export const useDynamicTitle = () => { + const settingsStore = useSettingsStore(); + if (settingsStore.dynamicTitle) { + document.title = settingsStore.title + ' - ' + import.meta.env.VITE_APP_TITLE; + } else { + document.title = defaultSettings.title as string; + } +}; diff --git a/src/utils/errorCode.ts b/src/utils/errorCode.ts new file mode 100644 index 0000000..d85914e --- /dev/null +++ b/src/utils/errorCode.ts @@ -0,0 +1,7 @@ +export const errorCode: any = { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + default: '系统未知错误,请反馈给管理员' +}; +export default errorCode; diff --git a/src/utils/geolocation.ts b/src/utils/geolocation.ts new file mode 100644 index 0000000..0df69b1 --- /dev/null +++ b/src/utils/geolocation.ts @@ -0,0 +1,125 @@ +// utils/geolocation.ts + +/** + * 定位配置参数 + * @interface GeoOptions + */ +interface GeoOptions { + /** 是否开启高精度模式,一般会调用设备GPS,耗电更高、速度可能变慢 */ + enableHighAccuracy?: boolean; + /** 定位超时时间,单位毫秒 */ + timeout?: number; + /** 复用缓存位置的最大有效期,单位毫秒;0代表不使用缓存,每次都重新获取 */ + maximumAge?: number; +} + +/** + * 定位成功返回的数据结构 + * @interface GeoResult + */ +export interface GeoResult { + /** 纬度 */ + lat: number; + /** 经度 */ + lng: number; + /** 定位精度,单位米,数值越小越精准 */ + accuracy: number; +} + +/** + * 获取浏览器单次当前定位,Promise封装 + * @param options 定位配置项,可选 + * @returns Promise 定位信息 + * @description 线上环境必须HTTPS,localhost本地调试不受限制;需要用户授权定位权限 + */ +export function getCurrentPosition( + options: GeoOptions = {}, +): Promise { + return new Promise((resolve, reject) => { + // 判断当前环境是否支持浏览器原生定位API + if (!navigator?.geolocation) { + return reject(new Error("当前浏览器不支持定位")); + } + + // 合并默认配置与传入配置,PositionOptions是TS内置原生类型 + const geoOpt: PositionOptions = { + timeout: 5000, + enableHighAccuracy: true, // ✅ 关键:开启高精度GPS模式 + maximumAge: 0, // ❗不使用缓存,每次都重新获取最新GPS坐标 + ...options, + }; + + // 调用原生定位API + navigator.geolocation.getCurrentPosition( + // 定位成功回调 + (position: GeolocationPosition) => { + console.log(position); + const { latitude, longitude, accuracy } = position.coords; + resolve({ + lat: latitude, + lng: longitude, + accuracy, + }); + }, + // 定位失败回调,GeolocationPositionError是TS原生错误类型 + (err: GeolocationPositionError) => { + let msg = "定位失败"; + console.log(err); + switch (err.code) { + case err.PERMISSION_DENIED: + msg = "用户拒绝定位权限"; + break; + case err.POSITION_UNAVAILABLE: + msg = "无法获取位置,信号较差"; + break; + case err.TIMEOUT: + msg = "定位超时"; + break; + } + reject(new Error(msg)); + }, + geoOpt, + ); + }); +} + +/** + * 持续监听位置变化(适合轨迹、导航场景) + * @param onSuccess 位置更新成功回调,每次位置变动都会触发 + * @param onError 定位异常回调,可选 + * @param options 定位配置项,可选 + * @returns number watchId 监听标识,用于后续停止监听 + */ +export function watchPosition( + onSuccess: (res: GeoResult) => void, + onError?: (err: GeolocationPositionError) => void, + options: GeoOptions = {}, +): number { + const geoOpt: PositionOptions = { + enableHighAccuracy: true, + timeout: 10000, + maximumAge: 0, + ...options, + }; + + return navigator.geolocation.watchPosition( + (pos) => { + // 每次位置更新,统一格式化返回结构 + onSuccess({ + lat: pos.coords.latitude, + lng: pos.coords.longitude, + accuracy: pos.coords.accuracy, + }); + }, + onError, + geoOpt, + ); +} + +/** + * 停止持续位置监听 + * @param watchId 监听ID,watchPosition返回的值 + */ +export function clearWatchPosition(watchId: number): void { + navigator.geolocation?.clearWatch(watchId); +} diff --git a/src/utils/i18n.ts b/src/utils/i18n.ts new file mode 100644 index 0000000..ab77af0 --- /dev/null +++ b/src/utils/i18n.ts @@ -0,0 +1,16 @@ +// translate router.meta.title, be used in breadcrumb sidebar tagsview +import i18n from '@/lang/index'; + +/** + * 获取国际化路由,如果不存在则原生返回 + * @param title 路由名称 + * @returns {string} + */ +export const translateRouteTitle = (title: string): string => { + const hasKey = i18n.global.te('route.' + title); + if (hasKey) { + const translatedTitle = i18n.global.t('route.' + title); + return translatedTitle; + } + return title; +}; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000..be2be50 --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,319 @@ +import { parseTime } from '@/utils/ruoyi'; + +/** + * 表格时间格式化 + */ +export const formatDate = (cellValue: string) => { + if (cellValue == null || cellValue == '') return ''; + const date = new Date(cellValue); + const year = date.getFullYear(); + const month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1; + const day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate(); + const hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours(); + const minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes(); + const seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds(); + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds; +}; + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export const formatTime = (time: string, option: string) => { + let t: number; + if (('' + time).length === 10) { + t = parseInt(time) * 1000; + } else { + t = +time; + } + const d: any = new Date(t); + const now = Date.now(); + + const diff = (now - d) / 1000; + + if (diff < 30) { + return '刚刚'; + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前'; + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前'; + } else if (diff < 3600 * 24 * 2) { + return '1天前'; + } + if (option) { + return parseTime(t, option); + } else { + return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'; + } +}; + +/** + * @param {string} url + * @returns {Object} + */ +export const getQueryObject = (url: string) => { + url = url == null ? window.location.href : url; + const search = url.substring(url.lastIndexOf('?') + 1); + const obj: { [key: string]: string } = {}; + const reg = /([^?&=]+)=([^?&=]*)/g; + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1); + let val = decodeURIComponent($2); + val = String(val); + obj[name] = val; + return rs; + }); + return obj; +}; + +/** + * @param {string} input value + * @returns {number} output value + */ +export const byteLength = (str: string) => { + // returns the byte length of an utf8 string + let s = str.length; + for (let i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i); + if (code > 0x7f && code <= 0x7ff) s++; + else if (code > 0x7ff && code <= 0xffff) s += 2; + if (code >= 0xdc00 && code <= 0xdfff) i--; + } + return s; +}; + +/** + * @param {Array} actual + * @returns {Array} + */ +export const cleanArray = (actual: Array) => { + const newArray: any[] = []; + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]); + } + } + return newArray; +}; + +/** + * @param {Object} json + * @returns {Array} + */ +export const param = (json: any) => { + if (!json) return ''; + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return ''; + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]); + }) + ).join('&'); +}; + +/** + * @param {string} url + * @returns {Object} + */ +export const param2Obj = (url: string) => { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' '); + if (!search) { + return {}; + } + const obj: any = {}; + const searchArr = search.split('&'); + searchArr.forEach(v => { + const index = v.indexOf('='); + if (index !== -1) { + const name = v.substring(0, index); + const val = v.substring(index + 1, v.length); + obj[name] = val; + } + }); + return obj; +}; + +/** + * @param {string} val + * @returns {string} + */ +export const html2Text = (val: string) => { + const div = document.createElement('div'); + div.innerHTML = val; + return div.textContent || div.innerText; +}; + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export const objectMerge = (target: any, source: any | any[]) => { + if (typeof target !== 'object') { + target = {}; + } + if (Array.isArray(source)) { + return source.slice(); + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property]; + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty); + } else { + target[property] = sourceProperty; + } + }); + return target; +}; + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export const toggleClass = (element: HTMLElement, className: string) => { + if (!element || !className) { + return; + } + let classString = element.className; + const nameIndex = classString.indexOf(className); + if (nameIndex === -1) { + classString += '' + className; + } else { + classString = classString.substring(0, nameIndex) + classString.substring(nameIndex + className.length); + } + element.className = classString; +}; + +/** + * @param {string} type + * @returns {Date} + */ +export const getTime = (type: string) => { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90; + } else { + return new Date(new Date().toDateString()); + } +}; + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func: (...args: any[]) => any, wait: number, immediate: boolean) { + let timeout: ReturnType | null = null; + let lastArgs: any[] | null = null; + let lastContext: any = null; + let timestamp = 0; + let result: any; + + function later() { + const last = Date.now() - timestamp; + + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(lastContext, lastArgs!); + if (!timeout) lastContext = lastArgs = null; + } + } + } + + return function (this: any, ...args: any[]) { + lastContext = this; + lastArgs = args; + timestamp = Date.now(); + const callNow = immediate && !timeout; + if (!timeout) timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(lastContext, lastArgs); + lastContext = lastArgs = null; + } + return result; + }; +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export const deepClone = (source: any) => { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone' as any); + } + const targetObj: any = source.constructor === Array ? [] : {}; + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]); + } else { + targetObj[keys] = source[keys]; + } + }); + return targetObj; +}; + +/** + * @param {Array} arr + * @returns {Array} + */ +export const uniqueArr = (arr: any) => { + return Array.from(new Set(arr)); +}; + +/** + * @returns {string} + */ +export const createUniqueString = (): string => { + const timestamp = +new Date() + ''; + const num = (1 + Math.random()) * 65536; + const randomNum = parseInt(num + ''); + return (+(randomNum + timestamp)).toString(32); +}; + +/** + * Check if an element has a class + * @param ele + * @param {string} cls + * @returns {boolean} + */ +export const hasClass = (ele: HTMLElement, cls: string): boolean => { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); +}; + +/** + * Add class to element + * @param ele + * @param {string} cls + */ +export const addClass = (ele: HTMLElement, cls: string) => { + if (!hasClass(ele, cls)) ele.className += ' ' + cls; +}; + +/** + * Remove class from element + * @param ele + * @param {string} cls + */ +export const removeClass = (ele: HTMLElement, cls: string) => { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); + ele.className = ele.className.replace(reg, ' '); + } +}; + +/** + * @param {string} path + * @returns {Boolean} + */ +export const isExternal = (path: string) => { + return /^(https?:|http?:|mailto:|tel:)/.test(path); +}; diff --git a/src/utils/jsencrypt.ts b/src/utils/jsencrypt.ts new file mode 100644 index 0000000..e833d3b --- /dev/null +++ b/src/utils/jsencrypt.ts @@ -0,0 +1,22 @@ +import { JSEncrypt } from 'jsencrypt'; + +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = import.meta.env.VITE_APP_RSA_PUBLIC_KEY; + +// 前端不建议存放私钥 不建议解密数据 因为都是透明的意义不大 +const privateKey = import.meta.env.VITE_APP_RSA_PRIVATE_KEY; + +// 加密 +export const encrypt = (txt: string) => { + const encryptor = new JSEncrypt(); + encryptor.setPublicKey(publicKey); // 设置公钥 + return encryptor.encrypt(txt); // 对数据进行加密 +}; + +// 解密 +export const decrypt = (txt: string) => { + const encryptor = new JSEncrypt(); + encryptor.setPrivateKey(privateKey); // 设置私钥 + return encryptor.decrypt(txt); // 对数据进行解密 +}; diff --git a/src/utils/message-read.ts b/src/utils/message-read.ts new file mode 100644 index 0000000..9c070cf --- /dev/null +++ b/src/utils/message-read.ts @@ -0,0 +1,43 @@ +import cache from '@/plugins/cache'; + +const MESSAGE_READ_KEY = 'message_read_ids'; +const MAX_READ_IDS = 300; + +const buildCacheKey = (userId: string | number) => `${MESSAGE_READ_KEY}:${userId}`; + +const normalizeIds = (ids: Array) => { + const values = ids.map(item => String(item)).filter(item => !!item); + return Array.from(new Set(values)).slice(0, MAX_READ_IDS); +}; + +export const getMessageReadIds = (userId: string | number) => { + if (!userId) { + return [] as string[]; + } + const ids = cache.local.getJSON(buildCacheKey(userId)); + return Array.isArray(ids) ? normalizeIds(ids) : []; +}; + +export const isMessageRead = (userId: string | number, messageId?: string | number) => { + if (!userId || messageId === undefined || messageId === null) { + return false; + } + return getMessageReadIds(userId).includes(String(messageId)); +}; + +export const markMessageRead = (userId: string | number, messageId?: string | number) => { + if (!userId || messageId === undefined || messageId === null) { + return []; + } + return markMessageReadBatch(userId, [messageId]); +}; + +export const markMessageReadBatch = (userId: string | number, messageIds: Array) => { + if (!userId) { + return [] as string[]; + } + const current = getMessageReadIds(userId); + const next = normalizeIds([...messageIds, ...current]); + cache.local.setJSON(buildCacheKey(userId), next); + return next; +}; diff --git a/src/utils/ossContent.ts b/src/utils/ossContent.ts new file mode 100644 index 0000000..e8daa36 --- /dev/null +++ b/src/utils/ossContent.ts @@ -0,0 +1,31 @@ +import { listByIds } from '@/api/system/oss'; + +const OSS_MARKER_RE = /oss:\/\/([\w-]+)/g; + +/** + * 将 HTML 中的 oss://{ossId} 标记批量解析为真实的 OSS 授权 URL + * + * 适用于富文本内容展示场景(Editor 组件 / 详情页只读渲染等) + * + * @example + * const html = await resolveOssContent('

'); + */ +export async function resolveOssContent(html: string): Promise { + if (!html) return html; + + const matches = [...html.matchAll(OSS_MARKER_RE)]; + if (matches.length === 0) return html; + + const ossIds = [...new Set(matches.map(m => m[1]))]; + + try { + const res = await listByIds(ossIds.join(',')); + let result = html; + for (const oss of res.data) { + result = result.replaceAll(`oss://${oss.ossId}`, oss.url); + } + return result; + } catch { + return html; + } +} diff --git a/src/utils/permission.ts b/src/utils/permission.ts new file mode 100644 index 0000000..da071c1 --- /dev/null +++ b/src/utils/permission.ts @@ -0,0 +1,51 @@ +import { useUserStore } from '@/store/modules/user'; + +/** + * 字符权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export const checkPermi = (value: any) => { + if (value && value instanceof Array && value.length > 0) { + const permissions = useUserStore().permissions; + const permissionDatas = value; + const all_permission = '*:*:*'; + + const hasPermission = permissions.some(permission => { + return all_permission === permission || permissionDatas.includes(permission); + }); + + if (!hasPermission) { + return false; + } + return true; + } else { + console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`); + return false; + } +}; + +/** + * 角色权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export const checkRole = (value: any): boolean => { + if (value && value instanceof Array && value.length > 0) { + const roles = useUserStore().roles; + const permissionRoles = value; + const super_admin = 'admin'; + + const hasRole = roles.some(role => { + return super_admin === role || permissionRoles.includes(role); + }); + + if (!hasRole) { + return false; + } + return true; + } else { + console.error(`need roles! Like checkRole="['admin','editor']"`); + return false; + } +}; diff --git a/src/utils/propTypes.ts b/src/utils/propTypes.ts new file mode 100644 index 0000000..4a8ca1a --- /dev/null +++ b/src/utils/propTypes.ts @@ -0,0 +1,26 @@ +import type { CSSProperties } from 'vue'; +import VueTypes, { createTypes, toValidableType, type VueTypesInterface, type VueTypeValidableDef } from 'vue-types'; + +type PropTypes = VueTypesInterface & { + readonly style: VueTypeValidableDef; + readonly fieldOption: VueTypeValidableDef>; +}; + +const propTypes = createTypes({ + func: undefined, + bool: undefined, + string: undefined, + number: undefined, + object: undefined, + integer: undefined +}) as PropTypes; + +export default class ProjectTypes extends VueTypes { + static get style() { + return toValidableType('style', { + type: [String, Object], + default: undefined + }); + } +} +export { propTypes }; diff --git a/src/utils/push-message.ts b/src/utils/push-message.ts new file mode 100644 index 0000000..d487584 --- /dev/null +++ b/src/utils/push-message.ts @@ -0,0 +1,82 @@ +export const PUSH_MESSAGE_TYPE = { + MESSAGE: 'message', + NOTICE: 'notice', + LLM: 'llm', + CUSTOM: 'custom' +} as const; + +export const PUSH_MESSAGE_SOURCE = { + BACKEND: 'backend', + NOTICE: 'notice', + WORKFLOW: 'workflow', + LLM: 'llm', + CLIENT: 'client' +} as const; + +export const NOTICE_GROUP = { + SYSTEM: 'system', + NOTICE: 'notice', + WORKFLOW: 'workflow' +} as const; + +export interface PushMessagePayload { + messageId?: string | number; + type?: string; + source?: string; + message?: string; + data?: Record | null; + path?: string; + timestamp?: number; +} + +const MESSAGE_CENTER_TYPES = new Set([PUSH_MESSAGE_TYPE.MESSAGE, PUSH_MESSAGE_TYPE.NOTICE]); + +export const parsePushMessage = (raw: string): PushMessagePayload => { + try { + const payload = JSON.parse(raw) as PushMessagePayload; + return { + type: payload.type ?? PUSH_MESSAGE_TYPE.MESSAGE, + source: payload.source ?? 'backend', + messageId: payload.messageId, + message: payload.message ?? '', + data: payload.data ?? null, + path: payload.path, + timestamp: payload.timestamp ?? Date.now() + }; + } catch { + return { + type: PUSH_MESSAGE_TYPE.MESSAGE, + source: 'backend', + messageId: undefined, + message: raw, + data: null, + path: undefined, + timestamp: Date.now() + }; + } +}; + +export const shouldAppendNotice = (payload: PushMessagePayload) => { + return MESSAGE_CENTER_TYPES.has(payload.type ?? PUSH_MESSAGE_TYPE.MESSAGE); +}; + +export const resolveNoticeGroup = (payload: PushMessagePayload) => { + if (payload.type === PUSH_MESSAGE_TYPE.NOTICE || payload.source === PUSH_MESSAGE_SOURCE.NOTICE) { + return NOTICE_GROUP.NOTICE; + } + if (payload.source === PUSH_MESSAGE_SOURCE.WORKFLOW) { + return NOTICE_GROUP.WORKFLOW; + } + return NOTICE_GROUP.SYSTEM; +}; + +export const resolveNoticeTitle = (payload: PushMessagePayload) => { + const group = resolveNoticeGroup(payload); + if (group === NOTICE_GROUP.NOTICE) { + return '通知公告消息'; + } + if (group === NOTICE_GROUP.WORKFLOW) { + return '工作流消息'; + } + return '系统消息'; +}; diff --git a/src/utils/push.ts b/src/utils/push.ts new file mode 100644 index 0000000..2fef2f5 --- /dev/null +++ b/src/utils/push.ts @@ -0,0 +1,192 @@ +import { ElNotification } from 'element-plus'; +import type { MessageVO } from '@/api/system/message/types'; +import { getMessageBox } from '@/api/system/message'; +import { useNoticeStore } from '@/store/modules/notice'; +import { useUserStore } from '@/store/modules/user'; +import { getToken } from '@/utils/auth'; +import { isMessageRead } from '@/utils/message-read'; +import { parsePushMessage, resolveNoticeGroup, resolveNoticeTitle, shouldAppendNotice } from '@/utils/push-message'; + +let closePushConnection: (() => void) | undefined; +let stopPushWatchers: Array<() => void> = []; +const KICKED_MESSAGE = 'kicked'; +let pushKicked = false; +let resumePushTimer: ReturnType | undefined; + +const formatNoticeTime = (timestamp?: number | string) => { + const time = timestamp ? new Date(timestamp) : new Date(); + return time.toLocaleString(); +}; + +const appendNotice = (raw: string) => { + const payload = parsePushMessage(raw); + if (!shouldAppendNotice(payload)) { + return; + } + const userId = useUserStore().userId; + const title = resolveNoticeTitle(payload); + useNoticeStore().addNotice({ + messageId: payload.messageId, + title, + category: resolveNoticeGroup(payload), + type: payload.type, + source: payload.source, + message: payload.message ?? '', + content: payload.data?.noticeContent, + data: payload.data, + path: payload.path, + read: isMessageRead(userId, payload.messageId), + timestamp: payload.timestamp ?? Date.now(), + time: formatNoticeTime(payload.timestamp) + }); + ElNotification({ + title, + message: payload.message ?? '', + type: 'success', + duration: 3000 + }); +}; + +const handlePushMessage = (raw: string) => { + if (raw === KICKED_MESSAGE) { + pushKicked = true; + closePush(); + return; + } + appendNotice(raw); +}; + +const toNoticeItem = (item: MessageVO) => { + const userId = useUserStore().userId; + const timestamp = item.createTime ? new Date(item.createTime).getTime() : Date.now(); + return { + messageId: item.messageId, + title: item.title, + category: resolveNoticeGroup(item), + type: item.type, + source: item.source, + message: item.message ?? '', + content: item.content, + data: item.data ?? null, + path: item.path, + read: isMessageRead(userId, item.messageId), + timestamp, + time: formatNoticeTime(timestamp) + }; +}; + +const buildSseUrl = (path: string) => { + return `${import.meta.env.VITE_APP_BASE_API}${path}?Authorization=Bearer ${getToken()}&clientid=${import.meta.env.VITE_APP_CLIENT_ID}`; +}; + +const buildWsUrl = (path: string) => { + const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'; + return `${protocol}${window.location.host}${buildSseUrl(path)}`; +}; + +const initSsePush = (url: string) => { + const { data, error, close } = useEventSource(url, [], { + autoReconnect: { + retries: 5, + delay: 5000, + onFailed() { + console.warn('SSE connection failed after 5 retries'); + } + } + }); + closePushConnection = close; + + const stopErrorWatch = watch(error, () => { + console.warn('SSE connection error:', error.value); + error.value = null; + }); + + const stopDataWatch = watch(data, () => { + if (!data.value) return; + handlePushMessage(data.value); + data.value = null; + }); + stopPushWatchers.push(stopErrorWatch, stopDataWatch); +}; + +const initWsPush = (url: string) => { + const { close } = useWebSocket(url, { + autoReconnect: { + retries: 3, + delay: 1000, + onFailed() { + console.warn('websocket重连失败'); + } + }, + heartbeat: { + message: 'ping', + interval: 10000, + pongTimeout: 2000 + }, + onMessage: (_, e) => { + if (String(e.data) === 'pong') { + return; + } + handlePushMessage(String(e.data)); + } + }); + closePushConnection = close; +}; + +export const initPush = () => { + closePush(); + if (import.meta.env.VITE_APP_MESSAGE_ENABLED === 'false') { + return; + } + pushKicked = false; + const path = import.meta.env.VITE_APP_MESSAGE_PATH || '/resource/message'; + const transport = import.meta.env.VITE_APP_MESSAGE_TRANSPORT || 'sse'; + if (transport.toLowerCase() === 'websocket') { + initWsPush(buildWsUrl(path)); + return; + } + initSsePush(buildSseUrl(path)); +}; + +export const initMessageBox = async () => { + if (import.meta.env.VITE_APP_MESSAGE_ENABLED === 'false') { + useNoticeStore().clearNotice(); + return; + } + const { data } = await getMessageBox(); + const notices = [...(data?.systemList ?? []), ...(data?.noticeList ?? []), ...(data?.workflowList ?? [])].map( + toNoticeItem + ); + useNoticeStore().setNotices(notices); +}; + +export const closePush = () => { + closePushConnection?.(); + closePushConnection = undefined; + stopPushWatchers.forEach(stop => stop()); + stopPushWatchers = []; +}; + +const resumePushIfNeeded = () => { + if (!pushKicked || !getToken() || document.visibilityState !== 'visible') { + return; + } + if (resumePushTimer) { + clearTimeout(resumePushTimer); + } + resumePushTimer = setTimeout(async () => { + resumePushTimer = undefined; + if (!pushKicked || !getToken() || document.visibilityState !== 'visible') { + return; + } + try { + await initMessageBox(); + } finally { + initPush(); + } + }, 300); +}; + +window.addEventListener('focus', resumePushIfNeeded); +document.addEventListener('visibilitychange', resumePushIfNeeded); +window.addEventListener('online', resumePushIfNeeded); diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000..9de8455 --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,296 @@ +import type { LoadingInstance } from 'element-plus'; +import axiosModule from 'axios'; +import { HttpStatus } from '@/enums/RespEnum'; +import { getLanguage } from '@/lang'; +import cache from '@/plugins/cache'; +import router from '@/router'; +import { useUserStore } from '@/store/modules/user'; +import { getToken } from '@/utils/auth'; +import { decryptBase64, decryptWithAes, encryptBase64, encryptWithAes, generateAesKey } from '@/utils/crypto'; +import { errorCode } from '@/utils/errorCode'; +import { decrypt, encrypt } from '@/utils/jsencrypt'; +import { blobValidate, tansParams } from '@/utils/ruoyi'; +import { saveBlob } from '@/utils/save'; + +/** axios 1.13 + TS6:默认导出在类型上会被解析为不可调用的 export= 形态 */ +const axios = axiosModule as any; + +const encryptHeader = 'encrypt-key'; +let downloadLoadingInstance: LoadingInstance | undefined; +// 是否显示重新登录 +export const isRelogin = { show: false }; + +function createHandledError(message: string) { + const error = new Error(message) as Error & { isHandled?: boolean }; + error.isHandled = true; + return error; +} + +export function isHandledRequestError(error: unknown) { + return Boolean((error as { isHandled?: boolean } | undefined)?.isHandled); +} + +function normalizeErrorMessage(message?: string) { + if (!message) { + return undefined; + } + if (message === 'Network Error') { + return '后端接口连接异常'; + } + if (message.includes('timeout')) { + return '系统接口请求超时'; + } + if (message.includes('Request failed with status code')) { + return '系统接口' + message.slice(-3) + '异常'; + } + return message; +} + +async function parseResponseErrorData(data: unknown): Promise { + if (!data) { + return undefined; + } + + if (data instanceof Blob) { + return parseResponseErrorData(await data.text()); + } + + if (data instanceof ArrayBuffer) { + return parseResponseErrorData(new TextDecoder().decode(data)); + } + + if (typeof data === 'string') { + const text = data.trim(); + if (!text) { + return undefined; + } + try { + return parseResponseErrorData(JSON.parse(text)); + } catch { + return text; + } + } + + if (typeof data === 'object') { + const payload = data as Record; + return payload.msg || payload.message || errorCode[payload.code] || errorCode['default']; + } + + return undefined; +} + +export async function extractErrorMessage(error: any): Promise { + const responseMessage = await parseResponseErrorData(error?.response?.data); + if (responseMessage) { + return responseMessage; + } + return normalizeErrorMessage(error?.message); +} + +export const globalHeaders = () => { + return { + Authorization: 'Bearer ' + getToken(), + clientid: import.meta.env.VITE_APP_CLIENT_ID + }; +}; + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'; +axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID; +// 创建 axios 实例 +const service = axios.create({ + baseURL: import.meta.env.VITE_APP_BASE_API, + timeout: 50000, + transitional: { + // 超时错误更明确 + clarifyTimeoutError: true + } +}); + +// 请求拦截器 +service.interceptors.request.use( + (config: any) => { + // 对应国际化资源文件后缀 + config.headers['Content-Language'] = getLanguage(); + + const isToken = config.headers?.isToken === false; + // 是否需要防止数据重复提交 + const isRepeatSubmit = config.headers?.repeatSubmit === false; + // 是否需要加密 + const isEncrypt = config.headers?.isEncrypt === 'true'; + + if (getToken() && !isToken) { + config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 + } + // get请求映射params参数 + if (config.method === 'get' && config.params) { + let url = config.url + '?' + tansParams(config.params); + url = url.slice(0, -1); + config.params = {}; + config.url = url; + } + + if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { + const requestObj = { + url: config.url, + data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + time: new Date().getTime() + }; + const sessionObj = cache.session.getJSON('sessionObj'); + if (sessionObj === undefined || sessionObj === null || sessionObj === '') { + cache.session.setJSON('sessionObj', requestObj); + } else { + const s_url = sessionObj.url; // 请求地址 + const s_data = sessionObj.data; // 请求数据 + const s_time = sessionObj.time; // 请求时间 + const interval = 500; // 间隔时间(ms),小于此时间视为重复提交 + if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { + const message = '数据正在处理,请勿重复提交'; + console.warn(`[${s_url}]: ` + message); + return Promise.reject(new Error(message)); + } else { + cache.session.setJSON('sessionObj', requestObj); + } + } + } + if (import.meta.env.VITE_APP_ENCRYPT === 'true') { + // 当开启参数加密 + if (isEncrypt && (config.method === 'post' || config.method === 'put')) { + // 生成一个 AES 密钥 + const aesKey = generateAesKey(); + config.headers[encryptHeader] = encrypt(encryptBase64(aesKey)); + config.data = + typeof config.data === 'object' + ? encryptWithAes(JSON.stringify(config.data), aesKey) + : encryptWithAes(config.data, aesKey); + } + } + // FormData数据去请求头Content-Type + if (config.data instanceof FormData) { + delete config.headers['Content-Type']; + } + return config; + }, + (error: any) => { + return Promise.reject(error); + } +); + +// 响应拦截器 +service.interceptors.response.use( + (res: any) => { + if (import.meta.env.VITE_APP_ENCRYPT === 'true') { + // 加密后的 AES 秘钥 + const keyStr = res.headers[encryptHeader]; + // 加密 + if (keyStr != null && keyStr != '') { + const data = res.data; + // 请求体 AES 解密 + const base64Str = decrypt(keyStr); + // base64 解码 得到请求头的 AES 秘钥 + const aesKey = decryptBase64(base64Str.toString()); + // aesKey 解码 data + const decryptData = decryptWithAes(data, aesKey); + // 将结果 (得到的是 JSON 字符串) 转为 JSON + res.data = JSON.parse(decryptData); + } + } + // 未设置状态码则默认成功状态 + const code = res.data.code || HttpStatus.SUCCESS; + // 获取错误信息 + const msg = res.data.msg || errorCode[code] || errorCode['default']; + // 二进制数据则直接返回 + if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { + return res.data; + } + if (code === 401) { + // prettier-ignore + if (!isRelogin.show) { + isRelogin.show = true; + ElMessageBox.confirm( + "登录状态已过期,您可以继续留在该页面,或者重新登录", + "系统提示", + { + confirmButtonText: "重新登录", + cancelButtonText: "取消", + type: "warning", + }, + ) + .then(() => { + isRelogin.show = false; + useUserStore() + .logout() + .then(() => { + router.replace({ + path: "/login", + query: { + redirect: encodeURIComponent( + router.currentRoute.value.fullPath || "/", + ), + }, + }); + }); + }) + .catch(() => { + isRelogin.show = false; + }); + } + return Promise.reject('无效的会话,或者会话已过期,请重新登录。'); + } else if (code === HttpStatus.SERVER_ERROR) { + ElMessage({ message: msg, type: 'error' }); + return Promise.reject(createHandledError(msg)); + } else if (code === HttpStatus.WARN) { + ElMessage({ message: msg, type: 'warning' }); + return Promise.reject(createHandledError(msg)); + } else if (code !== HttpStatus.SUCCESS) { + ElNotification.error({ title: msg }); + return Promise.reject(createHandledError(msg)); + } else { + return Promise.resolve(res.data); + } + }, + async (error: any) => { + const message = (await extractErrorMessage(error)) || errorCode['default']; + ElMessage({ message: message, type: 'error', duration: 5 * 1000 }); + error.isHandled = true; + return Promise.reject(error); + } +); +// 通用下载方法 +export function download(url: string, params: any, fileName: string) { + downloadLoadingInstance = ElLoading.service({ + text: '正在下载数据,请稍候', + background: 'rgba(0, 0, 0, 0.7)' + }); + // prettier-ignore + return service + .post(url, params, { + transformRequest: [ + (params: any) => { + return tansParams(params); + }, + ], + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + responseType: "blob", + }) + .then(async (resp: any) => { + const isLogin = blobValidate(resp); + if (isLogin) { + const blob = new Blob([resp]); + saveBlob(blob, fileName); + } else { + const blob = new Blob([resp]); + const resText = await blob.text(); + const rspObj = JSON.parse(resText); + const errMsg = + errorCode[rspObj.code] || rspObj.msg || errorCode["default"]; + ElMessage.error(errMsg); + } + downloadLoadingInstance?.close(); + }) + .catch((r: any) => { + console.error(r); + downloadLoadingInstance?.close(); + }); +} +// 导出 axios 实例 +export default service; diff --git a/src/utils/ruoyi.ts b/src/utils/ruoyi.ts new file mode 100644 index 0000000..36a91be --- /dev/null +++ b/src/utils/ruoyi.ts @@ -0,0 +1,237 @@ +// 日期格式化 +export function parseTime(time: any, pattern?: string) { + if (arguments.length === 0 || !time) { + return null; + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'; + let date; + if (typeof time === 'object') { + date = time; + } else { + if (typeof time === 'string' && /^[0-9]+$/.test(time)) { + time = parseInt(time); + } else if (typeof time === 'string') { + time = time + .replace(new RegExp(/-/gm), '/') + .replace('T', ' ') + .replace(new RegExp(/\.[\d]{3}/gm), ''); + } + if (typeof time === 'number' && time.toString().length === 10) { + time = time * 1000; + } + date = new Date(time); + } + const formatObj: { [key: string]: any } = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + }; + return format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: string, key: string) => { + let value = formatObj[key]; + // Note: getDay() returns 0 on Sunday + if (key === 'a') { + return ['日', '一', '二', '三', '四', '五', '六'][value]; + } + if (result.length > 0 && value < 10) { + value = '0' + value; + } + return value || 0; + }); +} + +/** + * 添加日期范围 + * @param params + * @param dateRange + * @param propName + */ +export const addDateRange = (params: any, dateRange: any[], propName?: string) => { + const search = params; + search.params = + typeof search.params === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {}; + dateRange = Array.isArray(dateRange) ? dateRange : []; + if (typeof propName === 'undefined') { + search.params['beginTime'] = dateRange[0]; + search.params['endTime'] = dateRange[1]; + } else { + search.params['begin' + propName] = dateRange[0]; + search.params['end' + propName] = dateRange[1]; + } + return search; +}; + +// 回显数据字典 +export const selectDictLabel = (datas: any, value: number | string) => { + if (value === undefined) { + return ''; + } + const actions: Array = []; + Object.keys(datas).some(key => { + if (datas[key].value == '' + value) { + actions.push(datas[key].label); + return true; + } + }); + if (actions.length === 0) { + actions.push(value); + } + return actions.join(''); +}; + +// 回显数据字典(字符串数组) +export const selectDictLabels = (datas: any, value: any, separator: any) => { + if (value === undefined || value.length === 0) { + return ''; + } + if (Array.isArray(value)) { + value = value.join(','); + } + const actions: any[] = []; + const currentSeparator = undefined === separator ? ',' : separator; + const temp = value.split(currentSeparator); + Object.keys(value.split(currentSeparator)).some(val => { + let match = false; + Object.keys(datas).some(key => { + if (datas[key].value == '' + temp[val]) { + actions.push(datas[key].label + currentSeparator); + match = true; + } + }); + if (!match) { + actions.push(temp[val] + currentSeparator); + } + }); + return actions.join('').substring(0, actions.join('').length - 1); +}; + +// 字符串格式化(%s ) +export function sprintf(str: string) { + if (arguments.length !== 0) { + let flag = true, + i = 1; + str = str.replace(/%s/g, function () { + const arg = arguments[i++]; + if (typeof arg === 'undefined') { + flag = false; + return ''; + } + return arg; + }); + return flag ? str : ''; + } +} + +// 转换字符串,undefined,null等转化为"" +export const parseStrEmpty = (str: any) => { + if (!str || str == 'undefined' || str == 'null') { + return ''; + } + return str; +}; + +// 数据合并 +export const mergeRecursive = (source: any, target: any) => { + for (const p in target) { + try { + if (target[p].constructor == Object) { + source[p] = mergeRecursive(source[p], target[p]); + } else { + source[p] = target[p]; + } + } catch (e) { + source[p] = target[p]; + } + } + return source; +}; + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export const handleTree = (data: any[], id?: string, parentId?: string, children?: string): T[] => { + const config: { + id: string; + parentId: string; + childrenList: string; + } = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + }; + + const childrenListMap: any = {}; + const tree: T[] = []; + for (const d of data) { + const id = d[config.id]; + childrenListMap[id] = d; + if (!d[config.childrenList]) { + d[config.childrenList] = []; + } + } + + for (const d of data) { + const parentId = d[config.parentId]; + const parentObj = childrenListMap[parentId]; + if (!parentObj) { + tree.push(d); + } else { + parentObj[config.childrenList].push(d); + } + } + return tree; +}; + +/** + * 参数处理 + * @param {*} params 参数 + */ +export const tansParams = (params: any) => { + let result = ''; + for (const propName of Object.keys(params)) { + const value = params[propName]; + const part = encodeURIComponent(propName) + '='; + if (value !== null && value !== '' && typeof value !== 'undefined') { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') { + const params = propName + '[' + key + ']'; + const subPart = encodeURIComponent(params) + '='; + result += subPart + encodeURIComponent(value[key]) + '&'; + } + } + } else { + result += part + encodeURIComponent(value) + '&'; + } + } + } + return result; +}; + +// 返回项目路径 +export const getNormalPath = (p: string): string => { + if (p.length === 0 || !p || p === 'undefined') { + return p; + } + const res = p.replace('//', '/'); + if (res[res.length - 1] === '/') { + return res.slice(0, res.length - 1); + } + return res; +}; + +// 验证是否为blob格式 +export const blobValidate = (data: any) => { + return data.type !== 'application/json'; +}; + +export default { + handleTree +}; diff --git a/src/utils/sanitize.ts b/src/utils/sanitize.ts new file mode 100644 index 0000000..49515db --- /dev/null +++ b/src/utils/sanitize.ts @@ -0,0 +1,214 @@ +const DEFAULT_ALLOWED_TAGS = new Set([ + 'a', + 'article', + 'b', + 'blockquote', + 'br', + 'caption', + 'code', + 'col', + 'colgroup', + 'del', + 'div', + 'em', + 'figcaption', + 'figure', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'hr', + 'i', + 'img', + 'li', + 'ol', + 'p', + 'pre', + 's', + 'section', + 'span', + 'strong', + 'sub', + 'sup', + 'table', + 'tbody', + 'td', + 'th', + 'thead', + 'tr', + 'u', + 'ul', + 'video', + 'source' +]); + +const URI_ATTRS = new Set(['href', 'src']); +const GLOBAL_ALLOWED_ATTRS = new Set(['class', 'title', 'alt', 'style']); +const TAG_ALLOWED_ATTRS: Record> = { + a: new Set(['href', 'target', 'rel']), + img: new Set(['src', 'alt', 'title']), + video: new Set(['src', 'poster', 'controls', 'autoplay', 'muted', 'loop']), + source: new Set(['src', 'type']), + td: new Set(['colspan', 'rowspan']), + th: new Set(['colspan', 'rowspan']), + col: new Set(['span']), + colgroup: new Set(['span']) +}; + +const DANGEROUS_TAGS = new Set([ + 'script', + 'style', + 'iframe', + 'object', + 'embed', + 'link', + 'meta', + 'base', + 'form', + 'input', + 'button', + 'textarea', + 'select', + 'option', + 'svg', + 'math' +]); +const SAFE_URL_PATTERN = /^(https?:|mailto:|tel:|\/|#|data:image\/(?:png|jpeg|jpg|gif|webp);base64,)/i; +const ALLOWED_STYLE_PROPS = new Set([ + 'background-color', + 'color', + 'font-family', + 'font-size', + 'font-style', + 'font-weight', + 'height', + 'line-height', + 'list-style-type', + 'margin-left', + 'max-width', + 'min-width', + 'padding-left', + 'text-align', + 'text-decoration', + 'text-indent', + 'vertical-align', + 'white-space', + 'width' +]); +const UNSAFE_STYLE_VALUE_PATTERN = /(?:expression\s*\(|url\s*\(|javascript:|vbscript:|data:|@import|behavior:)/i; + +const sanitizeUrl = (value: string) => { + const normalized = value.trim(); + if (!normalized) { + return ''; + } + return SAFE_URL_PATTERN.test(normalized) ? normalized : ''; +}; + +const sanitizeStyle = (value: string) => { + const declarations = value + .split(';') + .map(item => item.trim()) + .filter(Boolean); + + const safeDeclarations = declarations.flatMap(declaration => { + const separatorIndex = declaration.indexOf(':'); + if (separatorIndex <= 0) { + return []; + } + + const property = declaration.slice(0, separatorIndex).trim().toLowerCase(); + const propertyValue = declaration.slice(separatorIndex + 1).trim(); + + if (!ALLOWED_STYLE_PROPS.has(property) || !propertyValue || UNSAFE_STYLE_VALUE_PATTERN.test(propertyValue)) { + return []; + } + + return [`${property}: ${propertyValue}`]; + }); + + return safeDeclarations.join('; '); +}; + +const shouldKeepAttr = (tagName: string, attrName: string) => { + if (attrName.startsWith('on')) { + return false; + } + if (GLOBAL_ALLOWED_ATTRS.has(attrName)) { + return true; + } + return TAG_ALLOWED_ATTRS[tagName]?.has(attrName) ?? false; +}; + +const sanitizeElement = (element: Element) => { + const tagName = element.tagName.toLowerCase(); + + if (DANGEROUS_TAGS.has(tagName)) { + element.remove(); + return; + } + + if (!DEFAULT_ALLOWED_TAGS.has(tagName)) { + const parent = element.parentNode; + if (!parent) { + element.remove(); + return; + } + while (element.firstChild) { + parent.insertBefore(element.firstChild, element); + } + parent.removeChild(element); + return; + } + + Array.from(element.attributes).forEach(attr => { + const attrName = attr.name.toLowerCase(); + if (!shouldKeepAttr(tagName, attrName)) { + element.removeAttribute(attr.name); + return; + } + + if (attrName === 'style') { + const sanitizedStyle = sanitizeStyle(attr.value); + if (!sanitizedStyle) { + element.removeAttribute(attr.name); + return; + } + element.setAttribute(attr.name, sanitizedStyle); + return; + } + + if (URI_ATTRS.has(attrName)) { + const sanitized = sanitizeUrl(attr.value); + if (!sanitized) { + element.removeAttribute(attr.name); + return; + } + element.setAttribute(attr.name, sanitized); + } + }); + + if (tagName === 'a' && element.hasAttribute('target')) { + element.setAttribute('rel', 'noopener noreferrer'); + } +}; + +export function sanitizeHtml(html?: string) { + if (!html) { + return ''; + } + + const template = document.createElement('template'); + template.innerHTML = html; + const walker = document.createTreeWalker(template.content, NodeFilter.SHOW_ELEMENT); + const elements: Element[] = []; + + while (walker.nextNode()) { + elements.push(walker.currentNode as Element); + } + + elements.forEach(sanitizeElement); + return template.innerHTML; +} diff --git a/src/utils/save.ts b/src/utils/save.ts new file mode 100644 index 0000000..6cb0502 --- /dev/null +++ b/src/utils/save.ts @@ -0,0 +1,13 @@ +export function saveBlob(blob: Blob, fileName: string) { + const downloadLink = document.createElement('a'); + const objectUrl = URL.createObjectURL(blob); + + downloadLink.href = objectUrl; + downloadLink.download = fileName; + downloadLink.rel = 'noopener'; + downloadLink.click(); + + setTimeout(() => { + URL.revokeObjectURL(objectUrl); + }, 40_000); +} diff --git a/src/utils/scroll-to.ts b/src/utils/scroll-to.ts new file mode 100644 index 0000000..f89af39 --- /dev/null +++ b/src/utils/scroll-to.ts @@ -0,0 +1,64 @@ +const easeInOutQuad = (t: number, b: number, c: number, d: number) => { + t /= d / 2; + if (t < 1) { + return (c / 2) * t * t + b; + } + t--; + return (-c / 2) * (t * (t - 2) - 1) + b; +}; + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +const requestAnimFrame = (() => + window.requestAnimationFrame || + (window as any).webkitRequestAnimationFrame || + (window as any).mozRequestAnimationFrame || + (callback => { + window.setTimeout(callback, 1000 / 60); + }))(); + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +const move = (amount: number) => { + document.documentElement.scrollTop = amount; + (document.body.parentNode as HTMLElement).scrollTop = amount; + document.body.scrollTop = amount; +}; + +const position = () => { + return ( + document.documentElement.scrollTop || (document.body.parentNode as HTMLElement).scrollTop || document.body.scrollTop + ); +}; + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export const scrollTo = (to: number, duration: number, callback?: any) => { + const start = position(); + const change = to - start; + const increment = 20; + let currentTime = 0; + duration = typeof duration === 'undefined' ? 500 : duration; + const animateScroll = () => { + // increment the time + currentTime += increment; + // find the value with the quadratic in-out easing function + const val = easeInOutQuad(currentTime, start, change, duration); + // move the document.body + move(val); + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll); + } else { + if (callback && typeof callback === 'function') { + // the animation is done so lets callback + callback(); + } + } + }; + animateScroll(); +}; diff --git a/src/utils/theme.ts b/src/utils/theme.ts new file mode 100644 index 0000000..3936248 --- /dev/null +++ b/src/utils/theme.ts @@ -0,0 +1,52 @@ +// 处理主题样式 +export const handleThemeStyle = (theme: string) => { + 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)}`); + } + for (let i = 1; i <= 9; i++) { + document.documentElement.style.setProperty(`--el-color-primary-dark-${i}`, `${getDarkColor(theme, i / 10)}`); + } +}; + +// hex颜色转rgb颜色 +export const hexToRgb = (str: string): string[] => { + str = str.replace('#', ''); + const hexs = str.match(/../g); + for (let i = 0; i < 3; i++) { + if (hexs) { + hexs[i] = String(parseInt(hexs[i], 16)); + } + } + return hexs ? hexs : []; +}; + +// rgb颜色转Hex颜色 +export const rgbToHex = (r: string, g: string, b: string) => { + const hexs = [Number(r).toString(16), Number(g).toString(16), Number(b).toString(16)]; + for (let i = 0; i < 3; i++) { + if (hexs[i].length == 1) { + hexs[i] = `0${hexs[i]}`; + } + } + return `#${hexs.join('')}`; +}; + +// 变浅颜色值 +export const getLightColor = (color: string, level: number) => { + const rgb = hexToRgb(color); + for (let i = 0; i < 3; i++) { + const s = (255 - Number(rgb[i])) * level + Number(rgb[i]); + rgb[i] = String(Math.floor(s)); + } + return rgbToHex(rgb[0], rgb[1], rgb[2]); +}; + +// 变深颜色值 +export const getDarkColor = (color: string, level: number) => { + const rgb = hexToRgb(color); + for (let i = 0; i < 3; i++) { + rgb[i] = String(Math.floor(Number(rgb[i]) * (1 - level))); + } + return rgbToHex(rgb[0], rgb[1], rgb[2]); +}; diff --git a/src/utils/validate.ts b/src/utils/validate.ts new file mode 100644 index 0000000..d673bfd --- /dev/null +++ b/src/utils/validate.ts @@ -0,0 +1,109 @@ +/** + * 路径匹配器 + * @param {string} pattern + * @param {string} path + * @returns {Boolean} + */ +export function isPathMatch(pattern: string, path: string) { + const regexPattern = pattern + .replace(/([.+^${}()|[\]\\])/g, '\\$1') + .replace(/\*\*/g, '__DOUBLE_STAR__') + .replace(/\*/g, '[^/]*') + .replace(/\?/g, '[^/]') + .replace(/__DOUBLE_STAR__/g, '.*'); + const regex = new RegExp(`^${regexPattern}$`); + return regex.test(path); +} + +/** + * 判断url是否是http或https + * @returns {Boolean} + * @param url + */ +export const isHttp = (url: string): boolean => { + return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1; +}; + +/** + * 判断path是否为外链 + * @param {string} path + * @returns {Boolean} + */ +export const isExternal = (path: string) => { + return /^(https?:|mailto:|tel:)/.test(path); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const validUsername = (str: string) => { + const valid_map = ['admin', 'editor']; + return valid_map.indexOf(str.trim()) >= 0; +}; + +/** + * @param {string} url + * @returns {Boolean} + */ +export const validURL = (url: string) => { + const reg = + /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/; + return reg.test(url); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const validLowerCase = (str: string) => { + const reg = /^[a-z]+$/; + return reg.test(str); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const validUpperCase = (str: string) => { + const reg = /^[A-Z]+$/; + return reg.test(str); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const validAlphabets = (str: string) => { + const reg = /^[A-Za-z]+$/; + return reg.test(str); +}; + +/** + * @param {string} email + * @returns {Boolean} + */ +export const validEmail = (email: string) => { + const reg = + /^(([^<>()\]\\.,;:\s@"]+(\.[^<>()\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return reg.test(email); +}; + +/** + * @param {string} str + * @returns {Boolean} + */ +export const isString = (str: any) => { + return typeof str === 'string' || str instanceof String; +}; + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export const isArray = (arg: string | string[]) => { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]'; + } + return Array.isArray(arg); +}; diff --git a/src/views/ai/chat/index.vue b/src/views/ai/chat/index.vue new file mode 100644 index 0000000..07c8f7a --- /dev/null +++ b/src/views/ai/chat/index.vue @@ -0,0 +1,81 @@ + + + + + + + diff --git a/src/views/workflow/processDefinition/index.vue b/src/views/workflow/processDefinition/index.vue new file mode 100644 index 0000000..63764de --- /dev/null +++ b/src/views/workflow/processDefinition/index.vue @@ -0,0 +1,740 @@ + + + + + diff --git a/src/views/workflow/processInstance/index.vue b/src/views/workflow/processInstance/index.vue new file mode 100644 index 0000000..e49c061 --- /dev/null +++ b/src/views/workflow/processInstance/index.vue @@ -0,0 +1,590 @@ + + + + + diff --git a/src/views/workflow/spel/index.vue b/src/views/workflow/spel/index.vue new file mode 100644 index 0000000..5fc1caa --- /dev/null +++ b/src/views/workflow/spel/index.vue @@ -0,0 +1,404 @@ + + + + diff --git a/src/views/workflow/task/allTaskWaiting.vue b/src/views/workflow/task/allTaskWaiting.vue new file mode 100644 index 0000000..5578816 --- /dev/null +++ b/src/views/workflow/task/allTaskWaiting.vue @@ -0,0 +1,389 @@ + + + + + diff --git a/src/views/workflow/task/myDocument.vue b/src/views/workflow/task/myDocument.vue new file mode 100644 index 0000000..4fa83cf --- /dev/null +++ b/src/views/workflow/task/myDocument.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/src/views/workflow/task/taskCopyList.vue b/src/views/workflow/task/taskCopyList.vue new file mode 100644 index 0000000..de023c1 --- /dev/null +++ b/src/views/workflow/task/taskCopyList.vue @@ -0,0 +1,161 @@ + + + diff --git a/src/views/workflow/task/taskFinish.vue b/src/views/workflow/task/taskFinish.vue new file mode 100644 index 0000000..6878e0b --- /dev/null +++ b/src/views/workflow/task/taskFinish.vue @@ -0,0 +1,226 @@ + + + diff --git a/src/views/workflow/task/taskWaiting.vue b/src/views/workflow/task/taskWaiting.vue new file mode 100644 index 0000000..b545fff --- /dev/null +++ b/src/views/workflow/task/taskWaiting.vue @@ -0,0 +1,208 @@ + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..9c3fdc1 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ESNext", "DOM"], + "skipLibCheck": true, + // This setting lets you specify a file for storing incremental compilation information as a part of composite projects which enables faster building of larger TypeScript codebases. + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo", + "strict": true, + "allowJs": true, + "jsx": "preserve", + "sourceMap": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "noEmit": true, + "paths": { + "@/*": ["./src/*"] + }, + "types": ["node", "vite/client"], + // 自定义配置 + "noImplicitAny": false, // 禁用 隐式的any错误 + "removeComments": true, // 移除 ts注释 + "experimentalDecorators": true, // 启用实验性的装饰器支持 + "strictFunctionTypes": false, // 禁用严格函数类型检查 + "strictNullChecks": false, // 禁用严格的空值检查 + "allowSyntheticDefaultImports": true, // 允许默认导入 + "forceConsistentCasingInFileNames": true // 强制在文件名中使用一致的大小写 + }, + "include": ["src/**/*.ts", "src/**/*.vue", "vite.config.ts", "vitest.config.ts", "src/**/*.d.ts"], + "exclude": ["node_modules", "dist", "src/**/__tests__/*"] +} diff --git a/uno.config.ts b/uno.config.ts new file mode 100644 index 0000000..f7bc7de --- /dev/null +++ b/uno.config.ts @@ -0,0 +1,31 @@ +import { + defineConfig, + presetAttributify, + presetWind3, + presetTypography, + presetWebFonts, + transformerDirectives, + transformerVariantGroup +} from 'unocss'; + +export default defineConfig({ + shortcuts: { + 'panel-title': + 'pb-[5px] font-sans leading-[1.1] font-medium text-base text-[#6379bb] border-b border-b-solid border-[var(--el-border-color-light)] mb-5 mt-0' + }, + theme: { + colors: { + primary: 'var(--el-color-primary)', + primary_dark: 'var(--el-color-primary-light-5)' + } + }, + presets: [ + presetWind3(), + presetAttributify(), + presetTypography(), + presetWebFonts({ + fonts: {} + }) + ], + transformers: [transformerDirectives(), transformerVariantGroup()] +}); diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..4c167e6 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,63 @@ +import { defineConfig, loadEnv } from 'vite'; +import createPlugins from './vite/plugins/index.js'; +import autoprefixer from 'autoprefixer'; // css自动添加兼容性前缀 + +export default defineConfig(({ mode, command }) => { + const env = loadEnv(mode, process.cwd()); + return { + // 部署生产环境和开发环境下的URL。 + // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上 + // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。 + base: env.VITE_APP_CONTEXT_PATH, + resolve: { + tsconfigPaths: true, + extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'] + }, + // https://cn.vitejs.dev/config/#resolve-extensions + plugins: createPlugins(env, command === 'build'), + build: { + chunkSizeWarningLimit: 1500, + rolldownOptions: { + checks: { + invalidAnnotation: false, + pluginTimings: false + } + } + }, + server: { + host: '0.0.0.0', + port: Number(env.VITE_APP_PORT), + open: true, + proxy: { + [env.VITE_APP_BASE_API]: { + target: 'http://localhost:8080', + changeOrigin: true, + ws: true, + rewrite: path => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '') + } + } + }, + css: { + preprocessorOptions: { + scss: { + // additionalData: '@use "@/assets/styles/variables.module.scss as *";' + // javascriptEnabled: true + } + }, + postcss: { + plugins: [ + // 浏览器兼容性 + autoprefixer(), + { + postcssPlugin: 'internal:charset-removal', + AtRule: { + charset: atRule => { + atRule.remove(); + } + } + } + ] + } + } + }; +}); diff --git a/vite/plugins/auto-import.ts b/vite/plugins/auto-import.ts new file mode 100644 index 0000000..5b4d1a2 --- /dev/null +++ b/vite/plugins/auto-import.ts @@ -0,0 +1,18 @@ +import AutoImport from 'unplugin-auto-import/vite'; +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; +import { resolve } from 'path'; + +export default () => { + return AutoImport({ + // 自动导入 Vue 相关函数 + imports: ['vue', 'vue-router', '@vueuse/core', 'pinia'], + resolvers: [ + // 自动导入 Element Plus 相关函数ElMessage, ElMessageBox... (带样式) + ElementPlusResolver({ + importStyle: false + }) + ], + vueTemplate: true, // 是否在 vue 模板中自动导入 + dts: resolve(import.meta.dirname, '../../src/types/auto-imports.d.ts') + }); +}; diff --git a/vite/plugins/check-transition.ts b/vite/plugins/check-transition.ts new file mode 100644 index 0000000..5e54af8 --- /dev/null +++ b/vite/plugins/check-transition.ts @@ -0,0 +1,117 @@ +import type { PluginOption } from 'vite'; + +import { normalizePath } from 'vite'; +import { parse } from 'vue/compiler-sfc'; + +/** 模板 AST 元素节点类型 */ +const NODE_TYPE_ELEMENT = 1; +/** 模板 AST 注释节点类型 */ +const NODE_TYPE_COMMENT = 3; +/** 模板 AST 指令属性类型 */ +const NODE_TYPE_DIRECTIVE = 7; + +/** + * 判断节点是否为元素节点 + */ +function isElement(node: any): boolean { + return node?.type === NODE_TYPE_ELEMENT; +} + +/** + * 判断节点是否为注释节点 + * @description 注释节点同样是 transition 的一个子节点,会导致多节点 + */ +function isComment(node: any): boolean { + return node?.type === NODE_TYPE_COMMENT; +} + +/** + * 节点用于报错展示的标签文本 + */ +function nodeLabel(node: any): string { + return isComment(node) ? '' : `<${node.tag}>`; +} + +/** + * 判断元素是否带有指定指令(如 v-if / v-else-if / v-else) + */ +function hasDirective(node: any, name: string): boolean { + return (node.props || []).some( + (prop: any) => prop.type === NODE_TYPE_DIRECTIVE && prop.name === name, + ); +} + +/** + * 判断多个元素子节点是否构成合法的 v-if / v-else-if / v-else 分支链 + * @description 分支链任意时刻只渲染一个节点,对外层 transition 是合法的 + */ +function isConditionalChain(elements: any[]): boolean { + // 首个节点必须是 v-if,后续节点必须都是 v-else-if 或 v-else + if (!hasDirective(elements[0], 'if')) { + return false; + } + return elements + .slice(1) + .every((el) => hasDirective(el, 'else-if') || hasDirective(el, 'else')); +} + +/** + * 检测页面模板根节点是否存在多标签的 vite 插件 + * @description 外层路由/页面切换已用 包裹,每个页面即为 transition 的子节点。 + * Vue 的 只能管理单个子节点,因此页面模板出现多个根节点会导致过渡失效。 + * 该插件在编译 src/views 下的页面时检测此类写法并直接报错。 + */ +export const viteCheckTransitionPlugin = (): PluginOption => { + // 仅检测 views 目录下的页面 + const targetDir = normalizePath('/src/views/'); + + return { + enforce: 'pre', + name: 'vite:check-transition', + transform(code, id) { + const normalizedId = normalizePath(id); + + // 跳过非 views 下的文件、非 .vue 文件以及带 query 的子请求 + if ( + normalizedId.includes('?') || + !normalizedId.endsWith('.vue') || + !normalizedId.includes(targetDir) + ) { + return null; + } + + const { descriptor, errors: parseErrors } = parse(code, { + filename: id, + }); + + // 解析失败交由 vue 插件处理,这里不重复报错 + if (parseErrors.length > 0 || !descriptor.template?.ast) { + return null; + } + + // 统计根级元素节点与注释节点,二者都是 transition 的子节点,忽略空白文本 + const rootNodes = (descriptor.template.ast.children || []).filter( + (child: any) => isElement(child) || isComment(child), + ); + + // 仅元素节点参与 v-if 分支链判断(注释不属于分支链) + const rootElements = rootNodes.filter((node: any) => isElement(node)); + const isChain = + rootNodes.length === rootElements.length && + isConditionalChain(rootElements); + + if (rootNodes.length > 1 && !isChain) { + const relativePath = normalizedId.slice( + normalizedId.indexOf('/src/') + 1, + ); + const tags = rootNodes.map((node: any) => nodeLabel(node)).join(' '); + + throw new Error( + `[check-transition] ${relativePath} 存在多个根节点: ${tags}\n 页面外层已包裹 ,模板必须只有单个根节点,请用一个容器元素(如 div)包裹,并删除多余的注释/空标签。`, + ); + } + + return null; + }, + }; +}; diff --git a/vite/plugins/components.ts b/vite/plugins/components.ts new file mode 100644 index 0000000..36bcda6 --- /dev/null +++ b/vite/plugins/components.ts @@ -0,0 +1,15 @@ +import Components from 'unplugin-vue-components/vite'; +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'; +import { resolve } from 'path'; + +export default () => { + return Components({ + resolvers: [ + // 自动导入 Element Plus 组件 + ElementPlusResolver({ + importStyle: false + }) + ], + dts: resolve(import.meta.dirname, '../../src/types/components.d.ts') + }); +}; diff --git a/vite/plugins/compression.ts b/vite/plugins/compression.ts new file mode 100644 index 0000000..8305a52 --- /dev/null +++ b/vite/plugins/compression.ts @@ -0,0 +1,112 @@ +import { promises as fs } from 'fs'; +import path from 'path'; +import zlib from 'zlib'; +import { promisify } from 'util'; +import type { Plugin, ResolvedConfig } from 'vite'; + +const gzip = promisify(zlib.gzip); +const brotliCompress = promisify(zlib.brotliCompress); +const compressibleFileRE = /\.(js|mjs|json|css|html)$/i; +const defaultThreshold = 1025; + +type CompressionKind = 'gzip' | 'brotli'; + +const compressionHandlers: Record Promise }> = { + gzip: { + ext: '.gz', + compress: content => gzip(content, { level: zlib.constants.Z_BEST_COMPRESSION }) + }, + brotli: { + ext: '.br', + compress: content => + brotliCompress(content, { + params: { + [zlib.constants.BROTLI_PARAM_QUALITY]: zlib.constants.BROTLI_MAX_QUALITY, + [zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT + } + }) + } +}; + +async function collectFiles(rootDir: string): Promise { + const entries = await fs.readdir(rootDir, { withFileTypes: true }); + const files = await Promise.all( + entries.map(async entry => { + const fullPath = path.join(rootDir, entry.name); + if (entry.isDirectory()) { + return collectFiles(fullPath); + } + return compressibleFileRE.test(entry.name) ? [fullPath] : []; + }) + ); + return files.flat(); +} + +function createCompressionPlugin(kind: CompressionKind): Plugin { + const handler = compressionHandlers[kind]; + let config: ResolvedConfig | undefined; + + return { + name: `local:compression:${kind}`, + apply: 'build', + enforce: 'post', + configResolved(resolvedConfig) { + config = resolvedConfig; + }, + async closeBundle() { + const outputDir = path.resolve(process.cwd(), config?.build.outDir ?? 'dist'); + const files = await collectFiles(outputDir); + const compressedEntries: Array<{ file: string; originalKb: string; compressedKb: string }> = []; + + await Promise.all( + files.map(async filePath => { + const stat = await fs.stat(filePath); + if (stat.size < defaultThreshold) { + return; + } + + const content = await fs.readFile(filePath); + const compressed = await handler.compress(content); + const outputFile = `${filePath}${handler.ext}`; + + await fs.writeFile(outputFile, compressed); + compressedEntries.push({ + file: path.relative(outputDir, outputFile).replaceAll('\\', '/'), + originalKb: (stat.size / 1024).toFixed(2), + compressedKb: (compressed.byteLength / 1024).toFixed(2) + }); + }) + ); + + if (!compressedEntries.length) { + return; + } + + compressedEntries.sort((a, b) => a.file.localeCompare(b.file)); + config?.logger.info(`\n[compression:${kind}] generated ${compressedEntries.length} files`); + for (const entry of compressedEntries) { + config?.logger.info( + `${path.basename(outputDir)}/${entry.file} ${entry.originalKb}kb -> ${entry.compressedKb}kb` + ); + } + config?.logger.info(''); + } + }; +} + +export default (env: Record) => { + const { VITE_BUILD_COMPRESS } = env; + const plugins: Plugin[] = []; + if (!VITE_BUILD_COMPRESS) { + return plugins; + } + + const compressionList = VITE_BUILD_COMPRESS.split(',').map(item => item.trim()) as CompressionKind[]; + if (compressionList.includes('gzip')) { + plugins.push(createCompressionPlugin('gzip')); + } + if (compressionList.includes('brotli')) { + plugins.push(createCompressionPlugin('brotli')); + } + return plugins; +}; diff --git a/vite/plugins/index.ts b/vite/plugins/index.ts new file mode 100644 index 0000000..c080b07 --- /dev/null +++ b/vite/plugins/index.ts @@ -0,0 +1,22 @@ +import vue from '@vitejs/plugin-vue'; + +import createUnoCss from './unocss.js'; +import createAutoImport from './auto-import.js'; +import createComponents from './components.js'; +import createSvgIconsPlugin from './svg-icon.js'; +import createCompression from './compression.js'; +import createSetupExtend from './setup-extend.js'; +import { viteCheckTransitionPlugin } from './check-transition.js'; + +export default (viteEnv: any, isBuild = false): [] => { + const vitePlugins: any = []; + vitePlugins.push(vue()); + vitePlugins.push(createUnoCss()); + vitePlugins.push(createAutoImport()); + vitePlugins.push(createComponents()); + vitePlugins.push(createCompression(viteEnv)); + vitePlugins.push(createSvgIconsPlugin()); + vitePlugins.push(createSetupExtend()); + vitePlugins.push(viteCheckTransitionPlugin()) + return vitePlugins; +}; diff --git a/vite/plugins/setup-extend.ts b/vite/plugins/setup-extend.ts new file mode 100644 index 0000000..ed3423f --- /dev/null +++ b/vite/plugins/setup-extend.ts @@ -0,0 +1,5 @@ +import setupExtend from 'unplugin-vue-setup-extend-plus/vite'; + +export default () => { + return setupExtend({}); +}; diff --git a/vite/plugins/svg-icon.ts b/vite/plugins/svg-icon.ts new file mode 100644 index 0000000..17c0cca --- /dev/null +++ b/vite/plugins/svg-icon.ts @@ -0,0 +1,11 @@ +import { createSvgIconsPlugin } from 'vite-plugin-svg-icons-ng'; +import { resolve } from 'path'; + +export default () => { + return createSvgIconsPlugin({ + // 指定需要缓存的图标文件夹 + iconDirs: [resolve(import.meta.dirname, '../../src/assets/icons/svg')], + // 指定symbolId格式 + symbolId: 'icon-[dir]-[name]' + }); +}; diff --git a/vite/plugins/unocss.ts b/vite/plugins/unocss.ts new file mode 100644 index 0000000..08e186b --- /dev/null +++ b/vite/plugins/unocss.ts @@ -0,0 +1,7 @@ +import UnoCss from 'unocss/vite'; + +export default () => { + return UnoCss({ + hmrTopLevelAwait: false // unocss默认是true,低版本浏览器是不支持的,启动后会报错 + }); +};