diff --git a/.gitee/image/common/ai-feature.png b/.gitee/image/common/ai-feature.png new file mode 100644 index 000000000..1c22dbe4e Binary files /dev/null and b/.gitee/image/common/ai-feature.png differ diff --git a/.gitee/image/common/ai-preview.gif b/.gitee/image/common/ai-preview.gif new file mode 100644 index 000000000..5f13ac4ff Binary files /dev/null and b/.gitee/image/common/ai-preview.gif differ diff --git a/.gitee/image/common/bpm-feature.png b/.gitee/image/common/bpm-feature.png new file mode 100644 index 000000000..23787fb4f Binary files /dev/null and b/.gitee/image/common/bpm-feature.png differ diff --git a/.gitee/image/common/crm-feature.png b/.gitee/image/common/crm-feature.png new file mode 100644 index 000000000..e1c9670c6 Binary files /dev/null and b/.gitee/image/common/crm-feature.png differ diff --git a/.gitee/image/common/erp-feature.png b/.gitee/image/common/erp-feature.png new file mode 100644 index 000000000..d30b30eed Binary files /dev/null and b/.gitee/image/common/erp-feature.png differ diff --git a/.gitee/image/common/infra-feature.png b/.gitee/image/common/infra-feature.png new file mode 100644 index 000000000..f5cef50c5 Binary files /dev/null and b/.gitee/image/common/infra-feature.png differ diff --git a/.gitee/image/common/mall-feature.png b/.gitee/image/common/mall-feature.png new file mode 100644 index 000000000..cca05c0e6 Binary files /dev/null and b/.gitee/image/common/mall-feature.png differ diff --git a/.gitee/image/common/mall-preview.png b/.gitee/image/common/mall-preview.png new file mode 100644 index 000000000..f939214bf Binary files /dev/null and b/.gitee/image/common/mall-preview.png differ diff --git a/.gitee/image/common/project-vs.png b/.gitee/image/common/project-vs.png new file mode 100644 index 000000000..561e092f4 Binary files /dev/null and b/.gitee/image/common/project-vs.png differ diff --git a/.gitee/image/common/ruoyi-vue-pro-architecture.png b/.gitee/image/common/ruoyi-vue-pro-architecture.png new file mode 100644 index 000000000..7bd7d59ab Binary files /dev/null and b/.gitee/image/common/ruoyi-vue-pro-architecture.png differ diff --git a/.gitee/image/common/ruoyi-vue-pro-biz.png b/.gitee/image/common/ruoyi-vue-pro-biz.png new file mode 100644 index 000000000..24a385abe Binary files /dev/null and b/.gitee/image/common/ruoyi-vue-pro-biz.png differ diff --git a/.gitee/image/common/system-feature.png b/.gitee/image/common/system-feature.png new file mode 100644 index 000000000..366087ce0 Binary files /dev/null and b/.gitee/image/common/system-feature.png differ diff --git a/.gitee/image/common/yudao-cloud-architecture.png b/.gitee/image/common/yudao-cloud-architecture.png new file mode 100644 index 000000000..59416d806 Binary files /dev/null and b/.gitee/image/common/yudao-cloud-architecture.png differ diff --git a/.gitee/image/common/yudao-roadmap.png b/.gitee/image/common/yudao-roadmap.png new file mode 100644 index 000000000..f4becc98c Binary files /dev/null and b/.gitee/image/common/yudao-roadmap.png differ diff --git a/.gitee/image/demo/vben.png b/.gitee/image/demo/vben.png new file mode 100644 index 000000000..989187d19 Binary files /dev/null and b/.gitee/image/demo/vben.png differ diff --git a/.gitee/image/wx-xingyu.png b/.gitee/image/wx-xingyu.png new file mode 100644 index 000000000..da2e45a34 Binary files /dev/null and b/.gitee/image/wx-xingyu.png differ diff --git a/.gitee/image/工作流设计器-bpmn.jpg b/.gitee/image/工作流设计器-bpmn.jpg new file mode 100644 index 000000000..2a61f60f8 Binary files /dev/null and b/.gitee/image/工作流设计器-bpmn.jpg differ diff --git a/.gitee/image/工作流设计器-simple.jpg b/.gitee/image/工作流设计器-simple.jpg new file mode 100644 index 000000000..9ef2c9e29 Binary files /dev/null and b/.gitee/image/工作流设计器-simple.jpg differ diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..bb59abb03 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,172 @@ +name: Deploy Website on push + +on: + push: + branches: + - main + +jobs: + deploy-playground-ftp: + name: Deploy Push Playground Ftp + if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Sed Config Base + shell: bash + run: | + sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./playground/.env.production + sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./playground/.env.production + cat ./playground/.env.production + + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Build + run: pnpm build:play + + - name: Sync Playground files + uses: SamKirkland/FTP-Deploy-Action@v4.3.6 + with: + server: ${{ secrets.PRO_FTP_HOST }} + username: ${{ secrets.WEB_PLAYGROUND_FTP_ACCOUNT }} + password: ${{ secrets.WEB_PLAYGROUND_FTP_PWSSWORD }} + local-dir: ./playground/dist/ + + deploy-docs-ftp: + name: Deploy Push Docs Ftp + if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Build + run: pnpm build:docs + + - name: Sync Docs files + uses: SamKirkland/FTP-Deploy-Action@v4.3.6 + with: + server: ${{ secrets.PRO_FTP_HOST }} + username: ${{ secrets.WEBSITE_FTP_ACCOUNT }} + password: ${{ secrets.WEBSITE_FTP_PASSWORD }} + local-dir: ./docs/.vitepress/dist/ + + deploy-antd-ftp: + name: Deploy Push Antd Ftp + if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Sed Config Base + shell: bash + run: | + sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-antd/.env.production + sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-antd/.env.production + cat ./apps/web-antd/.env.production + + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Build + run: pnpm run build:antd + + - name: Sync files + uses: SamKirkland/FTP-Deploy-Action@v4.3.6 + with: + server: ${{ secrets.PRO_FTP_HOST }} + username: ${{ secrets.WEB_ANTD_FTP_ACCOUNT }} + password: ${{ secrets.WEB_ANTD_FTP_PASSWORD }} + local-dir: ./apps/web-antd/dist/ + + deploy-ele-ftp: + name: Deploy Push Element Ftp + if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Sed Config Base + shell: bash + run: | + sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-ele/.env.production + sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-ele/.env.production + cat ./apps/web-ele/.env.production + + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Build + run: pnpm run build:ele + + - name: Sync files + uses: SamKirkland/FTP-Deploy-Action@v4.3.6 + with: + server: ${{ secrets.PRO_FTP_HOST }} + username: ${{ secrets.WEB_ELE_FTP_ACCOUNT }} + password: ${{ secrets.WEB_ELE_FTP_PASSWORD }} + local-dir: ./apps/web-ele/dist/ + + deploy-naive-ftp: + name: Deploy Push Naive Ftp + if: github.actor != 'dependabot[bot]' && !contains(github.event.head_commit.message, '[skip ci]') && github.repository == 'vbenjs/vue-vben-admin' + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Sed Config Base + shell: bash + run: | + sed -i "s#VITE_COMPRESS\s*=.*#VITE_COMPRESS = gzip#g" ./apps/web-naive/.env.production + sed -i "s#VITE_PWA\s*=.*#VITE_PWA = true#g" ./apps/web-naive/.env.production + cat ./apps/web-naive/.env.production + + - name: Setup Node + uses: ./.github/actions/setup-node + + - name: Build + run: pnpm run build:naive + + - name: Sync files + uses: SamKirkland/FTP-Deploy-Action@v4.3.6 + with: + server: ${{ secrets.PRO_FTP_HOST }} + username: ${{ secrets.WEB_NAIVE_FTP_ACCOUNT }} + password: ${{ secrets.WEB_NAIVE_FTP_PASSWORD }} + local-dir: ./apps/web-naive/dist/ + + rerun-on-failure: + name: Rerun on failure + needs: + - deploy-playground-ftp + - deploy-docs-ftp + - deploy-antd-ftp + - deploy-ele-ftp + - deploy-naive-ftp + if: failure() && fromJSON(github.run_attempt) < 10 + runs-on: ubuntu-latest + steps: + - name: Retry ${{ fromJSON(github.run_attempt) }} of 10 + env: + GH_REPO: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + run: gh workflow run rerun.yml -F run_id=${{ github.run_id }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3399f39c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +node_modules +.DS_Store +dist +dist-ssr +dist.zip +dist.tar +dist.war +.nitro +.output +*-dist.zip +*-dist.tar +*-dist.war +coverage +*.local +**/.vitepress/cache +.cache +.turbo +.temp +dev-dist +.stylelintcache +yarn.lock +package-lock.json +.VSCodeCounter +**/backend-mock/data + +# local env files +.env.local +.env.*.local +.eslintcache + +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* +vite.config.mts.* +vite.config.mjs.* +vite.config.js.* +vite.config.ts.* + +# Editor directories and files +.idea +# .vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.history +.cursor diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..aeac1ae91 --- /dev/null +++ b/.npmrc @@ -0,0 +1,13 @@ +registry=https://registry.npmmirror.com +public-hoist-pattern[]=lefthook +public-hoist-pattern[]=eslint +public-hoist-pattern[]=prettier +public-hoist-pattern[]=prettier-plugin-tailwindcss +public-hoist-pattern[]=stylelint +public-hoist-pattern[]=*postcss* +public-hoist-pattern[]=@commitlint/* +public-hoist-pattern[]=czg + +strict-peer-dependencies=false +auto-install-peers=true +dedupe-peer-dependents=true diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..0278f8370 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "name": "vben admin antd dev", + "request": "launch", + "url": "http://localhost:5666", + "env": { "NODE_ENV": "development" }, + "sourceMaps": true, + "webRoot": "${workspaceFolder}/apps/web-antd" + }, + { + "type": "chrome", + "name": "vben admin ele dev", + "request": "launch", + "url": "http://localhost:5777", + "env": { "NODE_ENV": "development" }, + "sourceMaps": true, + "webRoot": "${workspaceFolder}/apps/web-ele" + }, + { + "type": "chrome", + "name": "vben admin naive dev", + "request": "launch", + "url": "http://localhost:5888", + "env": { "NODE_ENV": "development" }, + "sourceMaps": true, + "webRoot": "${workspaceFolder}/apps/web-naive" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..a45451b07 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,228 @@ +{ + "tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts", + // workbench + "workbench.list.smoothScrolling": true, + "workbench.startupEditor": "newUntitledFile", + "workbench.tree.indent": 10, + "workbench.editor.highlightModifiedTabs": true, + "workbench.editor.closeOnFileDelete": true, + "workbench.editor.limit.enabled": true, + "workbench.editor.limit.perEditorGroup": true, + "workbench.editor.limit.value": 5, + + // editor + "editor.tabSize": 2, + "editor.detectIndentation": false, + "editor.cursorBlinking": "expand", + "editor.largeFileOptimizations": true, + "editor.accessibilitySupport": "off", + "editor.cursorSmoothCaretAnimation": "on", + "editor.guides.bracketPairs": "active", + "editor.inlineSuggest.enabled": true, + "editor.suggestSelection": "recentlyUsedByPrefix", + "editor.acceptSuggestionOnEnter": "smart", + "editor.suggest.snippetsPreventQuickSuggestions": false, + "editor.stickyScroll.enabled": true, + "editor.hover.sticky": true, + "editor.suggest.insertMode": "replace", + "editor.bracketPairColorization.enabled": true, + "editor.autoClosingBrackets": "beforeWhitespace", + "editor.autoClosingDelete": "always", + "editor.autoClosingOvertype": "always", + "editor.autoClosingQuotes": "beforeWhitespace", + "editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.fixAll.stylelint": "explicit", + "source.organizeImports": "never" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[scss]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + // extensions + "extensions.ignoreRecommendations": true, + + // terminal + "terminal.integrated.cursorBlinking": true, + "terminal.integrated.persistentSessionReviveProcess": "never", + "terminal.integrated.tabs.enabled": true, + "terminal.integrated.scrollback": 10000, + "terminal.integrated.stickyScroll.enabled": true, + + // files + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.simpleDialog.enable": true, + "files.associations": { + "*.ejs": "html", + "*.art": "html", + "**/tsconfig.json": "jsonc", + "*.json": "jsonc", + "package.json": "json" + }, + + "files.exclude": { + "**/.eslintcache": true, + "**/bower_components": true, + "**/.turbo": true, + "**/.idea": true, + "**/.vitepress": true, + "**/tmp": true, + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.stylelintcache": true, + "**/.DS_Store": true, + "**/vite.config.mts.*": true, + "**/tea.yaml": true + }, + "files.watcherExclude": { + "**/.git/objects/**": true, + "**/.git/subtree-cache/**": true, + "**/.vscode/**": true, + "**/node_modules/**": true, + "**/tmp/**": true, + "**/bower_components/**": true, + "**/dist/**": true, + "**/yarn.lock": true + }, + + "typescript.tsserver.exclude": ["**/node_modules", "**/dist", "**/.turbo"], + + // search + "search.searchEditor.singleClickBehaviour": "peekDefinition", + "search.followSymlinks": false, + // 在使用搜索功能时,将这些文件夹/文件排除在外 + "search.exclude": { + "**/node_modules": true, + "**/*.log": true, + "**/*.log*": true, + "**/bower_components": true, + "**/dist": true, + "**/elehukouben": true, + "**/.git": true, + "**/.github": true, + "**/.gitignore": true, + "**/.svn": true, + "**/.DS_Store": true, + "**/.vitepress/cache": true, + "**/.idea": true, + "**/.vscode": false, + "**/.yarn": true, + "**/tmp": true, + "*.xml": true, + "out": true, + "dist": true, + "node_modules": true, + "CHANGELOG.md": true, + "**/pnpm-lock.yaml": true, + "**/yarn.lock": true, + "**/public/**": true + }, + + "debug.onTaskErrors": "debugAnyway", + "diffEditor.ignoreTrimWhitespace": false, + "npm.packageManager": "pnpm", + + "css.validate": false, + "less.validate": false, + "scss.validate": false, + + // extension + "emmet.showSuggestionsAsSnippets": true, + "emmet.triggerExpansionOnTab": false, + + "errorLens.enabledDiagnosticLevels": ["warning", "error"], + "errorLens.excludeBySource": ["cSpell", "Grammarly", "eslint"], + + "stylelint.enable": true, + "stylelint.packageManager": "pnpm", + "stylelint.validate": ["css", "less", "postcss", "scss", "vue"], + "stylelint.customSyntax": "postcss-html", + "stylelint.snippet": ["css", "less", "postcss", "scss", "vue"], + + "typescript.inlayHints.enumMemberValues.enabled": true, + "typescript.preferences.preferTypeOnlyAutoImports": true, + "typescript.preferences.includePackageJsonAutoImports": "on", + + "eslint.validate": [ + "javascript", + "typescript", + "javascriptreact", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "json5" + ], + + "tailwindCSS.experimental.classRegex": [ + ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] + ], + + "github.copilot.enable": { + "*": false + }, + + "cssVariables.lookupFiles": ["packages/core/base/design/src/**/*.css"], + + "i18n-ally.localesPaths": [ + "packages/locales/src/langs", + "playground/src/locales/langs", + "apps/*/src/locales/langs" + ], + "i18n-ally.pathMatcher": "{locale}/{namespace}.{ext}", + "i18n-ally.enabledParsers": ["json"], + "i18n-ally.sourceLanguage": "en", + "i18n-ally.displayLanguage": "zh-CN", + "i18n-ally.enabledFrameworks": ["vue", "react"], + "i18n-ally.keystyle": "nested", + "i18n-ally.sortKeys": true, + "i18n-ally.namespace": true, + + // 控制相关文件嵌套展示 + "explorer.fileNesting.enabled": true, + "explorer.fileNesting.expand": false, + "explorer.fileNesting.patterns": { + "*.ts": "$(capture).test.ts, $(capture).test.tsx, $(capture).spec.ts, $(capture).spec.tsx, $(capture).d.ts", + "*.tsx": "$(capture).test.ts, $(capture).test.tsx, $(capture).spec.ts, $(capture).spec.tsx,$(capture).d.ts", + "*.env": "$(capture).env.*", + "README.md": "README*,CHANGELOG*,LICENSE,CNAME", + "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json", + "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml", + "tailwind.config.mjs": "postcss.*" + }, + "commentTranslate.hover.enabled": false, + "commentTranslate.multiLineMerge": true, + "vue.server.hybridMode": true, + "typescript.tsdk": "node_modules/typescript/lib", + "oxc.enable": false +} diff --git a/README.md b/README.md new file mode 100644 index 000000000..26ef03fca --- /dev/null +++ b/README.md @@ -0,0 +1,240 @@ +# 严肃声明:现在、未来都不会有商业版本,所有代码全部开源 + +**「我喜欢写代码,乐此不疲」** +**「我喜欢做开源,以此为乐」** + +我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。 + +如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 + +## 🐶 新手必读 + +- nodejs > 20.12.0 && pnpm > 10.14.0 (强制使用pnpm) +- 演示地址【Vue3 + element-plus】: +- 演示地址【Vue3 + vben5(ant-design-vue)】: +- 演示地址【Vue2 + element-ui】: +- 启动文档: +- 视频教程: + +## 🐯 平台简介 + +**芋道**,以开发者为中心,打造中国第一流的快速开发平台,全部开源,个人与企业可 100% 免费使用。 + +- 采用最新 [vue-vben-admin](https://github.com/vbenjs/vue-vben-admin) v5 实现 +- 支持 [Ant Design Vue](https://www.antdv.com/) | [Element Plus](https://element-plus.org/zh-CN/) | [Naive UI](https://www.naiveui.com/) | [TDesign](https://tdesign.tencent.com/) 多种免费开源的中后台模版,具备如下特性: + +![首页](.gitee/image/demo/vben.png) + +- **最新技术栈**:使用 Vue3、Vite6 等前端前沿技术开发 +- **TypeScript**: 应用程序级 JavaScript 的语言 +- **主题**: 提供多套主题色彩,可配置自定义主题 +- **国际化**:内置完善的国际化方案 +- **权限**:内置完善的动态路由权限生成方案 +- **组件**:二次封装了多个常用的组件 +- **示例**:内置丰富的示例 + +## [外包项目请联系【非项目需求请勿扫码,非客服,不解答项目问题】](https://www.shuduokeji.com) + +![alt 软件定制开发 数舵科技](.gitee/image/wx-xingyu.png) + +## 技术栈 + +| 框架 | 说明 | 版本 | +| --- | --- | --- | +| [Vue](https://staging-cn.vuejs.org/) | vue框架 | 3.5.17 | +| [Vite](https://cn.vitejs.dev//) | 开发与构建工具 | 7.1.2 | +| [Ant Design Vue](https://www.antdv.com/) | Ant Design Vue | 4.2.6 | +| [Element Plus](https://element-plus.org/zh-CN/) | Element Plus | 2.10.2 | +| [Naive UI](https://www.naiveui.com/) | Naive UI | 2.42.0 | +| [TDesign](https://tdesign.tencent.com/) | TDesign | 1.17.1 | +| [TypeScript](https://www.typescriptlang.org/docs/) | JavaScript 超集 | 5.8.3 | +| [pinia](https://pinia.vuejs.org/) | Vue 存储库替代 vuex5 | 3.0.3 | +| [vueuse](https://vueuse.org/) | 常用工具集 | 13.4.0 | +| [vue-i18n](https://kazupon.github.io/vue-i18n/zh/introduction.html/) | 国际化 | 11.1.7 | +| [vue-router](https://router.vuejs.org/) | Vue 路由 | 4.5.1 | +| [Tailwind CSS](https://tailwindcss.com/) | 原子 CSS | 3.4.17 | +| [Iconify](https://iconify.design/) | 图标组件 | 5.0.0 | +| [Iconify](https://icon-sets.iconify.design/) | 在线图标库 | 2.2.354 | +| [TinyMCE](https://www.tiny.cloud/) | 富文本编辑器 | 6.1.0 | +| [Echarts](https://echarts.apache.org/) | 图表库 | 5.6.0 | +| [axios](https://axios-http.com/) | http客户端 | 1.10.0 | +| [dayjs](https://day.js.org/) | 日期处理库 | 1.11.13 | +| [vee-validate](https://vee-validate.logaretm.com/) | 表单验证 | 4.15.1 | +| [zod](https://zod.dev/) | 数据验证 | 3.25.67 | + +## 🔥 后端架构 + +支持 Spring Boot、Spring Cloud 两种架构: + +① Spring Boot 单体架构: + +![架构图](/.gitee/image/common/ruoyi-vue-pro-architecture.png) + +② Spring Cloud 微服务架构: + +![架构图](/.gitee/image/common/yudao-cloud-architecture.png) + +## 内置功能 + +系统内置多种多种业务功能,可以用于快速你的业务系统: + +系统内置多种多种业务功能,可以用于快速你的业务系统: + +![功能分层](/.gitee/image/common/ruoyi-vue-pro-biz.png) + +- 通用模块(必选):系统功能、基础设施 +- 通用模块(可选):工作流程、支付系统、数据报表、会员中心 +- 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型 + +### 系统功能 + +| | 功能 | 描述 | +|----|-------|---------------------------------| +| | 用户管理 | 用户是系统操作者,该功能主要完成系统用户配置 | +| ⭐️ | 在线用户 | 当前系统中活跃用户状态监控,支持手动踢下线 | +| | 角色管理 | 角色菜单权限分配、设置角色按机构进行数据范围权限划分 | +| | 菜单管理 | 配置系统菜单、操作权限、按钮权限标识等,本地缓存提供性能 | +| | 部门管理 | 配置系统组织机构(公司、部门、小组),树结构展现支持数据权限 | +| | 岗位管理 | 配置系统用户所属担任职务 | +| 🚀 | 租户管理 | 配置系统租户,支持 SaaS 场景下的多租户功能 | +| 🚀 | 租户套餐 | 配置租户套餐,自定每个租户的菜单、操作、按钮的权限 | +| | 字典管理 | 对系统中经常使用的一些较为固定的数据进行维护 | +| 🚀 | 短信管理 | 短信渠道、短息模板、短信日志,对接阿里云、腾讯云等主流短信平台 | +| 🚀 | 邮件管理 | 邮箱账号、邮件模版、邮件发送日志,支持所有邮件平台 | +| 🚀 | 站内信 | 系统内的消息通知,提供站内信模版、站内信消息 | +| 🚀 | 操作日志 | 系统正常操作日志记录和查询,集成 Swagger 生成日志内容 | +| ⭐️ | 登录日志 | 系统登录日志记录查询,包含登录异常 | +| 🚀 | 错误码管理 | 系统所有错误码的管理,可在线修改错误提示,无需重启服务 | +| | 通知公告 | 系统通知公告信息发布维护 | +| 🚀 | 敏感词 | 配置系统敏感词,支持标签分组 | +| 🚀 | 应用管理 | 管理 SSO 单点登录的应用,支持多种 OAuth2 授权方式 | +| 🚀 | 地区管理 | 展示省份、城市、区镇等城市信息,支持 IP 对应城市 | + +![功能图](/.gitee/image/common/system-feature.png) + +### 工作流程 + +![功能图](/.gitee/image/common/bpm-feature.png) + +基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作: + +| BPMN 设计器 | 钉钉/飞书设计器 | +| --- | --- | +| ![工作流设计器](.gitee/image/工作流设计器-bpmn.jpg) | ![工作流设计器](.gitee/image/工作流设计器-simple.jpg) | + +> 历经头部企业生产验证,工作流引擎须标配仿钉钉/飞书 + BPMN 双设计器!!! +> +> 前者支持轻量配置简单流程,后者实现复杂场景深度编排 + +| 功能列表 | 功能描述 | 是否完成 | +|------------|-------------------------------------------------------------------------------------|------| +| SIMPLE 设计器 | 仿钉钉/飞书设计器,支持拖拽搭建表单流程,10 分钟快速完成审批流程配置 | ✅ | +| BPMN 设计器 | 基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求 | ✅ | +| 会签 | 同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点 | ✅ | +| 或签 | 同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点 | ✅ | +| 依次审批 | (顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批,A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点 | ✅ | +| 抄送 | 将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人 | ✅ | +| 驳回 | (退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点 | ✅ | +| 转办 | A 转给其 B 审批,B 审批后,进入下一节点 | ✅ | +| 委派 | A 转给其 B 审批,B 审批后,转给 A,A 继续审批后进入下一节点 | ✅ | +| 加签 | 允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签 | ✅ | +| 减签 | (取消加签)在当前审批人操作之前,减少审批人 | ✅ | +| 撤销 | (取消流程)流程发起人,可以对流程进行撤销处理 | ✅ | +| 终止 | 系统管理员,在任意节点终止流程实例 | ✅ | +| 表单权限 | 支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限 | ✅ | +| 超时审批 | 配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作 | ✅ | +| 自动提醒 | 配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次 | ✅ | +| 父子流程 | 主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程 | ✅ | +| 条件分支 | (排它分支)用于在流程中实现决策,即根据条件选择一个分支执行 | ✅ | +| 并行分支 | 允许将流程分成多条分支,不进行条件判断,所有分支都会执行 | ✅ | +| 包容分支 | (条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支 | ✅ | +| 路由分支 | 根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行) | ✅ | +| 触发节点 | 执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等 | ✅ | +| 延迟节点 | 执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等 | ✅ | +| 拓展设置 | 流程前置/后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等 | ✅ | + +### 支付系统 + +| | 功能 | 描述 | +| --- | -------- | -------------------------------------------------- | +| 🚀 | 应用信息 | 配置商户的应用信息,对接支付宝、微信等多个支付渠道 | +| 🚀 | 支付订单 | 查看用户发起的支付宝、微信等的【支付】订单 | +| 🚀 | 退款订单 | 查看用户发起的支付宝、微信等的【退款】订单 | +| 🚀 | 回调通知 | 查看支付回调业务的【支付】【退款】的通知结果 | +| 🚀 | 接入示例 | 提供接入支付系统的【支付】【退款】的功能实战 | + +### 基础设施 + +| | 功能 | 描述 | +|----|-----------|----------------------------------------------| +| 🚀 | 代码生成 | 前后端代码的生成(Java、Vue、SQL、单元测试),支持 CRUD 下载 | +| 🚀 | 系统接口 | 基于 Swagger 自动生成相关的 RESTful API 接口文档 | +| 🚀 | 数据库文档 | 基于 Screw 自动生成数据库文档,支持导出 Word、HTML、MD 格式 | +| | 表单构建 | 拖动表单元素生成相应的 HTML 代码,支持导出 JSON、Vue 文件 | +| 🚀 | 配置管理 | 对系统动态配置常用参数,支持 SpringBoot 加载 | +| ⭐️ | 定时任务 | 在线(添加、修改、删除)任务调度包含执行结果日志 | +| 🚀 | 文件服务 | 支持将文件存储到 S3(MinIO、阿里云、腾讯云、七牛云)、本地、FTP、数据库等 | +| 🚀 | WebSocket | 提供 WebSocket 接入示例,支持一对一、一对多发送方式 | +| 🚀 | API 日志 | 包括 RESTful API 访问日志、异常日志两部分,方便排查 API 相关的问题 | +| | MySQL 监控 | 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈 | +| | Redis 监控 | 监控 Redis 数据库的使用情况,使用的 Redis Key 管理 | +| 🚀 | 消息队列 | 基于 Redis 实现消息队列,Stream 提供集群消费,Pub/Sub 提供广播消费 | +| 🚀 | Java 监控 | 基于 Spring Boot Admin 实现 Java 应用的监控 | +| 🚀 | 链路追踪 | 接入 SkyWalking 组件,实现链路追踪 | +| 🚀 | 日志中心 | 接入 SkyWalking 组件,实现日志中心 | +| 🚀 | 服务保障 | 基于 Redis 实现分布式锁、幂等、限流功能,满足高并发场景 | +| 🚀 | 日志服务 | 轻量级日志中心,查看远程服务器的日志 | +| 🚀 | 单元测试 | 基于 JUnit + Mockito 实现单元测试,保证功能的正确性、代码的质量等 | + +![功能图](/.gitee/image/common/infra-feature.png) + +### 数据报表 + +| | 功能 | 描述 | +| --- | ---------- | ------------------------------------ | +| 🚀 | 报表设计器 | 支持数据报表、图形报表、打印设计等 | +| 🚀 | 大屏设计器 | 拖拽生成数据大屏,内置几十种图表组件 | + +### 微信公众号 + +| | 功能 | 描述 | +|----|--------|-------------------------------| +| 🚀 | 账号管理 | 配置接入的微信公众号,可支持多个公众号 | +| 🚀 | 数据统计 | 统计公众号的用户增减、累计用户、消息概况、接口分析等数据 | +| 🚀 | 粉丝管理 | 查看已关注、取关的粉丝列表,可对粉丝进行同步、打标签等操作 | +| 🚀 | 消息管理 | 查看粉丝发送的消息列表,可主动回复粉丝消息 | +| 🚀 | 模版消息 | 配置和发送模版消息,用于向粉丝推送通知类消息 | +| 🚀 | 自动回复 | 自动回复粉丝发送的消息,支持关注回复、消息回复、关键字回复 | +| 🚀 | 标签管理 | 对公众号的标签进行创建、查询、修改、删除等操作 | +| 🚀 | 菜单管理 | 自定义公众号的菜单,也可以从公众号同步菜单 | +| 🚀 | 素材管理 | 管理公众号的图片、语音、视频等素材,支持在线播放语音、视频 | +| 🚀 | 图文草稿箱 | 新增常用的图文素材到草稿箱,可发布到公众号 | +| 🚀 | 图文发表记录 | 查看已发布成功的图文素材,支持删除操作 | + +### 商城系统 + +演示地址: + +![功能图](/.gitee/image/common/mall-feature.png) + +![功能图](/.gitee/image/common/mall-preview.png) + +### ERP 系统 + +演示地址: + +![功能图](/.gitee/image/common/erp-feature.png) + +### CRM 系统 + +演示地址: + +![功能图](/.gitee/image/common/crm-feature.png) + +### AI 大模型 + +演示地址: + +![功能图](/.gitee/image/common/ai-feature.png) + +![功能图](/.gitee/image/common/ai-preview.gif) diff --git a/apps/web-antd/.env b/apps/web-antd/.env new file mode 100644 index 000000000..778a9fa1f --- /dev/null +++ b/apps/web-antd/.env @@ -0,0 +1,35 @@ +# 应用标题 +VITE_APP_TITLE=芋道管理系统 + +# 应用命名空间,用于缓存、store等功能的前缀,确保隔离 +VITE_APP_NAMESPACE=yudao-vben-antd + +# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密 +VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key + +# 是否开启模拟数据 +VITE_NITRO_MOCK=false + +# 租户开关 +VITE_APP_TENANT_ENABLE=true + +# 验证码的开关 +VITE_APP_CAPTCHA_ENABLE=false + +# 文档地址的开关 +VITE_APP_DOCALERT_ENABLE=true + +# 百度统计 +VITE_APP_BAIDU_CODE = e98f2eab6ceb8688bc6d8fc5332ff093 + +# GoView域名 +VITE_GOVIEW_URL='http://127.0.0.1:3000' + +# API 加解密 +VITE_APP_API_ENCRYPT_ENABLE = true +VITE_APP_API_ENCRYPT_HEADER = X-Api-Encrypt +VITE_APP_API_ENCRYPT_ALGORITHM = AES +VITE_APP_API_ENCRYPT_REQUEST_KEY = 52549111389893486934626385991395 +VITE_APP_API_ENCRYPT_RESPONSE_KEY = 96103715984234343991809655248883 +# VITE_APP_API_ENCRYPT_REQUEST_KEY = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCls2rIpnGdYnLFgz1XU13GbNQ5DloyPpvW00FPGjqn5Z6JpK+kDtVlnkhwR87iRrE5Vf2WNqRX6vzbLSgveIQY8e8oqGCb829myjf1MuI+ZzN4ghf/7tEYhZJGPI9AbfxFqBUzm+kR3/HByAI22GLT96WM26QiMK8n3tIP/yiLswIDAQAB +# VITE_APP_API_ENCRYPT_RESPONSE_KEY = MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOH8IfIFxL/MR9XIg1UDv5z1fGXQI93E8wrU4iPFovL/sEt9uSgSkjyidC2O7N+m7EKtoN6b1u7cEwXSkwf3kfK0jdWLSQaNpX5YshqXCBzbDfugDaxuyYrNA4/tIMs7mzZAk0APuRXB35Dmupou7Yw7TFW/7QhQmGfzeEKULQvnAgMBAAECgYAw8LqlQGyQoPv5p3gRxEMOCfgL0JzD3XBJKztiRd35RDh40Nx1ejgjW4dPioFwGiVWd2W8cAGHLzALdcQT2KDJh+T/tsd4SPmI6uSBBK6Ff2DkO+kFFcuYvfclQQKqxma5CaZOSqhgenacmgTMFeg2eKlY3symV6JlFNu/IKU42QJBAOhxAK/Eq3e61aYQV2JSguhMR3b8NXJJRroRs/QHEanksJtl+M+2qhkC9nQVXBmBkndnkU/l2tYcHfSBlAyFySMCQQD445tgm/J2b6qMQmuUGQAYDN8FIkHjeKmha+l/fv0igWm8NDlBAem91lNDIPBUzHL1X1+pcts5bjmq99YdOnhtAkAg2J8dN3B3idpZDiQbC8fd5bGPmdI/pSUudAP27uzLEjr2qrE/QPPGdwm2m7IZFJtK7kK1hKio6u48t/bg0iL7AkEAuUUs94h+v702Fnym+jJ2CHEkXvz2US8UDs52nWrZYiM1o1y4tfSHm8H8bv8JCAa9GHyriEawfBraILOmllFdLQJAQSRZy4wmlaG48MhVXodB85X+VZ9krGXZ2TLhz7kz9iuToy53l9jTkESt6L5BfBDCVdIwcXLYgK+8KFdHN5W7HQ== diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json new file mode 100644 index 000000000..847d65725 --- /dev/null +++ b/apps/web-antd/package.json @@ -0,0 +1,71 @@ +{ + "name": "@vben/web-antd", + "version": "5.5.9", + "homepage": "https://vben.pro", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "apps/web-antd" + }, + "license": "MIT", + "author": { + "name": "vben", + "email": "ann.vben@gmail.com", + "url": "https://github.com/anncwb" + }, + "type": "module", + "scripts": { + "build": "pnpm vite build --mode production", + "build:analyze": "pnpm vite build --mode analyze", + "dev": "pnpm vite --mode development", + "preview": "vite preview", + "typecheck": "vue-tsc --noEmit --skipLibCheck" + }, + "imports": { + "#/*": "./src/*" + }, + "dependencies": { + "@form-create/ant-design-vue": "catalog:", + "@form-create/antd-designer": "catalog:", + "@tinymce/tinymce-vue": "catalog:", + "@vben/access": "workspace:*", + "@vben/common-ui": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/layouts": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/plugins": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/request": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/styles": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@videojs-player/vue": "catalog:", + "@vueuse/core": "catalog:", + "@vueuse/integrations": "catalog:", + "ant-design-vue": "catalog:", + "benz-amr-recorder": "catalog:", + "bpmn-js": "catalog:", + "bpmn-js-properties-panel": "catalog:", + "bpmn-js-token-simulation": "catalog:", + "camunda-bpmn-moddle": "catalog:", + "cropperjs": "catalog:", + "dayjs": "catalog:", + "diagram-js": "catalog:", + "fast-xml-parser": "catalog:", + "highlight.js": "catalog:", + "pinia": "catalog:", + "steady-xml": "catalog:", + "tinymce": "catalog:", + "video.js": "catalog:", + "vue": "catalog:", + "vue-dompurify-html": "catalog:", + "vue-router": "catalog:", + "vue3-print-nb": "catalog:", + "vue3-signature": "catalog:", + "vuedraggable": "catalog:" + } +} diff --git a/apps/web-antd/public/wx-xingyu.png b/apps/web-antd/public/wx-xingyu.png new file mode 100644 index 000000000..da2e45a34 Binary files /dev/null and b/apps/web-antd/public/wx-xingyu.png differ diff --git a/apps/web-antd/src/adapter/component/index.ts b/apps/web-antd/src/adapter/component/index.ts new file mode 100644 index 000000000..68e7e88b3 --- /dev/null +++ b/apps/web-antd/src/adapter/component/index.ts @@ -0,0 +1,244 @@ +/** + * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用 + * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, + */ + +import type { Component } from 'vue'; + +import type { BaseFormComponentType } from '@vben/common-ui'; +import type { Recordable } from '@vben/types'; + +import { defineAsyncComponent, defineComponent, h, ref } from 'vue'; + +import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +import { notification } from 'ant-design-vue'; + +import { Tinymce as RichTextarea } from '#/components/tinymce'; +import { FileUpload, ImageUpload } from '#/components/upload'; + +const AutoComplete = defineAsyncComponent( + () => import('ant-design-vue/es/auto-complete'), +); +const Button = defineAsyncComponent(() => import('ant-design-vue/es/button')); +const Cascader = defineAsyncComponent( + () => import('ant-design-vue/es/cascader'), +); +const Checkbox = defineAsyncComponent( + () => import('ant-design-vue/es/checkbox'), +); +const CheckboxGroup = defineAsyncComponent(() => + import('ant-design-vue/es/checkbox').then((res) => res.CheckboxGroup), +); +const DatePicker = defineAsyncComponent( + () => import('ant-design-vue/es/date-picker'), +); +const Divider = defineAsyncComponent(() => import('ant-design-vue/es/divider')); +const Input = defineAsyncComponent(() => import('ant-design-vue/es/input')); +const InputNumber = defineAsyncComponent( + () => import('ant-design-vue/es/input-number'), +); +const InputPassword = defineAsyncComponent(() => + import('ant-design-vue/es/input').then((res) => res.InputPassword), +); +const Mentions = defineAsyncComponent( + () => import('ant-design-vue/es/mentions'), +); +const Radio = defineAsyncComponent(() => import('ant-design-vue/es/radio')); +const RadioGroup = defineAsyncComponent(() => + import('ant-design-vue/es/radio').then((res) => res.RadioGroup), +); +const RangePicker = defineAsyncComponent(() => + import('ant-design-vue/es/date-picker').then((res) => res.RangePicker), +); +const Rate = defineAsyncComponent(() => import('ant-design-vue/es/rate')); +const Select = defineAsyncComponent(() => import('ant-design-vue/es/select')); +const Space = defineAsyncComponent(() => import('ant-design-vue/es/space')); +const Switch = defineAsyncComponent(() => import('ant-design-vue/es/switch')); +const Textarea = defineAsyncComponent(() => + import('ant-design-vue/es/input').then((res) => res.Textarea), +); +const TimePicker = defineAsyncComponent( + () => import('ant-design-vue/es/time-picker'), +); +const TimeRangePicker = defineAsyncComponent(() => + import('ant-design-vue/es/time-picker').then((res) => res.TimeRangePicker), +); +const TreeSelect = defineAsyncComponent( + () => import('ant-design-vue/es/tree-select'), +); +const Upload = defineAsyncComponent(() => import('ant-design-vue/es/upload')); + +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', + componentProps: Recordable = {}, +) => { + return defineComponent({ + name: component.name, + inheritAttrs: false, + setup: (props: any, { attrs, expose, slots }) => { + const placeholder = + props?.placeholder || + attrs?.placeholder || + $t(`ui.placeholder.${type}`); + // 透传组件暴露的方法 + const innerRef = ref(); + expose( + new Proxy( + {}, + { + get: (_target, key) => innerRef.value?.[key], + has: (_target, key) => key in (innerRef.value || {}), + }, + ), + ); + return () => + h( + component, + { ...componentProps, placeholder, ...props, ...attrs, ref: innerRef }, + slots, + ); + }, + }); +}; + +// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 +export type ComponentType = + | 'ApiCascader' + | 'ApiSelect' + | 'ApiTreeSelect' + | 'AutoComplete' + | 'Checkbox' + | 'CheckboxGroup' + | 'DatePicker' + | 'DefaultButton' + | 'Divider' + | 'FileUpload' + | 'IconPicker' + | 'ImageUpload' + | 'Input' + | 'InputNumber' + | 'InputPassword' + | 'Mentions' + | 'PrimaryButton' + | 'Radio' + | 'RadioGroup' + | 'RangePicker' + | 'Rate' + | 'RichTextarea' + | 'Select' + | 'Space' + | 'Switch' + | 'Textarea' + | 'TimePicker' + | 'TimeRangePicker' + | 'TreeSelect' + | 'Upload' + | BaseFormComponentType; + +async function initComponentAdapter() { + const components: Partial> = { + // 如果你的组件体积比较大,可以使用异步加载 + // Button: () => + // import('xxx').then((res) => res.Button), + ApiCascader: withDefaultPlaceholder( + { + ...ApiComponent, + name: 'ApiCascader', + }, + 'select', + { + component: Cascader, + fieldNames: { label: 'label', value: 'value', children: 'children' }, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + optionsPropName: 'treeData', + visibleEvent: 'onVisibleChange', + }, + ), + ApiSelect: withDefaultPlaceholder( + { + ...ApiComponent, + name: 'ApiSelect', + }, + 'select', + { + component: Select, + loadingSlot: 'suffixIcon', + visibleEvent: 'onDropdownVisibleChange', + modelPropName: 'value', + }, + ), + ApiTreeSelect: withDefaultPlaceholder( + { + ...ApiComponent, + name: 'ApiTreeSelect', + }, + 'select', + { + component: TreeSelect, + fieldNames: { label: 'label', value: 'value', children: 'children' }, + loadingSlot: 'suffixIcon', + modelPropName: 'value', + optionsPropName: 'treeData', + visibleEvent: 'onVisibleChange', + }, + ), + AutoComplete, + Checkbox, + CheckboxGroup, + DatePicker, + // 自定义默认按钮 + DefaultButton: (props, { attrs, slots }) => { + return h(Button, { ...props, attrs, type: 'default' }, slots); + }, + Divider, + IconPicker: withDefaultPlaceholder(IconPicker, 'select', { + iconSlot: 'addonAfter', + inputComponent: Input, + modelValueProp: 'value', + }), + Input: withDefaultPlaceholder(Input, 'input'), + InputNumber: withDefaultPlaceholder(InputNumber, 'input'), + InputPassword: withDefaultPlaceholder(InputPassword, 'input'), + Mentions: withDefaultPlaceholder(Mentions, 'input'), + // 自定义主要按钮 + PrimaryButton: (props, { attrs, slots }) => { + return h(Button, { ...props, attrs, type: 'primary' }, slots); + }, + Radio, + RadioGroup, + RangePicker, + Rate, + Select: withDefaultPlaceholder(Select, 'select'), + Space, + Switch, + Textarea: withDefaultPlaceholder(Textarea, 'input'), + RichTextarea, + TimePicker, + TimeRangePicker, + TreeSelect: withDefaultPlaceholder(TreeSelect, 'select'), + Upload, + FileUpload, + ImageUpload, + }; + + // 将组件注册到全局共享状态中 + globalShareState.setComponents(components); + + // 定义全局共享状态中的消息提示 + globalShareState.defineMessage({ + // 复制成功消息提示 + copyPreferencesSuccess: (title, content) => { + notification.success({ + description: content, + message: title, + placement: 'bottomRight', + }); + }, + }); +} + +export { initComponentAdapter }; diff --git a/apps/web-antd/src/adapter/form.ts b/apps/web-antd/src/adapter/form.ts new file mode 100644 index 000000000..d5eb21b89 --- /dev/null +++ b/apps/web-antd/src/adapter/form.ts @@ -0,0 +1,69 @@ +import type { + VbenFormSchema as FormSchema, + VbenFormProps, +} from '@vben/common-ui'; + +import type { ComponentType } from './component'; + +import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; +import { $t } from '@vben/locales'; +import { isMobile } from '@vben/utils'; + +async function initSetupVbenForm() { + setupVbenForm({ + config: { + // ant design vue组件库默认都是 v-model:value + baseModelPropName: 'value', + + // 一些组件是 v-model:checked 或者 v-model:fileList + modelPropNameMap: { + Checkbox: 'checked', + Radio: 'checked', + Switch: 'checked', + Upload: 'fileList', + }, + }, + defineRules: { + // 输入项目必填国际化适配 + required: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.required', [ctx.label]); + } + return true; + }, + // 选择项目必填国际化适配 + selectRequired: (value, _params, ctx) => { + if (value === undefined || value === null) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, + // 手机号非必填 + mobile: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return true; + } else if (!isMobile(value)) { + return $t('ui.formRules.mobile', [ctx.label]); + } + return true; + }, + // 手机号必填 + mobileRequired: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.required', [ctx.label]); + } + if (!isMobile(value)) { + return $t('ui.formRules.mobile', [ctx.label]); + } + return true; + }, + }, + }); +} + +const useVbenForm = useForm; + +export { initSetupVbenForm, useVbenForm, z }; + +export type VbenFormSchema = FormSchema; +export type { VbenFormProps }; diff --git a/apps/web-antd/src/adapter/vxe-table.ts b/apps/web-antd/src/adapter/vxe-table.ts new file mode 100644 index 000000000..d41f39113 --- /dev/null +++ b/apps/web-antd/src/adapter/vxe-table.ts @@ -0,0 +1,371 @@ +import type { VxeTableGridOptions } from '@vben/plugins/vxe-table'; +import type { Recordable } from '@vben/types'; + +import { h } from 'vue'; + +import { IconifyIcon } from '@vben/icons'; +import { $te } from '@vben/locales'; +import { + AsyncVxeColumn, + AsyncVxeTable, + createRequiredValidation, + setupVbenVxeTable, + useVbenVxeGrid, +} from '@vben/plugins/vxe-table'; +import { + erpCountInputFormatter, + erpNumberFormatter, + fenToYuan, + formatFileSize, + formatPast2, + isFunction, + isString, +} from '@vben/utils'; + +import { + Button, + Image, + ImagePreviewGroup, + Popconfirm, + Switch, + Tag, +} from 'ant-design-vue'; + +import { DictTag } from '#/components/dict-tag'; +import { $t } from '#/locales'; + +import { useVbenForm } from './form'; + +setupVbenVxeTable({ + configVxeTable: (vxeUI) => { + vxeUI.setConfig({ + grid: { + align: 'center', + border: false, + columnConfig: { + resizable: true, + }, + minHeight: 180, + formConfig: { + // 全局禁用vxe-table的表单配置,使用formOptions + enabled: false, + }, + toolbarConfig: { + import: false, // 是否导入 + export: false, // 是否导出 + refresh: true, // 是否刷新 + print: false, // 是否打印 + zoom: true, // 是否缩放 + custom: true, // 是否自定义配置 + }, + customConfig: { + mode: 'modal', + }, + proxyConfig: { + autoLoad: true, + response: { + result: 'list', + total: 'total', + }, + showActiveMsg: true, + showResponseMsg: false, + }, + pagerConfig: { + enabled: true, + }, + sortConfig: { + multiple: true, + }, + round: true, + showOverflow: true, + size: 'small', + } as VxeTableGridOptions, + }); + + // 表格配置项可以用 cellRender: { name: 'CellImage' }, + vxeUI.renderer.add('CellImage', { + renderTableDefault(_renderOpts, params) { + const { column, row } = params; + return h(Image, { src: row[column.field] }); + }, + }); + + vxeUI.renderer.add('CellImages', { + renderTableDefault(_renderOpts, params) { + const { column, row } = params; + if (column && column.field && row[column.field]) { + return h(ImagePreviewGroup, {}, () => { + return row[column.field].map((item: any) => + h(Image, { src: item }), + ); + }); + } + return ''; + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellLink' }, + vxeUI.renderer.add('CellLink', { + renderTableDefault(renderOpts) { + const { props } = renderOpts; + return h( + Button, + { size: 'small', type: 'link' }, + { default: () => props?.text }, + ); + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellTag' }, + vxeUI.renderer.add('CellTag', { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; + const { column, row } = params; + return h(Tag, { color: props?.color }, () => row[column.field]); + }, + }); + + vxeUI.renderer.add('CellTags', { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; + const { column, row } = params; + if (!row[column.field] || row[column.field].length === 0) { + return ''; + } + return h( + 'div', + { class: 'flex items-center justify-center' }, + { + default: () => + row[column.field].map((item: any) => + h(Tag, { color: props?.color }, { default: () => item }), + ), + }, + ); + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellDict', props:{dictType: ''} }, + vxeUI.renderer.add('CellDict', { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; + const { column, row } = params; + if (!props) { + return ''; + } + // 使用 DictTag 组件替代原来的实现 + return h(DictTag, { + type: props.type, + value: row[column.field]?.toString(), + }); + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellSwitch', props: { beforeChange: () => {} } }, + // add by 芋艿:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/adapter/vxe-table.ts#L97-L123 + vxeUI.renderer.add('CellSwitch', { + renderTableDefault({ attrs, props }, { column, row }) { + const loadingKey = `__loading_${column.field}`; + const finallyProps = { + checkedChildren: $t('common.enabled'), + checkedValue: 1, + unCheckedChildren: $t('common.disabled'), + unCheckedValue: 0, + ...props, + checked: row[column.field], + loading: row[loadingKey] ?? false, + 'onUpdate:checked': onChange, + }; + + async function onChange(newVal: any) { + row[loadingKey] = true; + try { + const result = await attrs?.beforeChange?.(newVal, row); + if (result !== false) { + row[column.field] = newVal; + } + } finally { + row[loadingKey] = false; + } + } + + return h(Switch, finallyProps); + }, + }); + + // 注册表格的操作按钮渲染器 cellRender: { name: 'CellOperation', options: ['edit', 'delete'] } + // add by 芋艿:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/adapter/vxe-table.ts#L125-L255 + vxeUI.renderer.add('CellOperation', { + renderTableDefault({ attrs, options, props }, { column, row }) { + const defaultProps = { size: 'small', type: 'link', ...props }; + let align = 'end'; + switch (column.align) { + case 'center': { + align = 'center'; + break; + } + case 'left': { + align = 'start'; + break; + } + default: { + align = 'end'; + break; + } + } + const presets: Recordable> = { + delete: { + danger: true, + text: $t('common.delete'), + }, + edit: { + text: $t('common.edit'), + }, + }; + const operations: Array> = ( + options || ['edit', 'delete'] + ) + .map((opt) => { + if (isString(opt)) { + return presets[opt] + ? { code: opt, ...presets[opt], ...defaultProps } + : { + code: opt, + text: $te(`common.${opt}`) ? $t(`common.${opt}`) : opt, + ...defaultProps, + }; + } else { + return { ...defaultProps, ...presets[opt.code], ...opt }; + } + }) + .map((opt) => { + const optBtn: Recordable = {}; + Object.keys(opt).forEach((key) => { + optBtn[key] = isFunction(opt[key]) ? opt[key](row) : opt[key]; + }); + return optBtn; + }) + .filter((opt) => opt.show !== false); + + function renderBtn(opt: Recordable, listen = true) { + return h( + Button, + { + ...props, + ...opt, + icon: undefined, + onClick: listen + ? () => + attrs?.onClick?.({ + code: opt.code, + row, + }) + : undefined, + }, + { + default: () => { + const content = []; + if (opt.icon) { + content.push( + h(IconifyIcon, { class: 'size-5', icon: opt.icon }), + ); + } + content.push(opt.text); + return content; + }, + }, + ); + } + + function renderConfirm(opt: Recordable) { + return h( + Popconfirm, + { + getPopupContainer(el) { + return el.closest('tbody') || document.body; + }, + placement: 'topLeft', + title: $t('ui.actionTitle.delete', [attrs?.nameTitle || '']), + ...props, + ...opt, + icon: undefined, + onConfirm: () => { + attrs?.onClick?.({ + code: opt.code, + row, + }); + }, + }, + { + default: () => renderBtn({ ...opt }, false), + description: () => + h( + 'div', + { class: 'truncate' }, + $t('ui.actionMessage.deleteConfirm', [ + row[attrs?.nameField || 'name'], + ]), + ), + }, + ); + } + + const btns = operations.map((opt) => + opt.code === 'delete' ? renderConfirm(opt) : renderBtn(opt), + ); + return h( + 'div', + { + class: 'flex table-operations', + style: { justifyContent: align }, + }, + btns, + ); + }, + }); + + // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 + // vxeUI.formats.add + + vxeUI.formats.add('formatPast2', { + tableCellFormatMethod({ cellValue }) { + return formatPast2(cellValue); + }, + }); + + // add by 星语:数量格式化,保留 3 位 + vxeUI.formats.add('formatAmount3', { + tableCellFormatMethod({ cellValue }) { + return erpCountInputFormatter(cellValue); + }, + }); + // add by 星语:数量格式化,保留 2 位 + vxeUI.formats.add('formatAmount2', { + tableCellFormatMethod({ cellValue }, digits = 2) { + return `${erpNumberFormatter(cellValue, digits)}`; + }, + }); + + vxeUI.formats.add('formatFenToYuanAmount', { + tableCellFormatMethod({ cellValue }, digits = 2) { + return `${erpNumberFormatter(fenToYuan(cellValue), digits)}`; + }, + }); + + // add by 星语:文件大小格式化 + vxeUI.formats.add('formatFileSize', { + tableCellFormatMethod({ cellValue }, digits = 2) { + return formatFileSize(cellValue, digits); + }, + }); + }, + useVbenForm, +}); + +export { createRequiredValidation, useVbenVxeGrid }; + +export const [VxeTable, VxeColumn] = [AsyncVxeTable, AsyncVxeColumn]; + +export * from '#/components/table-action'; + +export type * from '@vben/plugins/vxe-table'; diff --git a/apps/web-antd/src/api/ai/chat/conversation/index.ts b/apps/web-antd/src/api/ai/chat/conversation/index.ts new file mode 100644 index 000000000..afd9cbc83 --- /dev/null +++ b/apps/web-antd/src/api/ai/chat/conversation/index.ts @@ -0,0 +1,74 @@ +import type { PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiChatConversationApi { + export interface ChatConversation { + id: number; // ID 编号 + userId: number; // 用户编号 + title: string; // 对话标题 + pinned: boolean; // 是否置顶 + roleId: number; // 角色编号 + modelId: number; // 模型编号 + model: string; // 模型标志 + temperature: number; // 温度参数 + maxTokens: number; // 单条回复的最大 Token 数量 + maxContexts: number; // 上下文的最大 Message 数量 + createTime?: Date; // 创建时间 + systemMessage?: string; // 角色设定 + modelName?: string; // 模型名字 + roleAvatar?: string; // 角色头像 + modelMaxTokens?: string; // 模型的单条回复的最大 Token 数量 + modelMaxContexts?: string; // 模型的上下文的最大 Message 数量 + } +} + +/** 获得【我的】聊天对话 */ +export function getChatConversationMy(id: number) { + return requestClient.get( + `/ai/chat/conversation/get-my?id=${id}`, + ); +} + +/** 新增【我的】聊天对话 */ +export function createChatConversationMy( + data: AiChatConversationApi.ChatConversation, +) { + return requestClient.post('/ai/chat/conversation/create-my', data); +} + +/** 更新【我的】聊天对话 */ +export function updateChatConversationMy( + data: AiChatConversationApi.ChatConversation, +) { + return requestClient.put(`/ai/chat/conversation/update-my`, data); +} + +/** 删除【我的】聊天对话 */ +export function deleteChatConversationMy(id: number) { + return requestClient.delete(`/ai/chat/conversation/delete-my?id=${id}`); +} + +/** 删除【我的】所有对话,置顶除外 */ +export function deleteChatConversationMyByUnpinned() { + return requestClient.delete(`/ai/chat/conversation/delete-by-unpinned`); +} + +/** 获得【我的】聊天对话列表 */ +export function getChatConversationMyList() { + return requestClient.get( + `/ai/chat/conversation/my-list`, + ); +} + +/** 获得【我的】聊天对话列表 */ +export function getChatConversationPage(params: any) { + return requestClient.get< + PageResult + >(`/ai/chat/conversation/page`, { params }); +} + +/** 管理员删除消息 */ +export function deleteChatConversationByAdmin(id: number) { + return requestClient.delete(`/ai/chat/conversation/delete-by-admin?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/chat/message/index.ts b/apps/web-antd/src/api/ai/chat/message/index.ts new file mode 100644 index 000000000..fdb3c8090 --- /dev/null +++ b/apps/web-antd/src/api/ai/chat/message/index.ts @@ -0,0 +1,115 @@ +import type { PageResult } from '@vben/request'; + +import { useAppConfig } from '@vben/hooks'; +import { fetchEventSource } from '@vben/request'; +import { useAccessStore } from '@vben/stores'; + +import { requestClient } from '#/api/request'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); +const accessStore = useAccessStore(); + +export namespace AiChatMessageApi { + export interface ChatMessage { + id: number; // 编号 + conversationId: number; // 对话编号 + type: string; // 消息类型 + userId: string; // 用户编号 + roleId: string; // 角色编号 + model: number; // 模型标志 + modelId: number; // 模型编号 + content: string; // 聊天内容 + reasoningContent?: string; // 推理内容(深度思考) + tokens: number; // 消耗 Token 数量 + segmentIds?: number[]; // 段落编号 + segments?: { + content: string; // 段落内容 + documentId: number; // 文档编号 + documentName: string; // 文档名称 + id: number; // 段落编号 + }[]; + webSearchPages?: WebSearchPage[]; // 联网搜索结果 + attachmentUrls?: string[]; // 附件 URL 数组 + createTime: Date; // 创建时间 + roleAvatar: string; // 角色头像 + userAvatar: string; // 用户头像 + } + + /** 联网搜索页面接口 */ + export interface WebSearchPage { + name: string; // 网站名称 + icon: string; // 网站图标 URL + title: string; // 页面标题 + url: string; // 页面 URL + snippet: string; // 简短描述 + summary: string; // 内容摘要 + } +} + +/** 消息列表 */ +export function getChatMessageListByConversationId( + conversationId: null | number, +) { + return requestClient.get( + `/ai/chat/message/list-by-conversation-id?conversationId=${conversationId}`, + ); +} + +/** 发送 Stream 消息 */ +export function sendChatMessageStream( + conversationId: number, + content: string, + ctrl: any, + enableContext: boolean, + enableWebSearch: boolean, + onMessage: any, + onError: any, + onClose: any, + attachmentUrls?: string[], +) { + const token = accessStore.accessToken; + return fetchEventSource(`${apiURL}/ai/chat/message/send-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + openWhenHidden: true, + body: JSON.stringify({ + conversationId, + content, + useContext: enableContext, + useSearch: enableWebSearch, + attachmentUrls: attachmentUrls || [], + }), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal, + }); +} + +/** 删除消息 */ +export function deleteChatMessage(id: number) { + return requestClient.delete(`/ai/chat/message/delete?id=${id}`); +} + +/** 删除指定对话的消息 */ +export function deleteByConversationId(conversationId: number) { + return requestClient.delete( + `/ai/chat/message/delete-by-conversation-id?conversationId=${conversationId}`, + ); +} + +/** 获得消息分页 */ +export function getChatMessagePage(params: any) { + return requestClient.get>( + '/ai/chat/message/page', + { params }, + ); +} + +/** 管理员删除消息 */ +export function deleteChatMessageByAdmin(id: number) { + return requestClient.delete(`/ai/chat/message/delete-by-admin?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/image/index.ts b/apps/web-antd/src/api/ai/image/index.ts new file mode 100644 index 000000000..dfbf58b08 --- /dev/null +++ b/apps/web-antd/src/api/ai/image/index.ts @@ -0,0 +1,115 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiImageApi { + /** 绘图 */ + export interface Image { + id: number; // 编号 + userId: number; + platform: string; // 平台 + model: string; // 模型 + prompt: string; // 提示词 + width: number; // 图片宽度 + height: number; // 图片高度 + status: number; // 状态 + publicStatus: boolean; // 公开状态 + picUrl: string; // 任务地址 + errorMessage: string; // 错误信息 + options: any; // 配置 Map + taskId: number; // 任务编号 + buttons: ImageMidjourneyButtons[]; // mj 操作按钮 + createTime: Date; // 创建时间 + finishTime: Date; // 完成时间 + } + + export interface ImageMidjourneyButtons { + customId: string; // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识 + emoji: string; // 图标 emoji + label: string; // Make Variations 文本 + style: number; // 样式: 2(Primary)、3(Green) + } + + export interface ImageDrawReqVO { + prompt: string; // 提示词 + modelId: number; // 模型 + style: string; // 图像生成的风格 + width: string; // 图片宽度 + height: string; // 图片高度 + options: object; // 绘制参数,Map + } + + export interface ImageMidjourneyImagineReqVO { + prompt: string; // 提示词 + modelId: number; // 模型 + base64Array?: string[]; // size不能为空 + width: string; // 图片宽度 + height: string; // 图片高度 + version: string; // 版本 + } + + export interface ImageMidjourneyAction { + id: number; // 图片编号 + customId: string; // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识 + } +} + +/** 获取【我的】绘图分页 */ +export function getImagePageMy(params: PageParam) { + return requestClient.get>('/ai/image/my-page', { + params, + }); +} + +/** 获取【我的】绘图记录 */ +export function getImageMy(id: number) { + return requestClient.get(`/ai/image/get-my?id=${id}`); +} + +/** 获取【我的】绘图记录列表 */ +export function getImageListMyByIds(ids: number[]) { + return requestClient.get(`/ai/image/my-list-by-ids`, { + params: { ids: ids.join(',') }, + }); +} + +/** 生成图片 */ +export function drawImage(data: AiImageApi.ImageDrawReqVO) { + return requestClient.post(`/ai/image/draw`, data); +} + +/** 删除【我的】绘画记录 */ +export function deleteImageMy(id: number) { + return requestClient.delete(`/ai/image/delete-my?id=${id}`); +} + +/** ================ midjourney 专属 ================ */ + +/** 【Midjourney】生成图片 */ +export function midjourneyImagine( + data: AiImageApi.ImageMidjourneyImagineReqVO, +) { + return requestClient.post(`/ai/image/midjourney/imagine`, data); +} + +/** 【Midjourney】Action 操作(二次生成图片) */ +export function midjourneyAction(data: AiImageApi.ImageMidjourneyAction) { + return requestClient.post(`/ai/image/midjourney/action`, data); +} + +/** ================ 绘图管理 ================ */ + +/** 查询绘画分页 */ +export function getImagePage(params: any) { + return requestClient.get(`/ai/image/page`, { params }); +} + +/** 更新绘画发布状态 */ +export function updateImage(data: any) { + return requestClient.put(`/ai/image/update`, data); +} + +/** 删除绘画 */ +export function deleteImage(id: number) { + return requestClient.delete(`/ai/image/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/knowledge/document/index.ts b/apps/web-antd/src/api/ai/knowledge/document/index.ts new file mode 100644 index 000000000..589a2fe31 --- /dev/null +++ b/apps/web-antd/src/api/ai/knowledge/document/index.ts @@ -0,0 +1,53 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiKnowledgeDocumentApi { + export interface KnowledgeDocument { + id: number; // 编号 + knowledgeId: number; // 知识库编号 + name: string; // 文档名称 + contentLength: number; // 字符数 + tokens: number; // token 数 + segmentMaxTokens: number; // 分片最大 token 数 + retrievalCount: number; // 召回次数 + status: number; // 是否启用 + } +} + +/** 查询知识库文档分页 */ +export function getKnowledgeDocumentPage(params: PageParam) { + return requestClient.get< + PageResult + >('/ai/knowledge/document/page', { params }); +} + +/** 查询知识库文档详情 */ +export function getKnowledgeDocument(id: number) { + return requestClient.get(`/ai/knowledge/document/get?id=${id}`); +} + +/** 新增知识库文档(单个) */ +export function createKnowledge(data: any) { + return requestClient.post('/ai/knowledge/document/create', data); +} + +/** 新增知识库文档(多个) */ +export function createKnowledgeDocumentList(data: any) { + return requestClient.post('/ai/knowledge/document/create-list', data); +} + +/** 修改知识库文档 */ +export function updateKnowledgeDocument(data: any) { + return requestClient.put('/ai/knowledge/document/update', data); +} + +/** 修改知识库文档状态 */ +export function updateKnowledgeDocumentStatus(data: any) { + return requestClient.put('/ai/knowledge/document/update-status', data); +} + +/** 删除知识库文档 */ +export function deleteKnowledgeDocument(id: number) { + return requestClient.delete(`/ai/knowledge/document/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/knowledge/knowledge/index.ts b/apps/web-antd/src/api/ai/knowledge/knowledge/index.ts new file mode 100644 index 000000000..84d95e9a6 --- /dev/null +++ b/apps/web-antd/src/api/ai/knowledge/knowledge/index.ts @@ -0,0 +1,51 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiKnowledgeKnowledgeApi { + export interface Knowledge { + id: number; // 编号 + name: string; // 知识库名称 + description: string; // 知识库描述 + embeddingModelId: number; // 嵌入模型编号,高质量模式时维护 + topK: number; // topK + similarityThreshold: number; // 相似度阈值 + } +} + +/** 查询知识库分页 */ +export function getKnowledgePage(params: PageParam) { + return requestClient.get>( + '/ai/knowledge/page', + { params }, + ); +} + +/** 查询知识库详情 */ +export function getKnowledge(id: number) { + return requestClient.get( + `/ai/knowledge/get?id=${id}`, + ); +} + +/** 新增知识库 */ +export function createKnowledge(data: AiKnowledgeKnowledgeApi.Knowledge) { + return requestClient.post('/ai/knowledge/create', data); +} + +/** 修改知识库 */ +export function updateKnowledge(data: AiKnowledgeKnowledgeApi.Knowledge) { + return requestClient.put('/ai/knowledge/update', data); +} + +/** 删除知识库 */ +export function deleteKnowledge(id: number) { + return requestClient.delete(`/ai/knowledge/delete?id=${id}`); +} + +/** 获取知识库简单列表 */ +export function getSimpleKnowledgeList() { + return requestClient.get( + '/ai/knowledge/simple-list', + ); +} diff --git a/apps/web-antd/src/api/ai/knowledge/segment/index.ts b/apps/web-antd/src/api/ai/knowledge/segment/index.ts new file mode 100644 index 000000000..577ce0b13 --- /dev/null +++ b/apps/web-antd/src/api/ai/knowledge/segment/index.ts @@ -0,0 +1,81 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiKnowledgeSegmentApi { + export interface KnowledgeSegment { + id: number; // 编号 + documentId: number; // 文档编号 + knowledgeId: number; // 知识库编号 + vectorId: string; // 向量库编号 + content: string; // 切片内容 + contentLength: number; // 切片内容长度 + tokens: number; // token 数量 + retrievalCount: number; // 召回次数 + status: number; // 文档状态 + createTime: number; // 创建时间 + } +} + +/** 查询知识库分段分页 */ +export function getKnowledgeSegmentPage(params: PageParam) { + return requestClient.get>( + '/ai/knowledge/segment/page', + { params }, + ); +} + +/** 查询知识库分段详情 */ +export function getKnowledgeSegment(id: number) { + return requestClient.get( + `/ai/knowledge/segment/get?id=${id}`, + ); +} + +/** 新增知识库分段 */ +export function createKnowledgeSegment( + data: AiKnowledgeSegmentApi.KnowledgeSegment, +) { + return requestClient.post('/ai/knowledge/segment/create', data); +} + +/** 修改知识库分段 */ +export function updateKnowledgeSegment( + data: AiKnowledgeSegmentApi.KnowledgeSegment, +) { + return requestClient.put('/ai/knowledge/segment/update', data); +} + +/** 修改知识库分段状态 */ +export function updateKnowledgeSegmentStatus(id: number, status: number) { + return requestClient.put('/ai/knowledge/segment/update-status', { + id, + status, + }); +} + +/** 删除知识库分段 */ +export function deleteKnowledgeSegment(id: number) { + return requestClient.delete(`/ai/knowledge/segment/delete?id=${id}`); +} + +/** 切片内容 */ +export function splitContent(url: string, segmentMaxTokens: number) { + return requestClient.get('/ai/knowledge/segment/split', { + params: { url, segmentMaxTokens }, + }); +} + +/** 获取文档处理列表 */ +export function getKnowledgeSegmentProcessList(documentIds: number[]) { + return requestClient.get('/ai/knowledge/segment/get-process-list', { + params: { documentIds: documentIds.join(',') }, + }); +} + +/** 搜索知识库分段 */ +export function searchKnowledgeSegment(params: any) { + return requestClient.get('/ai/knowledge/segment/search', { + params, + }); +} diff --git a/apps/web-antd/src/api/ai/mindmap/index.ts b/apps/web-antd/src/api/ai/mindmap/index.ts new file mode 100644 index 000000000..54705afa7 --- /dev/null +++ b/apps/web-antd/src/api/ai/mindmap/index.ts @@ -0,0 +1,64 @@ +import { useAppConfig } from '@vben/hooks'; +import { fetchEventSource } from '@vben/request'; +import { useAccessStore } from '@vben/stores'; + +import { requestClient } from '#/api/request'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); +const accessStore = useAccessStore(); + +export namespace AiMindmapApi { + export interface MindMap { + id: number; // 编号 + userId: number; // 用户编号 + prompt: string; // 生成内容提示 + generatedContent: string; // 生成的思维导图内容 + platform: string; // 平台 + model: string; // 模型 + errorMessage: string; // 错误信息 + } + + export interface AiMindMapGenerateReqVO { + prompt: string; + } +} + +/** 生成思维导图 Stream */ +export function generateMindMap({ + data, + onClose, + onMessage, + onError, + ctrl, +}: { + ctrl: AbortController; + data: AiMindmapApi.AiMindMapGenerateReqVO; + onClose?: (...args: any[]) => void; + onError?: (...args: any[]) => void; + onMessage?: (res: any) => void; +}) { + const token = accessStore.accessToken; + return fetchEventSource(`${apiURL}/ai/mind-map/generate-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + openWhenHidden: true, + body: JSON.stringify(data), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal, + }); +} + +/** 查询思维导图分页 */ +export function getMindMapPage(params: any) { + return requestClient.get(`/ai/mind-map/page`, { params }); +} + +/** 删除思维导图 */ +export function deleteMindMap(id: number) { + return requestClient.delete(`/ai/mind-map/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/model/apiKey/index.ts b/apps/web-antd/src/api/ai/model/apiKey/index.ts new file mode 100644 index 000000000..2ffc842d8 --- /dev/null +++ b/apps/web-antd/src/api/ai/model/apiKey/index.ts @@ -0,0 +1,49 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiModelApiKeyApi { + export interface ApiKey { + id: number; // 编号 + name: string; // 名称 + apiKey: string; // 密钥 + platform: string; // 平台 + url: string; // 自定义 API 地址 + status: number; // 状态 + } +} + +/** 查询 API 密钥分页 */ +export function getApiKeyPage(params: PageParam) { + return requestClient.get>( + '/ai/api-key/page', + { params }, + ); +} + +/** 获得 API 密钥列表 */ +export function getApiKeySimpleList() { + return requestClient.get( + '/ai/api-key/simple-list', + ); +} + +/** 查询 API 密钥详情 */ +export function getApiKey(id: number) { + return requestClient.get(`/ai/api-key/get?id=${id}`); +} + +/** 新增 API 密钥 */ +export function createApiKey(data: AiModelApiKeyApi.ApiKey) { + return requestClient.post('/ai/api-key/create', data); +} + +/** 修改 API 密钥 */ +export function updateApiKey(data: AiModelApiKeyApi.ApiKey) { + return requestClient.put('/ai/api-key/update', data); +} + +/** 删除 API 密钥 */ +export function deleteApiKey(id: number) { + return requestClient.delete(`/ai/api-key/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/model/chatRole/index.ts b/apps/web-antd/src/api/ai/model/chatRole/index.ts new file mode 100644 index 000000000..266ca3474 --- /dev/null +++ b/apps/web-antd/src/api/ai/model/chatRole/index.ts @@ -0,0 +1,85 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiModelChatRoleApi { + export interface ChatRole { + id: number; // 角色编号 + modelId: number; // 模型编号 + name: string; // 角色名称 + avatar: string; // 角色头像 + category: string; // 角色类别 + sort: number; // 角色排序 + description: string; // 角色描述 + systemMessage: string; // 角色设定 + welcomeMessage: string; // 角色设定 + publicStatus: boolean; // 是否公开 + status: number; // 状态 + knowledgeIds?: number[]; // 引用的知识库 ID 列表 + toolIds?: number[]; // 引用的工具 ID 列表 + } + + // AI 聊天角色 分页请求 + export interface ChatRolePageReqVO { + name?: string; // 角色名称 + category?: string; // 角色类别 + publicStatus: boolean; // 是否公开 + pageNo: number; // 是否公开 + pageSize: number; // 是否公开 + } +} + +/** 查询聊天角色分页 */ +export function getChatRolePage(params: PageParam) { + return requestClient.get>( + '/ai/chat-role/page', + { params }, + ); +} + +/** 查询聊天角色详情 */ +export function getChatRole(id: number) { + return requestClient.get( + `/ai/chat-role/get?id=${id}`, + ); +} + +/** 新增聊天角色 */ +export function createChatRole(data: AiModelChatRoleApi.ChatRole) { + return requestClient.post('/ai/chat-role/create', data); +} + +/** 修改聊天角色 */ +export function updateChatRole(data: AiModelChatRoleApi.ChatRole) { + return requestClient.put('/ai/chat-role/update', data); +} + +/** 删除聊天角色 */ +export function deleteChatRole(id: number) { + return requestClient.delete(`/ai/chat-role/delete?id=${id}`); +} + +/** 获取 my role */ +export function getMyPage(params: AiModelChatRoleApi.ChatRolePageReqVO) { + return requestClient.get('/ai/chat-role/my-page', { params }); +} + +/** 获取角色分类 */ +export function getCategoryList() { + return requestClient.get('/ai/chat-role/category-list'); +} + +/** 创建角色 */ +export function createMy(data: AiModelChatRoleApi.ChatRole) { + return requestClient.post('/ai/chat-role/create-my', data); +} + +/** 更新角色 */ +export function updateMy(data: AiModelChatRoleApi.ChatRole) { + return requestClient.put('/ai/chat-role/update', data); +} + +/** 删除角色 my */ +export function deleteMy(id: number) { + return requestClient.delete(`/ai/chat-role/delete-my?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/model/model/index.ts b/apps/web-antd/src/api/ai/model/model/index.ts new file mode 100644 index 000000000..364327cc1 --- /dev/null +++ b/apps/web-antd/src/api/ai/model/model/index.ts @@ -0,0 +1,56 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiModelModelApi { + export interface Model { + id: number; // 编号 + keyId: number; // API 秘钥编号 + name: string; // 模型名字 + model: string; // 模型标识 + platform: string; // 模型平台 + type: number; // 模型类型 + sort: number; // 排序 + status: number; // 状态 + temperature?: number; // 温度参数 + maxTokens?: number; // 单条回复的最大 Token 数量 + maxContexts?: number; // 上下文的最大 Message 数量 + } +} + +/** 查询模型分页 */ +export function getModelPage(params: PageParam) { + return requestClient.get>( + '/ai/model/page', + { params }, + ); +} + +/** 获得模型列表 */ +export function getModelSimpleList(type?: number) { + return requestClient.get('/ai/model/simple-list', { + params: { + type, + }, + }); +} + +/** 查询模型详情 */ +export function getModel(id: number) { + return requestClient.get(`/ai/model/get?id=${id}`); +} + +/** 新增模型 */ +export function createModel(data: AiModelModelApi.Model) { + return requestClient.post('/ai/model/create', data); +} + +/** 修改模型 */ +export function updateModel(data: AiModelModelApi.Model) { + return requestClient.put('/ai/model/update', data); +} + +/** 删除模型 */ +export function deleteModel(id: number) { + return requestClient.delete(`/ai/model/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/model/tool/index.ts b/apps/web-antd/src/api/ai/model/tool/index.ts new file mode 100644 index 000000000..03abc4e7b --- /dev/null +++ b/apps/web-antd/src/api/ai/model/tool/index.ts @@ -0,0 +1,44 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiModelToolApi { + export interface Tool { + id: number; // 工具编号 + name: string; // 工具名称 + description: string; // 工具描述 + status: number; // 状态 + } +} + +/** 查询工具分页 */ +export function getToolPage(params: PageParam) { + return requestClient.get>('/ai/tool/page', { + params, + }); +} + +/** 查询工具详情 */ +export function getTool(id: number) { + return requestClient.get(`/ai/tool/get?id=${id}`); +} + +/** 新增工具 */ +export function createTool(data: AiModelToolApi.Tool) { + return requestClient.post('/ai/tool/create', data); +} + +/** 修改工具 */ +export function updateTool(data: AiModelToolApi.Tool) { + return requestClient.put('/ai/tool/update', data); +} + +/** 删除工具 */ +export function deleteTool(id: number) { + return requestClient.delete(`/ai/tool/delete?id=${id}`); +} + +/** 获取工具简单列表 */ +export function getToolSimpleList() { + return requestClient.get('/ai/tool/simple-list'); +} diff --git a/apps/web-antd/src/api/ai/music/index.ts b/apps/web-antd/src/api/ai/music/index.ts new file mode 100644 index 000000000..d3132a8d5 --- /dev/null +++ b/apps/web-antd/src/api/ai/music/index.ts @@ -0,0 +1,44 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiMusicApi { + // AI 音乐 + export interface Music { + id: number; // 编号 + userId: number; // 用户编号 + title: string; // 音乐名称 + lyric: string; // 歌词 + imageUrl: string; // 图片地址 + audioUrl: string; // 音频地址 + videoUrl: string; // 视频地址 + status: number; // 音乐状态 + gptDescriptionPrompt: string; // 描述词 + prompt: string; // 提示词 + platform: string; // 模型平台 + model: string; // 模型 + generateMode: number; // 生成模式 + tags: string; // 音乐风格标签 + duration: number; // 音乐时长 + publicStatus: boolean; // 是否发布 + taskId: string; // 任务id + errorMessage: string; // 错误信息 + } +} + +/** 查询音乐分页 */ +export function getMusicPage(params: PageParam) { + return requestClient.get>(`/ai/music/page`, { + params, + }); +} + +/** 更新音乐 */ +export function updateMusic(data: any) { + return requestClient.put('/ai/music/update', data); +} + +/** 删除音乐 */ +export function deleteMusic(id: number) { + return requestClient.delete(`/ai/music/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/ai/workflow/index.ts b/apps/web-antd/src/api/ai/workflow/index.ts new file mode 100644 index 000000000..676d6337c --- /dev/null +++ b/apps/web-antd/src/api/ai/workflow/index.ts @@ -0,0 +1,49 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AiWorkflowApi { + /** 工作流 */ + export interface Workflow { + id?: number; // 编号 + name: string; // 工作流名称 + code: string; // 工作流标识 + graph: string; // 工作流模型 JSON 数据 + remark?: string; // 备注 + status: number; // 状态 + createTime?: Date; // 创建时间 + } +} + +/** 查询工作流管理列表 */ +export function getWorkflowPage(params: PageParam) { + return requestClient.get>( + '/ai/workflow/page', + { params }, + ); +} + +/** 查询工作流详情 */ +export function getWorkflow(id: number) { + return requestClient.get(`/ai/workflow/get?id=${id}`); +} + +/** 新增工作流 */ +export function createWorkflow(data: AiWorkflowApi.Workflow) { + return requestClient.post('/ai/workflow/create', data); +} + +/** 修改工作流 */ +export function updateWorkflow(data: AiWorkflowApi.Workflow) { + return requestClient.put('/ai/workflow/update', data); +} + +/** 删除工作流 */ +export function deleteWorkflow(id: number) { + return requestClient.delete(`/ai/workflow/delete?id=${id}`); +} + +/** 测试工作流 */ +export function testWorkflow(data: any) { + return requestClient.post('/ai/workflow/test', data); +} diff --git a/apps/web-antd/src/api/ai/write/index.ts b/apps/web-antd/src/api/ai/write/index.ts new file mode 100644 index 000000000..25450342b --- /dev/null +++ b/apps/web-antd/src/api/ai/write/index.ts @@ -0,0 +1,79 @@ +import type { AiWriteTypeEnum } from '@vben/constants'; +import type { PageParam, PageResult } from '@vben/request'; + +import { useAppConfig } from '@vben/hooks'; +import { fetchEventSource } from '@vben/request'; +import { useAccessStore } from '@vben/stores'; + +import { requestClient } from '#/api/request'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); +const accessStore = useAccessStore(); + +export namespace AiWriteApi { + export interface Write { + id?: number; + type: AiWriteTypeEnum.REPLY | AiWriteTypeEnum.WRITING; // 1:撰写 2:回复 + prompt: string; // 写作内容提示 1。撰写 2回复 + originalContent: string; // 原文 + length: number; // 长度 + format: number; // 格式 + tone: number; // 语气 + language: number; // 语言 + userId?: number; // 用户编号 + platform?: string; // 平台 + model?: string; // 模型 + generatedContent?: string; // 生成的内容 + errorMessage?: string; // 错误信息 + createTime?: Date; // 创建时间 + } + + export interface AiWritePageReqVO extends PageParam { + userId?: number; // 用户编号 + type?: AiWriteTypeEnum; // 写作类型 + platform?: string; // 平台 + createTime?: [string, string]; // 创建时间 + } +} + +/** 写作 Stream */ +export function writeStream({ + data, + onClose, + onMessage, + onError, + ctrl, +}: { + ctrl: AbortController; + data: Partial; + onClose?: (...args: any[]) => void; + onError?: (...args: any[]) => void; + onMessage?: (res: any) => void; +}) { + const token = accessStore.accessToken; + return fetchEventSource(`${apiURL}/ai/write/generate-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + openWhenHidden: true, + body: JSON.stringify(data), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal, + }); +} + +/** 获取写作列表 */ +export function getWritePage(params: AiWriteApi.AiWritePageReqVO) { + return requestClient.get>(`/ai/write/page`, { + params, + }); +} + +/** 删除写作记录 */ +export function deleteWrite(id: number) { + return requestClient.delete(`/ai/write/delete`, { params: { id } }); +} diff --git a/apps/web-antd/src/api/bpm/category/index.ts b/apps/web-antd/src/api/bpm/category/index.ts new file mode 100644 index 000000000..64cf36cb4 --- /dev/null +++ b/apps/web-antd/src/api/bpm/category/index.ts @@ -0,0 +1,60 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace BpmCategoryApi { + /** 流程分类 */ + export interface Category { + id: number; + name: string; + code: string; + status: number; + description?: string; + sort: number; + } +} + +/** 查询流程分类分页 */ +export async function getCategoryPage(params: PageParam) { + return requestClient.get>( + '/bpm/category/page', + { params }, + ); +} + +/** 查询流程分类详情 */ +export async function getCategory(id: number) { + return requestClient.get( + `/bpm/category/get?id=${id}`, + ); +} + +/** 新增流程分类 */ +export async function createCategory(data: BpmCategoryApi.Category) { + return requestClient.post('/bpm/category/create', data); +} + +/** 修改流程分类 */ +export async function updateCategory(data: BpmCategoryApi.Category) { + return requestClient.put('/bpm/category/update', data); +} + +/** 删除流程分类 */ +export async function deleteCategory(id: number) { + return requestClient.delete(`/bpm/category/delete?id=${id}`); +} + +/** 查询流程分类列表 */ +export async function getCategorySimpleList() { + return requestClient.get( + `/bpm/category/simple-list`, + ); +} + +/** 批量修改流程分类的排序 */ +export async function updateCategorySortBatch(ids: number[]) { + const params = ids.join(','); + return requestClient.put( + `/bpm/category/update-sort-batch?ids=${params}`, + ); +} diff --git a/apps/web-antd/src/api/bpm/definition/index.ts b/apps/web-antd/src/api/bpm/definition/index.ts new file mode 100644 index 000000000..01f0fd2a5 --- /dev/null +++ b/apps/web-antd/src/api/bpm/definition/index.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { BpmModelApi } from '#/api/bpm/model'; + +import { requestClient } from '#/api/request'; + +export namespace BpmProcessDefinitionApi { + /** 流程定义 */ + export interface ProcessDefinition { + id: string; + key?: string; + version: number; + name: string; + category: string; + description: string; + deploymentTime: number; + suspensionState: number; + modelType: number; + modelId: string; + formType?: number; + formId?: number; + formName?: string; + formCustomCreatePath?: string; + bpmnXml?: string; + simpleModel?: string; + formFields?: string[]; + icon?: string; + startUsers?: BpmModelApi.UserInfo[]; + } +} + +/** 查询流程定义 */ +export async function getProcessDefinition(id?: string, key?: string) { + return requestClient.get( + '/bpm/process-definition/get', + { + params: { id, key }, + }, + ); +} + +/** 分页查询流程定义 */ +export async function getProcessDefinitionPage(params: PageParam) { + return requestClient.get< + PageResult + >('/bpm/process-definition/page', { params }); +} + +/** 查询流程定义列表 */ +export async function getProcessDefinitionList(params: any) { + return requestClient.get( + '/bpm/process-definition/list', + { + params, + }, + ); +} + +/** 查询流程定义列表(简单列表) */ +export async function getSimpleProcessDefinitionList() { + return requestClient.get< + PageResult + >('/bpm/process-definition/simple-list'); +} diff --git a/apps/web-antd/src/api/bpm/form/index.ts b/apps/web-antd/src/api/bpm/form/index.ts new file mode 100644 index 000000000..99c1299e9 --- /dev/null +++ b/apps/web-antd/src/api/bpm/form/index.ts @@ -0,0 +1,48 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace BpmFormApi { + /** 流程表单 */ + export interface Form { + id?: number; + name: string; + conf: string; + fields: string[]; + status: number; + remark: string; + createTime: number; + } +} + +/** 获取表单分页列表 */ +export async function getFormPage(params: PageParam) { + return requestClient.get>('/bpm/form/page', { + params, + }); +} + +/** 获取表单详情 */ +export async function getForm(id: number) { + return requestClient.get(`/bpm/form/get?id=${id}`); +} + +/** 创建表单 */ +export async function createForm(data: BpmFormApi.Form) { + return requestClient.post('/bpm/form/create', data); +} + +/** 更新表单 */ +export async function updateForm(data: BpmFormApi.Form) { + return requestClient.put('/bpm/form/update', data); +} + +/** 删除表单 */ +export async function deleteForm(id: number) { + return requestClient.delete(`/bpm/form/delete?id=${id}`); +} + +/** 获取表单简单列表 */ +export async function getFormSimpleList() { + return requestClient.get('/bpm/form/simple-list'); +} diff --git a/apps/web-antd/src/api/bpm/model/index.ts b/apps/web-antd/src/api/bpm/model/index.ts new file mode 100644 index 000000000..545a73c03 --- /dev/null +++ b/apps/web-antd/src/api/bpm/model/index.ts @@ -0,0 +1,111 @@ +import { requestClient } from '#/api/request'; + +export namespace BpmModelApi { + /** 流程模型 */ + export interface Model { + id: number; + key: string; + name: string; + icon?: string; + description: string; + category: string; + formName: string; + formType: number; + formId: number; + formCustomCreatePath: string; + formCustomViewPath: string; + processDefinition: ProcessDefinition; + status: number; + remark: string; + createTime: string; + bpmnXml: string; + startUsers?: UserInfo[]; + } + + /** 流程定义 */ + export interface ProcessDefinition { + id: string; + key?: string; + version: number; + deploymentTime: number; + suspensionState: number; + formType?: number; + formCustomViewPath?: string; + formFields?: string[]; + } + + /** 用户信息 */ + export interface UserInfo { + id: number; + nickname: string; + avatar?: string; + deptId?: number; + deptName?: string; + } +} + +/** 模型分类信息 */ +export interface ModelCategoryInfo { + id: number; + name: string; + modelList: BpmModelApi.Model[]; +} + +/** 获取流程模型列表 */ +export async function getModelList(name?: string) { + return requestClient.get('/bpm/model/list', { + params: { name }, + }); +} + +/** 获取流程模型详情 */ +export async function getModel(id: string) { + return requestClient.get(`/bpm/model/get?id=${id}`); +} + +/** 更新流程模型 */ +export async function updateModel(data: BpmModelApi.Model) { + return requestClient.put('/bpm/model/update', data); +} + +/** 批量修改流程模型排序 */ +export async function updateModelSortBatch(ids: number[]) { + const params = ids.join(','); + return requestClient.put( + `/bpm/model/update-sort-batch?ids=${params}`, + ); +} + +/** 更新流程模型的 BPMN XML */ +export async function updateModelBpmn(data: BpmModelApi.Model) { + return requestClient.put('/bpm/model/update-bpmn', data); +} + +/** 更新流程模型状态 */ +export async function updateModelState(id: number, state: number) { + const data = { + id, + state, + }; + return requestClient.put('/bpm/model/update-state', data); +} + +/** 创建流程模型 */ +export async function createModel(data: BpmModelApi.Model) { + return requestClient.post('/bpm/model/create', data); +} + +/** 删除流程模型 */ +export async function deleteModel(id: number) { + return requestClient.delete(`/bpm/model/delete?id=${id}`); +} + +/** 部署流程模型 */ +export async function deployModel(id: number) { + return requestClient.post(`/bpm/model/deploy?id=${id}`); +} + +/** 清理流程模型 */ +export async function cleanModel(id: number) { + return requestClient.delete(`/bpm/model/clean?id=${id}`); +} diff --git a/apps/web-antd/src/api/bpm/processExpression/index.ts b/apps/web-antd/src/api/bpm/processExpression/index.ts new file mode 100644 index 000000000..c10b28708 --- /dev/null +++ b/apps/web-antd/src/api/bpm/processExpression/index.ts @@ -0,0 +1,55 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace BpmProcessExpressionApi { + /** 流程表达式 */ + export interface ProcessExpression { + id: number; // 编号 + name: string; // 表达式名字 + status: number; // 表达式状态 + expression: string; // 表达式 + } +} + +/** 查询流程表达式分页 */ +export async function getProcessExpressionPage(params: PageParam) { + return requestClient.get< + PageResult + >('/bpm/process-expression/page', { params }); +} + +/** 查询流程表达式详情 */ +export async function getProcessExpression(id: number) { + return requestClient.get( + `/bpm/process-expression/get?id=${id}`, + ); +} + +/** 新增流程表达式 */ +export async function createProcessExpression( + data: BpmProcessExpressionApi.ProcessExpression, +) { + return requestClient.post('/bpm/process-expression/create', data); +} + +/** 修改流程表达式 */ +export async function updateProcessExpression( + data: BpmProcessExpressionApi.ProcessExpression, +) { + return requestClient.put('/bpm/process-expression/update', data); +} + +/** 删除流程表达式 */ +export async function deleteProcessExpression(id: number) { + return requestClient.delete( + `/bpm/process-expression/delete?id=${id}`, + ); +} + +/** 导出流程表达式 */ +export async function exportProcessExpression(params: any) { + return requestClient.download('/bpm/process-expression/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/bpm/processInstance/index.ts b/apps/web-antd/src/api/bpm/processInstance/index.ts new file mode 100644 index 000000000..7ee5421f8 --- /dev/null +++ b/apps/web-antd/src/api/bpm/processInstance/index.ts @@ -0,0 +1,221 @@ +import type { + BpmCandidateStrategyEnum, + BpmNodeTypeEnum, +} from '@vben/constants'; +import type { PageParam, PageResult } from '@vben/request'; + +import type { BpmTaskApi } from '../task'; + +import type { BpmModelApi } from '#/api/bpm/model'; + +import { requestClient } from '#/api/request'; + +export namespace BpmProcessInstanceApi { + /** 流程实例 */ + export interface ProcessInstance { + businessKey: string; + category: string; + categoryName?: string; + createTime: string; + endTime: string; + fields: string[]; + formVariables: Record; + id: number; + name: string; + processDefinition?: BpmModelApi.ProcessDefinition; + processDefinitionId: string; + remark: string; + result: number; + startTime?: Date; + startUser?: User; + status: number; + summary?: { + key: string; + value: string; + }[]; + tasks?: BpmProcessInstanceApi.Task[]; + } + + /** 流程实例的任务 */ + export interface Task { + id: number; + name: string; + assigneeUser?: User; + } + + /** 流程实例的用户信息 */ + export interface User { + id: number; + nickname: string; + avatar: string; + deptName?: string; + } + + /** 审批详情 */ + export interface ApprovalDetailRespVO { + activityNodes: BpmProcessInstanceApi.ApprovalNodeInfo[]; + formFieldsPermission: any; + processDefinition: BpmModelApi.ProcessDefinition; + processInstance: BpmProcessInstanceApi.ProcessInstance; + status: number; + todoTask: BpmTaskApi.Task; + } + + /** 审批详情的节点信息 */ + export interface ApprovalNodeInfo { + candidateStrategy?: BpmCandidateStrategyEnum; + candidateUsers?: User[]; + endTime?: Date; + id: string; + name: string; + nodeType: BpmNodeTypeEnum; + startTime?: Date; + status: number; + processInstanceId?: string; + tasks: ApprovalTaskInfo[]; + } + + /** 审批详情的节点的任务 */ + export interface ApprovalTaskInfo { + id: number; + assigneeUser: User; + ownerUser: User; + reason: string; + signPicUrl: string; + status: number; + } + + /** 抄送流程实例 */ + export interface ProcessInstanceCopyRespVO { + activityId: string; + activityName: string; + createTime: number; + createUser: User; + id: number; + processInstanceId: string; + processInstanceName: string; + processInstanceStartTime: number; + reason: string; + startUser: User; + summary: { + key: string; + value: string; + }[]; + taskId: string; + } + + /** 流程实例的打印数据响应 */ + export interface ProcessPrintDataRespVO { + printTemplateEnable: boolean; + printTemplateHtml?: string; + processInstance: ProcessInstance; + tasks: { + description: string; + id: number; + name: string; + signPicUrl?: string; + }[]; + } +} + +/** 查询我的流程实例分页 */ +export async function getProcessInstanceMyPage(params: PageParam) { + return requestClient.get>( + '/bpm/process-instance/my-page', + { params }, + ); +} + +/** 查询管理员流程实例分页 */ +export async function getProcessInstanceManagerPage(params: PageParam) { + return requestClient.get>( + '/bpm/process-instance/manager-page', + { params }, + ); +} + +/** 新增流程实例 */ +export async function createProcessInstance(data: any) { + return requestClient.post( + '/bpm/process-instance/create', + data, + ); +} + +/** 申请人主动取消流程实例 */ +export async function cancelProcessInstanceByStartUser( + id: number, + reason: string, +) { + return requestClient.delete( + '/bpm/process-instance/cancel-by-start-user', + { + data: { id, reason }, + }, + ); +} + +/** 管理员取消流程实例 */ +export async function cancelProcessInstanceByAdmin(id: number, reason: string) { + return requestClient.delete( + '/bpm/process-instance/cancel-by-admin', + { + data: { id, reason }, + }, + ); +} + +/** 查询流程实例详情 */ +export async function getProcessInstance(id: number) { + return requestClient.get( + `/bpm/process-instance/get?id=${id}`, + ); +} + +/** 查询复制流程实例分页 */ +export async function getProcessInstanceCopyPage(params: PageParam) { + return requestClient.get>( + '/bpm/process-instance/copy/page', + { params }, + ); +} + +/** 更新流程实例 */ +export async function updateProcessInstance( + data: BpmProcessInstanceApi.ProcessInstance, +) { + return requestClient.put( + '/bpm/process-instance/update', + data, + ); +} + +/** 获取审批详情 */ +export async function getApprovalDetail(params: any) { + return requestClient.get( + `/bpm/process-instance/get-approval-detail`, + { params }, + ); +} + +/** 获取下一个执行的流程节点 */ +export async function getNextApprovalNodes(params: any) { + return requestClient.get( + `/bpm/process-instance/get-next-approval-nodes`, + { params }, + ); +} + +/** 获取流程实例 BPMN 模型视图 */ +export async function getProcessInstanceBpmnModelView(id: string) { + return requestClient.get( + `/bpm/process-instance/get-bpmn-model-view?id=${id}`, + ); +} + +/** 获取流程实例打印数据 */ +export async function getProcessInstancePrintData(id: string) { + return requestClient.get( + `/bpm/process-instance/get-print-data?processInstanceId=${id}`, + ); +} diff --git a/apps/web-antd/src/api/bpm/processListener/index.ts b/apps/web-antd/src/api/bpm/processListener/index.ts new file mode 100644 index 000000000..a0262e742 --- /dev/null +++ b/apps/web-antd/src/api/bpm/processListener/index.ts @@ -0,0 +1,50 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace BpmProcessListenerApi { + /** 流程监听器 */ + export interface ProcessListener { + id: number; // 编号 + name: string; // 监听器名字 + type: string; // 监听器类型 + status: number; // 监听器状态 + event: string; // 监听事件 + valueType: string; // 监听器值类型 + value: string; // 监听器值 + } +} + +/** 查询流程监听器分页 */ +export async function getProcessListenerPage(params: PageParam) { + return requestClient.get>( + '/bpm/process-listener/page', + { params }, + ); +} + +/** 查询流程监听器详情 */ +export async function getProcessListener(id: number) { + return requestClient.get( + `/bpm/process-listener/get?id=${id}`, + ); +} + +/** 新增流程监听器 */ +export async function createProcessListener( + data: BpmProcessListenerApi.ProcessListener, +) { + return requestClient.post('/bpm/process-listener/create', data); +} + +/** 修改流程监听器 */ +export async function updateProcessListener( + data: BpmProcessListenerApi.ProcessListener, +) { + return requestClient.put('/bpm/process-listener/update', data); +} + +/** 删除流程监听器 */ +export async function deleteProcessListener(id: number) { + return requestClient.delete(`/bpm/process-listener/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/bpm/task/index.ts b/apps/web-antd/src/api/bpm/task/index.ts new file mode 100644 index 000000000..8572b7e6e --- /dev/null +++ b/apps/web-antd/src/api/bpm/task/index.ts @@ -0,0 +1,106 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { BpmProcessInstanceApi } from '../processInstance'; + +import { requestClient } from '#/api/request'; + +export namespace BpmTaskApi { + /** 流程任务 */ + export interface Task { + id: number; // 编号 + name: string; // 监听器名字 + type: string; // 监听器类型 + status: number; // 监听器状态 + event: string; // 监听事件 + valueType: string; // 监听器值类型 + processInstance?: BpmProcessInstanceApi.ProcessInstance; // 流程实例 + } +} + +/** 查询待办任务分页 */ +export async function getTaskTodoPage(params: PageParam) { + return requestClient.get>('/bpm/task/todo-page', { + params, + }); +} + +/** 查询已办任务分页 */ +export async function getTaskDonePage(params: PageParam) { + return requestClient.get>('/bpm/task/done-page', { + params, + }); +} + +/** 查询任务管理分页 */ +export async function getTaskManagerPage(params: PageParam) { + return requestClient.get>( + '/bpm/task/manager-page', + { params }, + ); +} + +/** 审批任务 */ +export const approveTask = async (data: any) => { + return await requestClient.put('/bpm/task/approve', data); +}; + +/** 驳回任务 */ +export const rejectTask = async (data: any) => { + return await requestClient.put('/bpm/task/reject', data); +}; + +/** 根据流程实例 ID 查询任务列表 */ +export const getTaskListByProcessInstanceId = async (id: string) => { + return await requestClient.get( + `/bpm/task/list-by-process-instance-id?processInstanceId=${id}`, + ); +}; + +/** 获取所有可退回的节点 */ +export const getTaskListByReturn = async (id: string) => { + return await requestClient.get(`/bpm/task/list-by-return?id=${id}`); +}; + +/** 退回任务 */ +export const returnTask = async (data: any) => { + return await requestClient.put('/bpm/task/return', data); +}; + +/** 委派任务 */ +export const delegateTask = async (data: any) => { + return await requestClient.put('/bpm/task/delegate', data); +}; + +/** 转派任务 */ +export const transferTask = async (data: any) => { + return await requestClient.put('/bpm/task/transfer', data); +}; + +/** 加签任务 */ +export const signCreateTask = async (data: any) => { + return await requestClient.put('/bpm/task/create-sign', data); +}; + +/** 减签任务 */ +export const signDeleteTask = async (data: any) => { + return await requestClient.delete('/bpm/task/delete-sign', data); +}; + +/** 抄送任务 */ +export const copyTask = async (data: any) => { + return await requestClient.put('/bpm/task/copy', data); +}; + +/** 获取加签任务列表 */ +export const getChildrenTaskList = async (id: string) => { + return await requestClient.get( + `/bpm/task/list-by-parent-task-id?parentTaskId=${id}`, + ); +}; + +/** 撤回任务 */ +export const withdrawTask = async (taskId: string) => { + return await requestClient.put('/bpm/task/withdraw', null, { + params: { taskId }, + }); +}; diff --git a/apps/web-antd/src/api/bpm/userGroup/index.ts b/apps/web-antd/src/api/bpm/userGroup/index.ts new file mode 100644 index 000000000..6e23410a9 --- /dev/null +++ b/apps/web-antd/src/api/bpm/userGroup/index.ts @@ -0,0 +1,53 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace BpmUserGroupApi { + /** 用户组 */ + export interface UserGroup { + id: number; + name: string; + description: string; + userIds: number[]; + status: number; + remark: string; + createTime: string; + } +} + +/** 查询用户组分页 */ +export async function getUserGroupPage(params: PageParam) { + return requestClient.get>( + '/bpm/user-group/page', + { params }, + ); +} + +/** 查询用户组详情 */ +export async function getUserGroup(id: number) { + return requestClient.get( + `/bpm/user-group/get?id=${id}`, + ); +} + +/** 新增用户组 */ +export async function createUserGroup(data: BpmUserGroupApi.UserGroup) { + return requestClient.post('/bpm/user-group/create', data); +} + +/** 修改用户组 */ +export async function updateUserGroup(data: BpmUserGroupApi.UserGroup) { + return requestClient.put('/bpm/user-group/update', data); +} + +/** 删除用户组 */ +export async function deleteUserGroup(id: number) { + return requestClient.delete(`/bpm/user-group/delete?id=${id}`); +} + +/** 查询用户组列表 */ +export async function getUserGroupSimpleList() { + return requestClient.get( + `/bpm/user-group/simple-list`, + ); +} diff --git a/apps/web-antd/src/api/core/auth.ts b/apps/web-antd/src/api/core/auth.ts new file mode 100644 index 000000000..604644a1f --- /dev/null +++ b/apps/web-antd/src/api/core/auth.ts @@ -0,0 +1,161 @@ +import type { AuthPermissionInfo } from '@vben/types'; + +import { baseRequestClient, requestClient } from '#/api/request'; + +export namespace AuthApi { + /** 登录接口参数 */ + export interface LoginParams { + password?: string; + username?: string; + captchaVerification?: string; + // 绑定社交登录时,需要传递如下参数 + socialType?: number; + socialCode?: string; + socialState?: string; + } + + /** 登录接口返回值 */ + export interface LoginResult { + accessToken: string; + refreshToken: string; + userId: number; + expiresTime: number; + } + + /** 租户信息返回值 */ + export interface TenantResult { + id: number; + name: string; + } + + /** 手机验证码获取接口参数 */ + export interface SmsCodeParams { + mobile: string; + scene: number; + } + + /** 手机验证码登录接口参数 */ + export interface SmsLoginParams { + mobile: string; + code: string; + } + + /** 注册接口参数 */ + export interface RegisterParams { + username: string; + password: string; + captchaVerification: string; + } + + /** 重置密码接口参数 */ + export interface ResetPasswordParams { + password: string; + mobile: string; + code: string; + } + + /** 社交快捷登录接口参数 */ + export interface SocialLoginParams { + type: number; + code: string; + state: string; + } +} + +/** 登录 */ +export async function loginApi(data: AuthApi.LoginParams) { + return requestClient.post('/system/auth/login', data, { + headers: { + isEncrypt: false, + }, + }); +} + +/** 刷新 accessToken */ +export async function refreshTokenApi(refreshToken: string) { + return baseRequestClient.post( + `/system/auth/refresh-token?refreshToken=${refreshToken}`, + ); +} + +/** 退出登录 */ +export async function logoutApi(accessToken: string) { + return baseRequestClient.post( + '/system/auth/logout', + {}, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }, + ); +} + +/** 获取权限信息 */ +export async function getAuthPermissionInfoApi() { + return requestClient.get( + '/system/auth/get-permission-info', + ); +} + +/** 获取租户列表 */ +export async function getTenantSimpleList() { + return requestClient.get( + `/system/tenant/simple-list`, + ); +} + +/** 使用租户域名,获得租户信息 */ +export async function getTenantByWebsite(website: string) { + return requestClient.get( + `/system/tenant/get-by-website?website=${website}`, + ); +} + +/** 获取验证码 */ +export async function getCaptcha(data: any) { + return baseRequestClient.post('/system/captcha/get', data); +} + +/** 校验验证码 */ +export async function checkCaptcha(data: any) { + return baseRequestClient.post('/system/captcha/check', data); +} + +/** 获取登录验证码 */ +export async function sendSmsCode(data: AuthApi.SmsCodeParams) { + return requestClient.post('/system/auth/send-sms-code', data); +} + +/** 短信验证码登录 */ +export async function smsLogin(data: AuthApi.SmsLoginParams) { + return requestClient.post('/system/auth/sms-login', data); +} + +/** 注册 */ +export async function register(data: AuthApi.RegisterParams) { + return requestClient.post('/system/auth/register', data); +} + +/** 通过短信重置密码 */ +export async function smsResetPassword(data: AuthApi.ResetPasswordParams) { + return requestClient.post('/system/auth/reset-password', data); +} + +/** 社交授权的跳转 */ +export async function socialAuthRedirect(type: number, redirectUri: string) { + return requestClient.get('/system/auth/social-auth-redirect', { + params: { + type, + redirectUri, + }, + }); +} + +/** 社交快捷登录 */ +export async function socialLogin(data: AuthApi.SocialLoginParams) { + return requestClient.post( + '/system/auth/social-login', + data, + ); +} diff --git a/apps/web-antd/src/api/crm/business/index.ts b/apps/web-antd/src/api/crm/business/index.ts new file mode 100644 index 000000000..7da62aeaa --- /dev/null +++ b/apps/web-antd/src/api/crm/business/index.ts @@ -0,0 +1,128 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { CrmPermissionApi } from '#/api/crm/permission'; + +import { requestClient } from '#/api/request'; + +export namespace CrmBusinessApi { + /** 商机信息 */ + export interface Business { + id: number; + name: string; + customerId: number; + customerName?: string; + followUpStatus: boolean; + contactLastTime: Date; + contactNextTime: Date; + ownerUserId: number; + ownerUserName?: string; // 负责人的用户名称 + ownerUserDept?: string; // 负责人的部门名称 + statusTypeId: number; + statusTypeName?: string; + statusId: number; + statusName?: string; + endStatus: number; + endRemark: string; + dealTime: Date; + totalProductPrice: number; + totalPrice: number; + discountPercent: number; + status?: number; + remark: string; + creator: string; // 创建人 + creatorName?: string; // 创建人名称 + createTime: Date; // 创建时间 + updateTime: Date; // 更新时间 + products?: BusinessProduct[]; + } + + /** 商机产品信息 */ + export interface BusinessProduct { + id: number; + productId: number; + productName: string; + productNo: string; + productUnit: number; + productPrice: number; + businessPrice: number; + count: number; + totalPrice: number; + } + + /** 商机更新状态请求 */ + export interface BusinessUpdateStatusReqVO { + id: number; + statusId: number | undefined; + endStatus: number | undefined; + } +} + +/** 查询商机列表 */ +export function getBusinessPage(params: PageParam) { + return requestClient.get>( + '/crm/business/page', + { params }, + ); +} + +/** 查询商机列表,基于指定客户 */ +export function getBusinessPageByCustomer(params: PageParam) { + return requestClient.get>( + '/crm/business/page-by-customer', + { params }, + ); +} + +/** 查询商机详情 */ +export function getBusiness(id: number) { + return requestClient.get( + `/crm/business/get?id=${id}`, + ); +} + +/** 获得商机列表(精简) */ +export function getSimpleBusinessList() { + return requestClient.get( + '/crm/business/simple-all-list', + ); +} + +/** 新增商机 */ +export function createBusiness(data: CrmBusinessApi.Business) { + return requestClient.post('/crm/business/create', data); +} + +/** 修改商机 */ +export function updateBusiness(data: CrmBusinessApi.Business) { + return requestClient.put('/crm/business/update', data); +} + +/** 修改商机状态 */ +export function updateBusinessStatus( + data: CrmBusinessApi.BusinessUpdateStatusReqVO, +) { + return requestClient.put('/crm/business/update-status', data); +} + +/** 删除商机 */ +export function deleteBusiness(id: number) { + return requestClient.delete(`/crm/business/delete?id=${id}`); +} + +/** 导出商机 */ +export function exportBusiness(params: any) { + return requestClient.download('/crm/business/export-excel', { params }); +} + +/** 联系人关联商机列表 */ +export function getBusinessPageByContact(params: PageParam) { + return requestClient.get>( + '/crm/business/page-by-contact', + { params }, + ); +} + +/** 商机转移 */ +export function transferBusiness(data: CrmPermissionApi.BusinessTransferReqVO) { + return requestClient.put('/crm/business/transfer', data); +} diff --git a/apps/web-antd/src/api/crm/business/status/index.ts b/apps/web-antd/src/api/crm/business/status/index.ts new file mode 100644 index 000000000..fb42abaff --- /dev/null +++ b/apps/web-antd/src/api/crm/business/status/index.ts @@ -0,0 +1,95 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmBusinessStatusApi { + /** 商机状态组信息 */ + export interface BusinessStatus { + id?: number; + name: string; + deptIds?: number[]; + deptNames?: string[]; + creator?: string; + createTime?: Date; + statuses?: BusinessStatusType[]; + } + + /** 商机状态信息 */ + export interface BusinessStatusType { + id?: number; + name: string; + percent: number; + [x: string]: any; + } +} + +/** 默认商机状态 */ +export const DEFAULT_STATUSES = [ + { + endStatus: 1, + key: '结束', + name: '赢单', + percent: 100, + }, + { + endStatus: 2, + key: '结束', + name: '输单', + percent: 0, + }, + { + endStatus: 3, + key: '结束', + name: '无效', + percent: 0, + }, +]; + +/** 查询商机状态组列表 */ +export function getBusinessStatusPage(params: PageParam) { + return requestClient.get>( + '/crm/business-status/page', + { params }, + ); +} + +/** 新增商机状态组 */ +export function createBusinessStatus( + data: CrmBusinessStatusApi.BusinessStatus, +) { + return requestClient.post('/crm/business-status/create', data); +} + +/** 修改商机状态组 */ +export function updateBusinessStatus( + data: CrmBusinessStatusApi.BusinessStatus, +) { + return requestClient.put('/crm/business-status/update', data); +} + +/** 查询商机状态类型详情 */ +export function getBusinessStatus(id: number) { + return requestClient.get( + `/crm/business-status/get?id=${id}`, + ); +} + +/** 删除商机状态 */ +export function deleteBusinessStatus(id: number) { + return requestClient.delete(`/crm/business-status/delete?id=${id}`); +} + +/** 获得商机状态组列表 */ +export function getBusinessStatusTypeSimpleList() { + return requestClient.get( + '/crm/business-status/type-simple-list', + ); +} + +/** 获得商机阶段列表 */ +export function getBusinessStatusSimpleList(typeId: number) { + return requestClient.get( + '/crm/business-status/status-simple-list', + { params: { typeId } }, + ); +} diff --git a/apps/web-antd/src/api/crm/clue/index.ts b/apps/web-antd/src/api/crm/clue/index.ts new file mode 100644 index 000000000..bcd23152c --- /dev/null +++ b/apps/web-antd/src/api/crm/clue/index.ts @@ -0,0 +1,86 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { CrmPermissionApi } from '#/api/crm/permission'; + +import { requestClient } from '#/api/request'; + +export namespace CrmClueApi { + /** 线索信息 */ + export interface Clue { + id: number; // 编号 + name: string; // 线索名称 + followUpStatus: boolean; // 跟进状态 + contactLastTime: Date; // 最后跟进时间 + contactLastContent: string; // 最后跟进内容 + contactNextTime: Date; // 下次联系时间 + ownerUserId: number; // 负责人的用户编号 + ownerUserName?: string; // 负责人的用户名称 + ownerUserDept?: string; // 负责人的部门名称 + transformStatus: boolean; // 转化状态 + customerId: number; // 客户编号 + customerName?: string; // 客户名称 + mobile: string; // 手机号 + telephone: string; // 电话 + qq: string; // QQ + wechat: string; // wechat + email: string; // email + areaId: number; // 所在地 + areaName?: string; // 所在地名称 + detailAddress: string; // 详细地址 + industryId: number; // 所属行业 + level: number; // 客户等级 + source: number; // 客户来源 + remark: string; // 备注 + creator: string; // 创建人 + creatorName?: string; // 创建人名称 + createTime: Date; // 创建时间 + updateTime: Date; // 更新时间 + } +} + +/** 查询线索列表 */ +export function getCluePage(params: PageParam) { + return requestClient.get>('/crm/clue/page', { + params, + }); +} + +/** 查询线索详情 */ +export function getClue(id: number) { + return requestClient.get(`/crm/clue/get?id=${id}`); +} + +/** 新增线索 */ +export function createClue(data: CrmClueApi.Clue) { + return requestClient.post('/crm/clue/create', data); +} + +/** 修改线索 */ +export function updateClue(data: CrmClueApi.Clue) { + return requestClient.put('/crm/clue/update', data); +} + +/** 删除线索 */ +export function deleteClue(id: number) { + return requestClient.delete(`/crm/clue/delete?id=${id}`); +} + +/** 导出线索 */ +export function exportClue(params: any) { + return requestClient.download('/crm/clue/export-excel', { params }); +} + +/** 线索转移 */ +export function transferClue(data: CrmPermissionApi.BusinessTransferReqVO) { + return requestClient.put('/crm/clue/transfer', data); +} + +/** 线索转化为客户 */ +export function transformClue(id: number) { + return requestClient.put(`/crm/clue/transform?id=${id}`); +} + +/** 获得分配给我的、待跟进的线索数量 */ +export function getFollowClueCount() { + return requestClient.get('/crm/clue/follow-count'); +} diff --git a/apps/web-antd/src/api/crm/contact/index.ts b/apps/web-antd/src/api/crm/contact/index.ts new file mode 100644 index 000000000..108b31060 --- /dev/null +++ b/apps/web-antd/src/api/crm/contact/index.ts @@ -0,0 +1,140 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { CrmPermissionApi } from '#/api/crm/permission'; + +import { requestClient } from '#/api/request'; + +export namespace CrmContactApi { + /** 联系人信息 */ + export interface Contact { + id: number; // 编号 + name: string; // 联系人名称 + customerId: number; // 客户编号 + customerName?: string; // 客户名称 + contactLastTime: Date; // 最后跟进时间 + contactLastContent: string; // 最后跟进内容 + contactNextTime: Date; // 下次联系时间 + ownerUserId: number; // 负责人的用户编号 + ownerUserName?: string; // 负责人的用户名称 + ownerUserDept?: string; // 负责人的部门名称 + mobile: string; // 手机号 + telephone: string; // 电话 + qq: string; // QQ + wechat: string; // wechat + email: string; // email + areaId: number; // 所在地 + areaName?: string; // 所在地名称 + detailAddress: string; // 详细地址 + sex: number; // 性别 + master: boolean; // 是否主联系人 + post: string; // 职务 + parentId: number; // 上级联系人编号 + parentName?: string; // 上级联系人名称 + remark: string; // 备注 + creator: string; // 创建人 + creatorName?: string; // 创建人名称 + createTime: Date; // 创建时间 + updateTime: Date; // 更新时间 + } + + /** 联系人商机关联请求 */ + export interface ContactBusinessReqVO { + contactId: number; + businessIds: number[]; + } + + /** 商机联系人关联请求 */ + export interface BusinessContactReqVO { + businessId: number; + contactIds: number[]; + } +} + +/** 查询联系人列表 */ +export function getContactPage(params: PageParam) { + return requestClient.get>( + '/crm/contact/page', + { params }, + ); +} + +/** 查询联系人列表,基于指定客户 */ +export function getContactPageByCustomer(params: PageParam) { + return requestClient.get>( + '/crm/contact/page-by-customer', + { params }, + ); +} + +/** 查询联系人列表,基于指定商机 */ +export function getContactPageByBusiness(params: PageParam) { + return requestClient.get>( + '/crm/contact/page-by-business', + { params }, + ); +} + +/** 查询联系人详情 */ +export function getContact(id: number) { + return requestClient.get(`/crm/contact/get?id=${id}`); +} + +/** 新增联系人 */ +export function createContact(data: CrmContactApi.Contact) { + return requestClient.post('/crm/contact/create', data); +} + +/** 修改联系人 */ +export function updateContact(data: CrmContactApi.Contact) { + return requestClient.put('/crm/contact/update', data); +} + +/** 删除联系人 */ +export function deleteContact(id: number) { + return requestClient.delete(`/crm/contact/delete?id=${id}`); +} + +/** 导出联系人 */ +export function exportContact(params: any) { + return requestClient.download('/crm/contact/export-excel', { params }); +} + +/** 获得联系人列表(精简) */ +export function getSimpleContactList() { + return requestClient.get( + '/crm/contact/simple-all-list', + ); +} + +/** 批量新增联系人商机关联 */ +export function createContactBusinessList( + data: CrmContactApi.ContactBusinessReqVO, +) { + return requestClient.post('/crm/contact/create-business-list', data); +} + +/** 批量新增商机联系人关联 */ +export function createBusinessContactList( + data: CrmContactApi.BusinessContactReqVO, +) { + return requestClient.post('/crm/contact/create-business-list2', data); +} + +/** 解除联系人商机关联 */ +export function deleteContactBusinessList( + data: CrmContactApi.ContactBusinessReqVO, +) { + return requestClient.delete('/crm/contact/delete-business-list', { data }); +} + +/** 解除商机联系人关联 */ +export function deleteBusinessContactList( + data: CrmContactApi.BusinessContactReqVO, +) { + return requestClient.delete('/crm/contact/delete-business-list2', { data }); +} + +/** 联系人转移 */ +export function transferContact(data: CrmPermissionApi.BusinessTransferReqVO) { + return requestClient.put('/crm/contact/transfer', data); +} diff --git a/apps/web-antd/src/api/crm/contract/index.ts b/apps/web-antd/src/api/crm/contract/index.ts new file mode 100644 index 000000000..e7ffc613c --- /dev/null +++ b/apps/web-antd/src/api/crm/contract/index.ts @@ -0,0 +1,133 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { CrmPermissionApi } from '#/api/crm/permission'; + +import { requestClient } from '#/api/request'; + +export namespace CrmContractApi { + /** 合同信息 */ + export interface Contract { + id: number; + name: string; + no: string; + customerId: number; + customerName?: string; + businessId: number; + businessName: string; + contactLastTime: Date; + ownerUserId: number; + ownerUserName?: string; + ownerUserDeptName?: string; + processInstanceId: number; + auditStatus: number; + orderDate: Date; + startTime: Date; + endTime: Date; + totalProductPrice: number; + discountPercent: number; + totalPrice: number; + totalReceivablePrice: number; + signContactId: number; + signContactName?: string; + signUserId: number; + signUserName: string; + remark: string; + createTime?: Date; + creator: string; + creatorName: string; + updateTime?: Date; + products?: ContractProduct[]; + contactName?: string; + } + + /** 合同产品信息 */ + export interface ContractProduct { + id: number; + productId: number; + productName: string; + productNo: string; + productUnit: number; + productPrice: number; + contractPrice: number; + count: number; + totalPrice: number; + } +} + +/** 查询合同列表 */ +export function getContractPage(params: PageParam) { + return requestClient.get>( + '/crm/contract/page', + { params }, + ); +} + +/** 查询合同列表,基于指定客户 */ +export function getContractPageByCustomer(params: PageParam) { + return requestClient.get>( + '/crm/contract/page-by-customer', + { params }, + ); +} + +/** 查询合同列表,基于指定商机 */ +export function getContractPageByBusiness(params: PageParam) { + return requestClient.get>( + '/crm/contract/page-by-business', + { params }, + ); +} + +/** 查询合同详情 */ +export function getContract(id: number) { + return requestClient.get( + `/crm/contract/get?id=${id}`, + ); +} + +/** 查询合同下拉列表 */ +export function getContractSimpleList(customerId: number) { + return requestClient.get( + `/crm/contract/simple-list?customerId=${customerId}`, + ); +} + +/** 新增合同 */ +export function createContract(data: CrmContractApi.Contract) { + return requestClient.post('/crm/contract/create', data); +} + +/** 修改合同 */ +export function updateContract(data: CrmContractApi.Contract) { + return requestClient.put('/crm/contract/update', data); +} + +/** 删除合同 */ +export function deleteContract(id: number) { + return requestClient.delete(`/crm/contract/delete?id=${id}`); +} + +/** 导出合同 */ +export function exportContract(params: any) { + return requestClient.download('/crm/contract/export-excel', { params }); +} + +/** 提交审核 */ +export function submitContract(id: number) { + return requestClient.put(`/crm/contract/submit?id=${id}`); +} + +/** 合同转移 */ +export function transferContract(data: CrmPermissionApi.BusinessTransferReqVO) { + return requestClient.put('/crm/contract/transfer', data); +} + +/** 获得待审核合同数量 */ +export function getAuditContractCount() { + return requestClient.get('/crm/contract/audit-count'); +} + +/** 获得即将到期(提醒)的合同数量 */ +export function getRemindContractCount() { + return requestClient.get('/crm/contract/remind-count'); +} diff --git a/apps/web-antd/src/api/crm/customer/index.ts b/apps/web-antd/src/api/crm/customer/index.ts new file mode 100644 index 000000000..038625fd7 --- /dev/null +++ b/apps/web-antd/src/api/crm/customer/index.ts @@ -0,0 +1,154 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { CrmPermissionApi } from '#/api/crm/permission'; + +import { requestClient } from '#/api/request'; + +export namespace CrmCustomerApi { + /** 客户信息 */ + export interface Customer { + id: number; // 编号 + name: string; // 客户名称 + followUpStatus: boolean; // 跟进状态 + contactLastTime: Date; // 最后跟进时间 + contactLastContent: string; // 最后跟进内容 + contactNextTime: Date; // 下次联系时间 + ownerUserId: number; // 负责人的用户编号 + ownerUserName?: string; // 负责人的用户名称 + ownerUserDept?: string; // 负责人的部门名称 + ownerUserDeptName?: string; // 负责人的部门名称 + lockStatus?: boolean; + dealStatus?: boolean; + mobile: string; // 手机号 + telephone: string; // 电话 + qq: string; // QQ + wechat: string; // wechat + email: string; // email + areaId: number; // 所在地 + areaName?: string; // 所在地名称 + detailAddress: string; // 详细地址 + industryId: number; // 所属行业 + level: number; // 客户等级 + source: number; // 客户来源 + remark: string; // 备注 + creator: string; // 创建人 + creatorName?: string; // 创建人名称 + createTime: Date; // 创建时间 + updateTime: Date; // 更新时间 + poolDay?: number; // 距离进入公海天数 + } + + /** 客户导入请求 */ + export interface CustomerImportReqVO { + ownerUserId: number; + file: File; + updateSupport: boolean; + } +} + +/** 查询客户列表 */ +export function getCustomerPage(params: PageParam) { + return requestClient.get>( + '/crm/customer/page', + { params }, + ); +} + +/** 查询客户详情 */ +export function getCustomer(id: number) { + return requestClient.get( + `/crm/customer/get?id=${id}`, + ); +} + +/** 新增客户 */ +export function createCustomer(data: CrmCustomerApi.Customer) { + return requestClient.post('/crm/customer/create', data); +} + +/** 修改客户 */ +export function updateCustomer(data: CrmCustomerApi.Customer) { + return requestClient.put('/crm/customer/update', data); +} + +/** 删除客户 */ +export function deleteCustomer(id: number) { + return requestClient.delete(`/crm/customer/delete?id=${id}`); +} + +/** 导出客户 */ +export function exportCustomer(params: any) { + return requestClient.download('/crm/customer/export-excel', { params }); +} + +/** 下载客户导入模板 */ +export function importCustomerTemplate() { + return requestClient.download('/crm/customer/get-import-template'); +} + +/** 导入客户 */ +export function importCustomer(data: CrmCustomerApi.CustomerImportReqVO) { + return requestClient.upload('/crm/customer/import', data); +} + +/** 获取客户精简信息列表 */ +export function getCustomerSimpleList() { + return requestClient.get( + '/crm/customer/simple-list', + ); +} + +/** 客户转移 */ +export function transferCustomer(data: CrmPermissionApi.BusinessTransferReqVO) { + return requestClient.put('/crm/customer/transfer', data); +} + +/** 锁定/解锁客户 */ +export function lockCustomer(id: number, lockStatus: boolean) { + return requestClient.put('/crm/customer/lock', { id, lockStatus }); +} + +/** 领取公海客户 */ +export function receiveCustomer(ids: number[]) { + return requestClient.put('/crm/customer/receive', { ids: ids.join(',') }); +} + +/** 分配公海给对应负责人 */ +export function distributeCustomer(ids: number[], ownerUserId: number) { + return requestClient.put('/crm/customer/distribute', { ids, ownerUserId }); +} + +/** 客户放入公海 */ +export function putCustomerPool(id: number) { + return requestClient.put(`/crm/customer/put-pool?id=${id}`); +} + +/** 更新客户的成交状态 */ +export function updateCustomerDealStatus(id: number, dealStatus: boolean) { + return requestClient.put( + `/crm/customer/update-deal-status?id=${id}&dealStatus=${dealStatus}`, + ); +} + +/** 进入公海客户提醒的客户列表 */ +export function getPutPoolRemindCustomerPage(params: PageParam) { + return requestClient.get>( + '/crm/customer/put-pool-remind-page', + { params }, + ); +} + +/** 获得待进入公海客户数量 */ +export function getPutPoolRemindCustomerCount() { + return requestClient.get('/crm/customer/put-pool-remind-count'); +} + +/** 获得今日需联系客户数量 */ +export function getTodayContactCustomerCount() { + return requestClient.get('/crm/customer/today-contact-count'); +} + +/** 获得分配给我、待跟进的线索数量的客户数量 */ +export function getFollowCustomerCount() { + return requestClient.get('/crm/customer/follow-count'); +} diff --git a/apps/web-antd/src/api/crm/followup/index.ts b/apps/web-antd/src/api/crm/followup/index.ts new file mode 100644 index 000000000..20592a9e1 --- /dev/null +++ b/apps/web-antd/src/api/crm/followup/index.ts @@ -0,0 +1,53 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmFollowUpApi { + /** 跟进记录信息 */ + export interface FollowUpRecord { + id: number; // 编号 + bizType: number; // 数据类型 + bizId: number; // 数据编号 + type: number; // 跟进类型 + content: string; // 跟进内容 + picUrls: string[]; // 图片 + fileUrls: string[]; // 附件 + nextTime: Date; // 下次联系时间 + businessIds: number[]; // 关联的商机编号数组 + businesses: Business[]; // 关联的商机数组 + contactIds: number[]; // 关联的联系人编号数组 + contacts: Contact[]; // 关联的联系人数组 + creator: string; + creatorName?: string; + } + + /** 关联商机信息 */ + export interface Business { + id: number; + name: string; + } + + /** 关联联系人信息 */ + export interface Contact { + id: number; + name: string; + } +} + +/** 查询跟进记录分页 */ +export function getFollowUpRecordPage(params: PageParam) { + return requestClient.get>( + '/crm/follow-up-record/page', + { params }, + ); +} + +/** 新增跟进记录 */ +export function createFollowUpRecord(data: CrmFollowUpApi.FollowUpRecord) { + return requestClient.post('/crm/follow-up-record/create', data); +} + +/** 删除跟进记录 */ +export function deleteFollowUpRecord(id: number) { + return requestClient.delete(`/crm/follow-up-record/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/crm/operateLog/index.ts b/apps/web-antd/src/api/crm/operateLog/index.ts new file mode 100644 index 000000000..9a7103ebf --- /dev/null +++ b/apps/web-antd/src/api/crm/operateLog/index.ts @@ -0,0 +1,35 @@ +import type { PageResult } from '@vben/request'; + +import type { SystemOperateLogApi } from '#/api/system/operate-log'; + +import { requestClient } from '#/api/request'; + +export namespace CrmOperateLogApi { + /** 操作日志信息 */ + export interface OperateLog { + id: number; + bizType: number; + bizId: number; + type: number; + content: string; + creator: string; + creatorName?: string; + createTime: Date; + } + + /** 操作日志查询请求 */ + export interface OperateLogQueryReqVO { + bizType: number; + bizId: number; + } +} + +/** 获得操作日志 */ +export function getOperateLogPage( + params: CrmOperateLogApi.OperateLogQueryReqVO, +) { + return requestClient.get>( + '/crm/operate-log/page', + { params }, + ); +} diff --git a/apps/web-antd/src/api/crm/permission/index.ts b/apps/web-antd/src/api/crm/permission/index.ts new file mode 100644 index 000000000..296e442f0 --- /dev/null +++ b/apps/web-antd/src/api/crm/permission/index.ts @@ -0,0 +1,85 @@ +import { requestClient } from '#/api/request'; + +export namespace CrmPermissionApi { + /** 数据权限信息 */ + export interface Permission { + id?: number; // 数据权限编号 + ids?: number[]; + userId?: number; // 用户编号 + bizType: number; // Crm 类型 + bizId: number; // Crm 类型数据编号 + level: number; // 权限级别 + toBizTypes?: number[]; // 同时添加至 + deptName?: string; // 部门名称 + nickname?: string; // 用户昵称 + postNames?: string[]; // 岗位名称数组 + createTime?: Date; + } + + /** 数据权限转移请求 */ + export interface BusinessTransferReqVO { + id: number; // 模块编号 + newOwnerUserId: number; // 新负责人的用户编号 + oldOwnerPermissionLevel?: number; // 老负责人加入团队后的权限级别 + toBizTypes?: number[]; // 转移客户时,需要额外有【联系人】【商机】【合同】的 checkbox 选择 + } + + /** 权限列表请求 */ + export interface PermissionListReqVO { + bizId: number; // 模块数据编号 + bizType: number; // 模块类型 + } +} + +/** + * CRM 业务类型枚举 + */ +export enum BizTypeEnum { + CRM_BUSINESS = 4, // 商机 + CRM_CLUE = 1, // 线索 + CRM_CONTACT = 3, // 联系人 + CRM_CONTRACT = 5, // 合同 + CRM_CUSTOMER = 2, // 客户 + CRM_PRODUCT = 6, // 产品 + CRM_RECEIVABLE = 7, // 回款 + CRM_RECEIVABLE_PLAN = 8, // 回款计划 +} + +/** + * CRM 数据权限级别枚举 + */ +export enum PermissionLevelEnum { + OWNER = 1, // 负责人 + READ = 2, // 只读 + WRITE = 3, // 读写 +} + +/** 获得数据权限列表(查询团队成员列表) */ +export function getPermissionList( + params: CrmPermissionApi.PermissionListReqVO, +) { + return requestClient.get( + '/crm/permission/list', + { params }, + ); +} + +/** 创建数据权限(新增团队成员) */ +export function createPermission(data: CrmPermissionApi.Permission) { + return requestClient.post('/crm/permission/create', data); +} + +/** 编辑数据权限(修改团队成员权限级别) */ +export function updatePermission(data: CrmPermissionApi.Permission) { + return requestClient.put('/crm/permission/update', data); +} + +/** 删除数据权限(删除团队成员) */ +export function deletePermissionBatch(ids: number[]) { + return requestClient.delete(`/crm/permission/delete?ids=${ids.join(',')}`); +} + +/** 删除自己的数据权限(退出团队) */ +export function deleteSelfPermission(id: number) { + return requestClient.delete(`/crm/permission/delete-self?id=${id}`); +} diff --git a/apps/web-antd/src/api/crm/product/index.ts b/apps/web-antd/src/api/crm/product/index.ts new file mode 100644 index 000000000..c2e3fe244 --- /dev/null +++ b/apps/web-antd/src/api/crm/product/index.ts @@ -0,0 +1,57 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmProductApi { + /** 产品信息 */ + export interface Product { + id: number; + name: string; + no: string; + unit: number; + price: number; + status: number; + categoryId: number; + categoryName?: string; + description: string; + ownerUserId: number; + } +} + +/** 查询产品列表 */ +export function getProductPage(params: PageParam) { + return requestClient.get>( + '/crm/product/page', + { params }, + ); +} + +/** 获得产品精简列表 */ +export function getProductSimpleList() { + return requestClient.get('/crm/product/simple-list'); +} + +/** 查询产品详情 */ +export function getProduct(id: number) { + return requestClient.get(`/crm/product/get?id=${id}`); +} + +/** 新增产品 */ +export function createProduct(data: CrmProductApi.Product) { + return requestClient.post('/crm/product/create', data); +} + +/** 修改产品 */ +export function updateProduct(data: CrmProductApi.Product) { + return requestClient.put('/crm/product/update', data); +} + +/** 删除产品 */ +export function deleteProduct(id: number) { + return requestClient.delete(`/crm/product/delete?id=${id}`); +} + +/** 导出产品 */ +export function exportProduct(params: any) { + return requestClient.download('/crm/product/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/crm/receivable/index.ts b/apps/web-antd/src/api/crm/receivable/index.ts new file mode 100644 index 000000000..6823170da --- /dev/null +++ b/apps/web-antd/src/api/crm/receivable/index.ts @@ -0,0 +1,90 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmReceivableApi { + /** 回款信息 */ + export interface Receivable { + id: number; + no: string; + planId?: number; + period?: number; + customerId?: number; + customerName?: string; + contractId?: number; + contract?: Contract; + auditStatus: number; + processInstanceId: number; + returnTime: Date; + returnType: number; + price: number; + ownerUserId: number; + ownerUserName?: string; + remark: string; + creator: string; // 创建人 + creatorName?: string; // 创建人名称 + createTime: Date; // 创建时间 + updateTime: Date; // 更新时间 + } + + /** 合同信息 */ + export interface Contract { + id?: number; + name?: string; + no: string; + totalPrice: number; + } +} + +/** 查询回款列表 */ +export function getReceivablePage(params: PageParam) { + return requestClient.get>( + '/crm/receivable/page', + { params }, + ); +} + +/** 查询回款列表,基于指定客户 */ +export function getReceivablePageByCustomer(params: PageParam) { + return requestClient.get>( + '/crm/receivable/page-by-customer', + { params }, + ); +} + +/** 查询回款详情 */ +export function getReceivable(id: number) { + return requestClient.get( + `/crm/receivable/get?id=${id}`, + ); +} + +/** 新增回款 */ +export function createReceivable(data: CrmReceivableApi.Receivable) { + return requestClient.post('/crm/receivable/create', data); +} + +/** 修改回款 */ +export function updateReceivable(data: CrmReceivableApi.Receivable) { + return requestClient.put('/crm/receivable/update', data); +} + +/** 删除回款 */ +export function deleteReceivable(id: number) { + return requestClient.delete(`/crm/receivable/delete?id=${id}`); +} + +/** 导出回款 */ +export function exportReceivable(params: any) { + return requestClient.download('/crm/receivable/export-excel', { params }); +} + +/** 提交审核 */ +export function submitReceivable(id: number) { + return requestClient.put(`/crm/receivable/submit?id=${id}`); +} + +/** 获得待审核回款数量 */ +export function getAuditReceivableCount() { + return requestClient.get('/crm/receivable/audit-count'); +} diff --git a/apps/web-antd/src/api/crm/receivable/plan/index.ts b/apps/web-antd/src/api/crm/receivable/plan/index.ts new file mode 100644 index 000000000..c4279d8ca --- /dev/null +++ b/apps/web-antd/src/api/crm/receivable/plan/index.ts @@ -0,0 +1,98 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmReceivablePlanApi { + /** 回款计划信息 */ + export interface Plan { + id: number; + period: number; + receivableId: number; + price: number; + returnTime: Date; + remindDays: number; + returnType: number; + remindTime: Date; + customerId: number; + customerName?: string; + contractId?: number; + contractNo?: string; + ownerUserId: number; + ownerUserName?: string; + remark: string; + creator: string; + creatorName?: string; + createTime: Date; + updateTime: Date; + receivable?: { + price: number; + returnTime: Date; + }; + } +} + +/** 查询回款计划列表 */ +export function getReceivablePlanPage(params: PageParam) { + return requestClient.get>( + '/crm/receivable-plan/page', + { params }, + ); +} + +/** 查询回款计划列表(按客户) */ +export function getReceivablePlanPageByCustomer(params: PageParam) { + return requestClient.get>( + '/crm/receivable-plan/page-by-customer', + { params }, + ); +} + +/** 查询回款计划详情 */ +export function getReceivablePlan(id: number) { + return requestClient.get( + '/crm/receivable-plan/get', + { params: { id } }, + ); +} + +/** 查询回款计划下拉数据 */ +export function getReceivablePlanSimpleList( + customerId: number, + contractId: number, +) { + return requestClient.get( + '/crm/receivable-plan/simple-list', + { + params: { customerId, contractId }, + }, + ); +} + +/** 新增回款计划 */ +export function createReceivablePlan(data: CrmReceivablePlanApi.Plan) { + return requestClient.post('/crm/receivable-plan/create', data); +} + +/** 修改回款计划 */ +export function updateReceivablePlan(data: CrmReceivablePlanApi.Plan) { + return requestClient.put('/crm/receivable-plan/update', data); +} + +/** 删除回款计划 */ +export function deleteReceivablePlan(id: number) { + return requestClient.delete('/crm/receivable-plan/delete', { + params: { id }, + }); +} + +/** 导出回款计划 Excel */ +export function exportReceivablePlan(params: any) { + return requestClient.download('/crm/receivable-plan/export-excel', { + params, + }); +} + +/** 获得待回款提醒数量 */ +export function getReceivablePlanRemindCount() { + return requestClient.get('/crm/receivable-plan/remind-count'); +} diff --git a/apps/web-antd/src/api/crm/statistics/customer.ts b/apps/web-antd/src/api/crm/statistics/customer.ts new file mode 100644 index 000000000..94cfb65ac --- /dev/null +++ b/apps/web-antd/src/api/crm/statistics/customer.ts @@ -0,0 +1,277 @@ +import { requestClient } from '#/api/request'; + +export namespace CrmStatisticsCustomerApi { + /** 客户统计请求 */ + export interface CustomerSummaryReqVO { + times: string[]; + interval: number; + deptId: number; + userId: number; + userIds: number[]; + } + + /** 客户总量分析(按日期)响应 */ + export interface CustomerSummaryByDateRespVO { + time: string; + customerCreateCount: number; + customerDealCount: number; + } + + /** 客户总量分析(按用户)响应 */ + export interface CustomerSummaryByUserRespVO { + ownerUserName: string; + customerCreateCount: number; + customerDealCount: number; + contractPrice: number; + receivablePrice: number; + } + + /** 客户跟进次数分析(按日期)响应 */ + export interface FollowUpSummaryByDateRespVO { + time: string; + followUpRecordCount: number; + followUpCustomerCount: number; + } + + /** 客户跟进次数分析(按用户)响应 */ + export interface FollowUpSummaryByUserRespVO { + ownerUserName: string; + followupRecordCount: number; + followupCustomerCount: number; + } + + /** 客户跟进方式统计响应 */ + export interface FollowUpSummaryByTypeRespVO { + followUpType: string; + followUpRecordCount: number; + } + + /** 合同摘要信息响应 */ + export interface CustomerContractSummaryRespVO { + customerName: string; + contractName: string; + totalPrice: number; + receivablePrice: number; + customerType: string; + customerSource: string; + ownerUserName: string; + creatorUserName: string; + createTime: Date; + orderDate: Date; + } + + /** 客户公海分析(按日期)响应 */ + export interface PoolSummaryByDateRespVO { + time: string; + customerPutCount: number; + customerTakeCount: number; + } + + /** 客户公海分析(按用户)响应 */ + export interface PoolSummaryByUserRespVO { + ownerUserName: string; + customerPutCount: number; + customerTakeCount: number; + } + + /** 客户成交周期(按日期)响应 */ + export interface CustomerDealCycleByDateRespVO { + time: string; + customerDealCycle: number; + } + + /** 客户成交周期(按用户)响应 */ + export interface CustomerDealCycleByUserRespVO { + ownerUserName: string; + customerDealCycle: number; + customerDealCount: number; + } + + /** 客户成交周期(按地区)响应 */ + export interface CustomerDealCycleByAreaRespVO { + areaName: string; + customerDealCycle: number; + customerDealCount: number; + } + + /** 客户成交周期(按产品)响应 */ + export interface CustomerDealCycleByProductRespVO { + productName: string; + customerDealCycle: number; + customerDealCount: number; + } +} + +export function getDatas(activeTabName: any, params: any) { + switch (activeTabName) { + case 'conversionStat': { + return getContractSummary(params); + } + case 'customerSummary': { + return getCustomerSummaryByUser(params); + } + case 'dealCycleByArea': { + return getCustomerDealCycleByArea(params); + } + case 'dealCycleByProduct': { + return getCustomerDealCycleByProduct(params); + } + case 'dealCycleByUser': { + return getCustomerDealCycleByUser(params); + } + case 'followUpSummary': { + return getFollowUpSummaryByUser(params); + } + case 'followUpType': { + return getFollowUpSummaryByType(params); + } + case 'poolSummary': { + return getPoolSummaryByUser(params); + } + default: { + return []; + } + } +} + +export function getChartDatas(activeTabName: any, params: any) { + switch (activeTabName) { + case 'conversionStat': { + return getCustomerSummaryByDate(params); + } + case 'customerSummary': { + return getCustomerSummaryByDate(params); + } + case 'dealCycleByArea': { + return getCustomerDealCycleByArea(params); + } + case 'dealCycleByProduct': { + return getCustomerDealCycleByProduct(params); + } + case 'dealCycleByUser': { + return getCustomerDealCycleByUser(params); + } + case 'followUpSummary': { + return getFollowUpSummaryByDate(params); + } + case 'followUpType': { + return getFollowUpSummaryByType(params); + } + case 'poolSummary': { + return getPoolSummaryByDate(params); + } + default: { + return []; + } + } +} + +/** 客户总量分析(按日期) */ +export function getCustomerSummaryByDate( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.CustomerSummaryByDateRespVO[] + >('/crm/statistics-customer/get-customer-summary-by-date', { params }); +} + +/** 客户总量分析(按用户) */ +export function getCustomerSummaryByUser( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.CustomerSummaryByUserRespVO[] + >('/crm/statistics-customer/get-customer-summary-by-user', { params }); +} + +/** 客户跟进次数分析(按日期) */ +export function getFollowUpSummaryByDate( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.FollowUpSummaryByDateRespVO[] + >('/crm/statistics-customer/get-follow-up-summary-by-date', { params }); +} + +/** 客户跟进次数分析(按用户) */ +export function getFollowUpSummaryByUser( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.FollowUpSummaryByUserRespVO[] + >('/crm/statistics-customer/get-follow-up-summary-by-user', { params }); +} + +/** 获取客户跟进方式统计数 */ +export function getFollowUpSummaryByType( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.FollowUpSummaryByTypeRespVO[] + >('/crm/statistics-customer/get-follow-up-summary-by-type', { params }); +} + +/** 合同摘要信息(客户转化率页面) */ +export function getContractSummary( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.CustomerContractSummaryRespVO[] + >('/crm/statistics-customer/get-contract-summary', { params }); +} + +/** 获取客户公海分析(按日期) */ +export function getPoolSummaryByDate( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get( + '/crm/statistics-customer/get-pool-summary-by-date', + { params }, + ); +} + +/** 获取客户公海分析(按用户) */ +export function getPoolSummaryByUser( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get( + '/crm/statistics-customer/get-pool-summary-by-user', + { params }, + ); +} + +/** 获取客户成交周期(按日期) */ +export function getCustomerDealCycleByDate( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.CustomerDealCycleByDateRespVO[] + >('/crm/statistics-customer/get-customer-deal-cycle-by-date', { params }); +} + +/** 获取客户成交周期(按用户) */ +export function getCustomerDealCycleByUser( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.CustomerDealCycleByUserRespVO[] + >('/crm/statistics-customer/get-customer-deal-cycle-by-user', { params }); +} + +/** 获取客户成交周期(按地区) */ +export function getCustomerDealCycleByArea( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.CustomerDealCycleByAreaRespVO[] + >('/crm/statistics-customer/get-customer-deal-cycle-by-area', { params }); +} + +/** 获取客户成交周期(按产品) */ +export function getCustomerDealCycleByProduct( + params: CrmStatisticsCustomerApi.CustomerSummaryReqVO, +) { + return requestClient.get< + CrmStatisticsCustomerApi.CustomerDealCycleByProductRespVO[] + >('/crm/statistics-customer/get-customer-deal-cycle-by-product', { params }); +} diff --git a/apps/web-antd/src/api/crm/statistics/funnel.ts b/apps/web-antd/src/api/crm/statistics/funnel.ts new file mode 100644 index 000000000..eb7cf8ca8 --- /dev/null +++ b/apps/web-antd/src/api/crm/statistics/funnel.ts @@ -0,0 +1,100 @@ +import type { PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmStatisticsFunnelApi { + /** 销售漏斗统计数据响应 */ + export interface FunnelSummaryRespVO { + customerCount: number; // 客户数 + businessCount: number; // 商机数 + businessWinCount: number; // 赢单数 + } + + /** 商机分析(按日期)响应 */ + export interface BusinessSummaryByDateRespVO { + time: string; // 时间 + businessCreateCount: number; // 商机数 + totalPrice: number | string; // 商机金额 + } + + /** 商机转化率分析(按日期)响应 */ + export interface BusinessInversionRateSummaryByDateRespVO { + time: string; // 时间 + businessCount: number; // 商机数量 + businessWinCount: number; // 赢单商机数 + } +} + +export function getDatas(activeTabName: any, params: any) { + switch (activeTabName) { + case 'businessInversionRateSummary': { + return getBusinessPageByDate(params); + } + case 'businessSummary': { + return getBusinessPageByDate(params); + } + case 'funnel': { + return getBusinessSummaryByEndStatus(params); + } + default: { + return []; + } + } +} + +export function getChartDatas(activeTabName: any, params: any) { + switch (activeTabName) { + case 'businessInversionRateSummary': { + return getBusinessInversionRateSummaryByDate(params); + } + case 'businessSummary': { + return getBusinessSummaryByDate(params); + } + case 'funnel': { + return getFunnelSummary(params); + } + default: { + return []; + } + } +} + +/** 获取销售漏斗统计数据 */ +export function getFunnelSummary(params: any) { + return requestClient.get( + '/crm/statistics-funnel/get-funnel-summary', + { params }, + ); +} + +/** 获取商机结束状态统计 */ +export function getBusinessSummaryByEndStatus(params: any) { + return requestClient.get>( + '/crm/statistics-funnel/get-business-summary-by-end-status', + { params }, + ); +} + +/** 获取新增商机分析(按日期) */ +export function getBusinessSummaryByDate(params: any) { + return requestClient.get< + CrmStatisticsFunnelApi.BusinessSummaryByDateRespVO[] + >('/crm/statistics-funnel/get-business-summary-by-date', { params }); +} + +/** 获取商机转化率分析(按日期) */ +export function getBusinessInversionRateSummaryByDate(params: any) { + return requestClient.get< + CrmStatisticsFunnelApi.BusinessInversionRateSummaryByDateRespVO[] + >('/crm/statistics-funnel/get-business-inversion-rate-summary-by-date', { + params, + }); +} + +/** 获取商机列表(按日期) */ +export function getBusinessPageByDate(params: any) { + return requestClient.get>( + '/crm/statistics-funnel/get-business-page-by-date', + { params }, + ); +} diff --git a/apps/web-antd/src/api/crm/statistics/performance.ts b/apps/web-antd/src/api/crm/statistics/performance.ts new file mode 100644 index 000000000..28048a314 --- /dev/null +++ b/apps/web-antd/src/api/crm/statistics/performance.ts @@ -0,0 +1,48 @@ +import { requestClient } from '#/api/request'; + +export namespace CrmStatisticsPerformanceApi { + /** 员工业绩统计请求 */ + export interface PerformanceReqVO { + times: string[]; + deptId: number; + userId: number; + } + + /** 员工业绩统计响应 */ + export interface PerformanceRespVO { + time: string; + currentMonthCount: number; + lastMonthCount: number; + lastYearCount: number; + } +} + +/** 员工获得合同金额统计 */ +export function getContractPricePerformance( + params: CrmStatisticsPerformanceApi.PerformanceReqVO, +) { + return requestClient.get( + '/crm/statistics-performance/get-contract-price-performance', + { params }, + ); +} + +/** 员工获得回款统计 */ +export function getReceivablePricePerformance( + params: CrmStatisticsPerformanceApi.PerformanceReqVO, +) { + return requestClient.get( + '/crm/statistics-performance/get-receivable-price-performance', + { params }, + ); +} + +/** 员工获得签约合同数量统计 */ +export function getContractCountPerformance( + params: CrmStatisticsPerformanceApi.PerformanceReqVO, +) { + return requestClient.get( + '/crm/statistics-performance/get-contract-count-performance', + { params }, + ); +} diff --git a/apps/web-antd/src/api/crm/statistics/portrait.ts b/apps/web-antd/src/api/crm/statistics/portrait.ts new file mode 100644 index 000000000..6c4e20109 --- /dev/null +++ b/apps/web-antd/src/api/crm/statistics/portrait.ts @@ -0,0 +1,89 @@ +import type { PageParam } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmStatisticsPortraitApi { + /** 客户基础统计响应 */ + export interface CustomerBaseRespVO { + customerCount: number; + dealCount: number; + dealPortion: number | string; + } + + /** 客户行业统计响应 */ + export interface CustomerIndustryRespVO extends CustomerBaseRespVO { + industryId: number; + industryPortion: number | string; + } + + /** 客户来源统计响应 */ + export interface CustomerSourceRespVO extends CustomerBaseRespVO { + source: number; + sourcePortion: number | string; + } + + /** 客户级别统计响应 */ + export interface CustomerLevelRespVO extends CustomerBaseRespVO { + level: number; + levelPortion: number | string; + } + + /** 客户地区统计响应 */ + export interface CustomerAreaRespVO extends CustomerBaseRespVO { + areaId: number; + areaName: string; + areaPortion: number | string; + } +} + +export function getDatas(activeTabName: any, params: any) { + switch (activeTabName) { + case 'area': { + return getCustomerArea(params); + } + case 'industry': { + return getCustomerIndustry(params); + } + case 'level': { + return getCustomerLevel(params); + } + case 'source': { + return getCustomerSource(params); + } + default: { + return []; + } + } +} + +/** 获取客户行业统计数据 */ +export function getCustomerIndustry(params: PageParam) { + return requestClient.get( + '/crm/statistics-portrait/get-customer-industry-summary', + { params }, + ); +} + +/** 获取客户来源统计数据 */ +export function getCustomerSource(params: PageParam) { + return requestClient.get( + '/crm/statistics-portrait/get-customer-source-summary', + { params }, + ); +} + +/** 获取客户级别统计数据 */ +export function getCustomerLevel(params: PageParam) { + return requestClient.get( + '/crm/statistics-portrait/get-customer-level-summary', + { params }, + ); +} + +/** 获取客户地区统计数据 */ +export function getCustomerArea(params: PageParam) { + return requestClient.get( + '/crm/statistics-portrait/get-customer-area-summary', + { params }, + ); +} diff --git a/apps/web-antd/src/api/crm/statistics/rank.ts b/apps/web-antd/src/api/crm/statistics/rank.ts new file mode 100644 index 000000000..b1f57b11e --- /dev/null +++ b/apps/web-antd/src/api/crm/statistics/rank.ts @@ -0,0 +1,108 @@ +import type { PageParam } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace CrmStatisticsRankApi { + /** 排行统计响应 */ + export interface RankRespVO { + count: number; + nickname: string; + deptName: string; + } +} + +export function getDatas(activeTabName: any, params: any) { + switch (activeTabName) { + case 'contactCountRank': { + return getContactsCountRank(params); + } + case 'contractCountRank': { + return getContractCountRank(params); + } + case 'contractPriceRank': { + return getContractPriceRank(params); + } + case 'customerCountRank': { + return getCustomerCountRank(params); + } + case 'followCountRank': { + return getFollowCountRank(params); + } + case 'followCustomerCountRank': { + return getFollowCustomerCountRank(params); + } + case 'productSalesRank': { + return getProductSalesRank(params); + } + case 'receivablePriceRank': { + return getReceivablePriceRank(params); + } + default: { + return []; + } + } +} + +/** 获得合同排行榜 */ +export function getContractPriceRank(params: PageParam) { + return requestClient.get( + '/crm/statistics-rank/get-contract-price-rank', + { params }, + ); +} + +/** 获得回款排行榜 */ +export function getReceivablePriceRank(params: PageParam) { + return requestClient.get( + '/crm/statistics-rank/get-receivable-price-rank', + { params }, + ); +} + +/** 签约合同排行 */ +export function getContractCountRank(params: PageParam) { + return requestClient.get( + '/crm/statistics-rank/get-contract-count-rank', + { params }, + ); +} + +/** 产品销量排行 */ +export function getProductSalesRank(params: PageParam) { + return requestClient.get( + '/crm/statistics-rank/get-product-sales-rank', + { params }, + ); +} + +/** 新增客户数排行 */ +export function getCustomerCountRank(params: PageParam) { + return requestClient.get( + '/crm/statistics-rank/get-customer-count-rank', + { params }, + ); +} + +/** 新增联系人数排行 */ +export function getContactsCountRank(params: PageParam) { + return requestClient.get( + '/crm/statistics-rank/get-contacts-count-rank', + { params }, + ); +} + +/** 跟进次数排行 */ +export function getFollowCountRank(params: PageParam) { + return requestClient.get( + '/crm/statistics-rank/get-follow-count-rank', + { params }, + ); +} + +/** 跟进客户数排行 */ +export function getFollowCustomerCountRank(params: PageParam) { + return requestClient.get( + '/crm/statistics-rank/get-follow-customer-count-rank', + { params }, + ); +} diff --git a/apps/web-antd/src/api/erp/finance/account/index.ts b/apps/web-antd/src/api/erp/finance/account/index.ts new file mode 100644 index 000000000..1f630b87d --- /dev/null +++ b/apps/web-antd/src/api/erp/finance/account/index.ts @@ -0,0 +1,61 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpAccountApi { + /** 结算账户信息 */ + export interface Account { + id?: number; // 结算账户编号 + no: string; // 账户编码 + remark: string; // 备注 + status: number; // 开启状态 + sort: number; // 排序 + defaultStatus: boolean; // 是否默认 + name: string; // 账户名称 + } +} + +/** 查询结算账户分页 */ +export function getAccountPage(params: PageParam) { + return requestClient.get>( + '/erp/account/page', + { params }, + ); +} + +/** 查询结算账户精简列表 */ +export function getAccountSimpleList() { + return requestClient.get('/erp/account/simple-list'); +} + +/** 查询结算账户详情 */ +export function getAccount(id: number) { + return requestClient.get(`/erp/account/get?id=${id}`); +} + +/** 新增结算账户 */ +export function createAccount(data: ErpAccountApi.Account) { + return requestClient.post('/erp/account/create', data); +} + +/** 修改结算账户 */ +export function updateAccount(data: ErpAccountApi.Account) { + return requestClient.put('/erp/account/update', data); +} + +/** 修改结算账户默认状态 */ +export function updateAccountDefaultStatus(id: number, defaultStatus: boolean) { + return requestClient.put('/erp/account/update-default-status', null, { + params: { id, defaultStatus }, + }); +} + +/** 删除结算账户 */ +export function deleteAccount(id: number) { + return requestClient.delete(`/erp/account/delete?id=${id}`); +} + +/** 导出结算账户 Excel */ +export function exportAccount(params: any) { + return requestClient.download('/erp/account/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/finance/payment/index.ts b/apps/web-antd/src/api/erp/finance/payment/index.ts new file mode 100644 index 000000000..004713ce8 --- /dev/null +++ b/apps/web-antd/src/api/erp/finance/payment/index.ts @@ -0,0 +1,95 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpFinancePaymentApi { + /** 付款单信息 */ + export interface FinancePayment { + id?: number; // 付款单编号 + no: string; // 付款单号 + supplierId?: number; // 供应商编号 + supplierName?: string; // 供应商名称 + paymentTime?: Date; // 付款时间 + totalPrice: number; // 合计金额,单位:元 + discountPrice: number; // 优惠金额 + paymentPrice: number; // 实际付款金额 + status: number; // 状态 + remark: string; // 备注 + fileUrl?: string; // 附件 + accountId?: number; // 付款账户 + accountName?: string; // 账户名称 + financeUserId?: number; // 财务人员 + financeUserName?: string; // 财务人员姓名 + creator?: string; // 创建人 + creatorName?: string; // 创建人姓名 + items?: FinancePaymentItem[]; // 付款明细 + bizNo?: string; // 业务单号 + } + + /** 付款单项 */ + export interface FinancePaymentItem { + id?: number; + row_id?: number; // 前端使用的临时 ID + bizId: number; // 业务ID + bizType: number; // 业务类型 + bizNo: string; // 业务编号 + totalPrice: number; // 应付金额 + paidPrice: number; // 已付金额 + paymentPrice: number; // 本次付款 + remark?: string; // 备注 + } +} + +/** 查询付款单分页 */ +export function getFinancePaymentPage(params: PageParam) { + return requestClient.get>( + '/erp/finance-payment/page', + { + params, + }, + ); +} + +/** 查询付款单详情 */ +export function getFinancePayment(id: number) { + return requestClient.get( + `/erp/finance-payment/get?id=${id}`, + ); +} + +/** 新增付款单 */ +export function createFinancePayment( + data: ErpFinancePaymentApi.FinancePayment, +) { + return requestClient.post('/erp/finance-payment/create', data); +} + +/** 修改付款单 */ +export function updateFinancePayment( + data: ErpFinancePaymentApi.FinancePayment, +) { + return requestClient.put('/erp/finance-payment/update', data); +} + +/** 更新付款单的状态 */ +export function updateFinancePaymentStatus(id: number, status: number) { + return requestClient.put('/erp/finance-payment/update-status', null, { + params: { id, status }, + }); +} + +/** 删除付款单 */ +export function deleteFinancePayment(ids: number[]) { + return requestClient.delete('/erp/finance-payment/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出付款单 Excel */ +export function exportFinancePayment(params: any) { + return requestClient.download('/erp/finance-payment/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/finance/receipt/index.ts b/apps/web-antd/src/api/erp/finance/receipt/index.ts new file mode 100644 index 000000000..f871035b5 --- /dev/null +++ b/apps/web-antd/src/api/erp/finance/receipt/index.ts @@ -0,0 +1,95 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpFinanceReceiptApi { + /** 收款单项 */ + export interface FinanceReceiptItem { + id?: number; + row_id?: number; // 前端使用的临时 ID + bizId: number; // 业务ID + bizType: number; // 业务类型 + bizNo: string; // 业务编号 + totalPrice: number; // 应收金额 + receiptedPrice: number; // 已收金额 + receiptPrice: number; // 本次收款 + remark?: string; // 备注 + } + + /** 收款单信息 */ + export interface FinanceReceipt { + id?: number; // 收款单编号 + no: string; // 收款单号 + customerId: number; // 客户编号 + customerName?: string; // 客户名称 + receiptTime: Date; // 收款时间 + totalPrice: number; // 合计金额,单位:元 + discountPrice: number; // 优惠金额 + receiptPrice: number; // 实际收款金额 + status: number; // 状态 + remark: string; // 备注 + fileUrl?: string; // 附件 + accountId?: number; // 收款账户 + accountName?: string; // 账户名称 + financeUserId?: number; // 财务人员 + financeUserName?: string; // 财务人员姓名 + creator?: string; // 创建人 + creatorName?: string; // 创建人姓名 + items?: FinanceReceiptItem[]; // 收款明细 + bizNo?: string; // 业务单号 + } +} + +/** 查询收款单分页 */ +export function getFinanceReceiptPage(params: PageParam) { + return requestClient.get>( + '/erp/finance-receipt/page', + { + params, + }, + ); +} + +/** 查询收款单详情 */ +export function getFinanceReceipt(id: number) { + return requestClient.get( + `/erp/finance-receipt/get?id=${id}`, + ); +} + +/** 新增收款单 */ +export function createFinanceReceipt( + data: ErpFinanceReceiptApi.FinanceReceipt, +) { + return requestClient.post('/erp/finance-receipt/create', data); +} + +/** 修改收款单 */ +export function updateFinanceReceipt( + data: ErpFinanceReceiptApi.FinanceReceipt, +) { + return requestClient.put('/erp/finance-receipt/update', data); +} + +/** 更新收款单的状态 */ +export function updateFinanceReceiptStatus(id: number, status: number) { + return requestClient.put('/erp/finance-receipt/update-status', null, { + params: { id, status }, + }); +} + +/** 删除收款单 */ +export function deleteFinanceReceipt(ids: number[]) { + return requestClient.delete('/erp/finance-receipt/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出收款单 Excel */ +export function exportFinanceReceipt(params: any) { + return requestClient.download('/erp/finance-receipt/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/product/category/index.ts b/apps/web-antd/src/api/erp/product/category/index.ts new file mode 100644 index 000000000..3c96b5893 --- /dev/null +++ b/apps/web-antd/src/api/erp/product/category/index.ts @@ -0,0 +1,62 @@ +import { requestClient } from '#/api/request'; + +export namespace ErpProductCategoryApi { + /** 产品分类信息 */ + export interface ProductCategory { + id?: number; // 分类编号 + parentId?: number; // 父分类编号 + name: string; // 分类名称 + code?: string; // 分类编码 + sort?: number; // 分类排序 + status?: number; // 开启状态 + children?: ProductCategory[]; // 子分类 + } +} + +/** 查询产品分类列表 */ +export function getProductCategoryList(params?: any) { + return requestClient.get( + '/erp/product-category/list', + { params }, + ); +} + +/** 查询产品分类精简列表 */ +export function getProductCategorySimpleList() { + return requestClient.get( + '/erp/product-category/simple-list', + ); +} + +/** 查询产品分类详情 */ +export function getProductCategory(id: number) { + return requestClient.get( + `/erp/product-category/get?id=${id}`, + ); +} + +/** 新增产品分类 */ +export function createProductCategory( + data: ErpProductCategoryApi.ProductCategory, +) { + return requestClient.post('/erp/product-category/create', data); +} + +/** 修改产品分类 */ +export function updateProductCategory( + data: ErpProductCategoryApi.ProductCategory, +) { + return requestClient.put('/erp/product-category/update', data); +} + +/** 删除产品分类 */ +export function deleteProductCategory(id: number) { + return requestClient.delete(`/erp/product-category/delete?id=${id}`); +} + +/** 导出产品分类 Excel */ +export function exportProductCategory(params: any) { + return requestClient.download('/erp/product-category/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/product/product/index.ts b/apps/web-antd/src/api/erp/product/product/index.ts new file mode 100644 index 000000000..a9ca836f9 --- /dev/null +++ b/apps/web-antd/src/api/erp/product/product/index.ts @@ -0,0 +1,61 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpProductApi { + /** 产品信息 */ + export interface Product { + id?: number; // 产品编号 + name: string; // 产品名称 + barCode: string; // 产品条码 + categoryId: number; // 产品类型编号 + unitId: number; // 单位编号 + unitName?: string; // 单位名字 + status: number; // 产品状态 + standard: string; // 产品规格 + remark: string; // 产品备注 + expiryDay: number; // 保质期天数 + weight: number; // 重量(kg) + purchasePrice: number; // 采购价格,单位:元 + salePrice: number; // 销售价格,单位:元 + minPrice: number; // 最低价格,单位:元 + } +} + +/** 查询产品分页 */ +export function getProductPage(params: PageParam) { + return requestClient.get>( + '/erp/product/page', + { params }, + ); +} + +/** 查询产品精简列表 */ +export function getProductSimpleList() { + return requestClient.get('/erp/product/simple-list'); +} + +/** 查询产品详情 */ +export function getProduct(id: number) { + return requestClient.get(`/erp/product/get?id=${id}`); +} + +/** 新增产品 */ +export function createProduct(data: ErpProductApi.Product) { + return requestClient.post('/erp/product/create', data); +} + +/** 修改产品 */ +export function updateProduct(data: ErpProductApi.Product) { + return requestClient.put('/erp/product/update', data); +} + +/** 删除产品 */ +export function deleteProduct(id: number) { + return requestClient.delete(`/erp/product/delete?id=${id}`); +} + +/** 导出产品 Excel */ +export function exportProduct(params: any) { + return requestClient.download('/erp/product/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/product/unit/index.ts b/apps/web-antd/src/api/erp/product/unit/index.ts new file mode 100644 index 000000000..23514963b --- /dev/null +++ b/apps/web-antd/src/api/erp/product/unit/index.ts @@ -0,0 +1,54 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpProductUnitApi { + /** 产品单位信息 */ + export interface ProductUnit { + id?: number; // 单位编号 + name: string; // 单位名字 + status: number; // 单位状态 + } +} + +/** 查询产品单位分页 */ +export function getProductUnitPage(params: PageParam) { + return requestClient.get>( + '/erp/product-unit/page', + { params }, + ); +} + +/** 查询产品单位精简列表 */ +export function getProductUnitSimpleList() { + return requestClient.get( + '/erp/product-unit/simple-list', + ); +} + +/** 查询产品单位详情 */ +export function getProductUnit(id: number) { + return requestClient.get( + `/erp/product-unit/get?id=${id}`, + ); +} + +/** 新增产品单位 */ +export function createProductUnit(data: ErpProductUnitApi.ProductUnit) { + return requestClient.post('/erp/product-unit/create', data); +} + +/** 修改产品单位 */ +export function updateProductUnit(data: ErpProductUnitApi.ProductUnit) { + return requestClient.put('/erp/product-unit/update', data); +} + +/** 删除产品单位 */ +export function deleteProductUnit(id: number) { + return requestClient.delete(`/erp/product-unit/delete?id=${id}`); +} + +/** 导出产品单位 Excel */ +export function exportProductUnit(params: any) { + return requestClient.download('/erp/product-unit/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/purchase/in/index.ts b/apps/web-antd/src/api/erp/purchase/in/index.ts new file mode 100644 index 000000000..060e1bcb8 --- /dev/null +++ b/apps/web-antd/src/api/erp/purchase/in/index.ts @@ -0,0 +1,100 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpPurchaseInApi { + /** 采购入库信息 */ + export interface PurchaseIn { + id?: number; // 入库工单编号 + no?: string; // 采购入库号 + supplierId?: number; // 供应商编号 + inTime?: Date; // 入库时间 + totalCount?: number; // 合计数量 + totalPrice?: number; // 合计金额,单位:元 + status?: number; // 状态 + remark?: string; // 备注 + outCount?: number; // 采购出库数量 + returnCount?: number; // 采购退货数量 + discountPercent?: number; // 折扣百分比 + discountPrice?: number; // 折扣金额 + paymentPrice?: number; // 实际支付金额 + otherPrice?: number; // 其他费用 + totalProductPrice?: number; // 合计商品金额 + taxPrice?: number; // 合计税额 + items?: PurchaseInItem[]; // 采购入库明细 + } + + /** 采购项信息 */ + export interface PurchaseInItem { + count?: number; + id?: number; + orderItemId?: number; + productBarCode?: string; + productId?: number; + productName: string; + productPrice: number; + productUnitId?: number; + productUnitName?: string; + totalProductPrice?: number; + remark: string; + stockCount?: number; + taxPercent?: number; + taxPrice?: number; + totalPrice?: number; + warehouseId?: number; + inCount?: number; + } +} + +/** 查询采购入库分页 */ +export function getPurchaseInPage(params: PageParam) { + return requestClient.get>( + '/erp/purchase-in/page', + { + params, + }, + ); +} + +/** 查询采购入库详情 */ +export function getPurchaseIn(id: number) { + return requestClient.get( + `/erp/purchase-in/get?id=${id}`, + ); +} + +/** 新增采购入库 */ +export function createPurchaseIn(data: ErpPurchaseInApi.PurchaseIn) { + return requestClient.post('/erp/purchase-in/create', data); +} + +/** 修改采购入库 */ +export function updatePurchaseIn(data: ErpPurchaseInApi.PurchaseIn) { + return requestClient.put('/erp/purchase-in/update', data); +} + +/** 更新采购入库的状态 */ +export function updatePurchaseInStatus(id: number, status: number) { + return requestClient.put('/erp/purchase-in/update-status', null, { + params: { + id, + status, + }, + }); +} + +/** 删除采购入库 */ +export function deletePurchaseIn(ids: number[]) { + return requestClient.delete('/erp/purchase-in/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出采购入库 Excel */ +export function exportPurchaseIn(params: any) { + return requestClient.download('/erp/purchase-in/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/purchase/order/index.ts b/apps/web-antd/src/api/erp/purchase/order/index.ts new file mode 100644 index 000000000..e59dcf68e --- /dev/null +++ b/apps/web-antd/src/api/erp/purchase/order/index.ts @@ -0,0 +1,97 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpPurchaseOrderApi { + /** 采购订单信息 */ + export interface PurchaseOrder { + id?: number; // 订单工单编号 + no?: string; // 采购订单号 + supplierId?: number; // 供应商编号 + supplierName?: string; // 供应商名称 + orderTime?: Date | string; // 订单时间 + totalCount?: number; // 合计数量 + totalPrice?: number; // 合计金额,单位:元 + totalProductPrice?: number; // 产品金额,单位:元 + discountPercent?: number; // 优惠率,百分比 + discountPrice?: number; // 优惠金额,单位:元 + depositPrice?: number; // 定金金额,单位:元 + accountId?: number; // 结算账户编号 + status?: number; // 状态 + remark?: string; // 备注 + fileUrl?: string; // 附件地址 + inCount?: number; // 采购入库数量 + count?: number; // 数量 + returnCount?: number; // 采购退货数量 + inStatus?: number; // 入库状态 + returnStatus?: number; // 退货状态 + productNames?: string; // 产品名称列表 + creatorName?: string; // 创建人名称 + createTime?: Date; // 创建时间 + items?: PurchaseOrderItem[]; // 订单项列表 + } + + /** 采购订单项信息 */ + export interface PurchaseOrderItem { + id?: number; // 订单项编号 + orderId?: number; // 采购订单编号 + productId?: number; // 产品编号 + productName?: string; // 产品名称 + productBarCode?: string; // 产品条码 + productUnitId?: number; // 产品单位编号 + productUnitName?: string; // 产品单位名称 + productPrice?: number; // 产品单价,单位:元 + totalProductPrice?: number; // 产品总价,单位:元 + count?: number; // 数量 + totalPrice?: number; // 总价,单位:元 + taxPercent?: number; // 税率,百分比 + taxPrice?: number; // 税额,单位:元 + totalTaxPrice?: number; // 含税总价,单位:元 + remark?: string; // 备注 + stockCount?: number; // 库存数量(显示字段) + } +} + +/** 查询采购订单分页 */ +export function getPurchaseOrderPage(params: PageParam) { + return requestClient.get>( + '/erp/purchase-order/page', + { params }, + ); +} + +/** 查询采购订单详情 */ +export function getPurchaseOrder(id: number) { + return requestClient.get( + `/erp/purchase-order/get?id=${id}`, + ); +} + +/** 新增采购订单 */ +export function createPurchaseOrder(data: ErpPurchaseOrderApi.PurchaseOrder) { + return requestClient.post('/erp/purchase-order/create', data); +} + +/** 修改采购订单 */ +export function updatePurchaseOrder(data: ErpPurchaseOrderApi.PurchaseOrder) { + return requestClient.put('/erp/purchase-order/update', data); +} + +/** 更新采购订单的状态 */ +export function updatePurchaseOrderStatus(id: number, status: number) { + return requestClient.put('/erp/purchase-order/update-status', null, { + params: { id, status }, + }); +} + +/** 删除采购订单 */ +export function deletePurchaseOrder(ids: number[]) { + return requestClient.delete('/erp/purchase-order/delete', { + params: { ids: ids.join(',') }, + }); +} + +/** 导出采购订单 Excel */ +export function exportPurchaseOrder(params: any) { + return requestClient.download('/erp/purchase-order/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/purchase/return/index.ts b/apps/web-antd/src/api/erp/purchase/return/index.ts new file mode 100644 index 000000000..315ceeadb --- /dev/null +++ b/apps/web-antd/src/api/erp/purchase/return/index.ts @@ -0,0 +1,96 @@ +import type { PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpPurchaseReturnApi { + /** 采购退货信息 */ + export interface PurchaseReturn { + id?: number; // 采购退货编号 + no?: string; // 采购退货号 + supplierId?: number; // 供应商编号 + returnTime?: Date; // 退货时间 + totalCount?: number; // 合计数量 + totalPrice: number; // 合计金额,单位:元 + discountPercent?: number; // 折扣百分比 + discountPrice?: number; // 折扣金额 + status?: number; // 状态 + remark?: string; // 备注 + totalTaxPrice?: number; // 合计税额 + otherPrice?: number; // 其他费用 + items?: PurchaseReturnItem[]; + } + + /** 采购退货项 */ + export interface PurchaseReturnItem { + count?: number; + id?: number; + orderItemId?: number; + productBarCode?: string; + productId?: number; + productName: string; + productPrice: number; + productUnitId?: number; + productUnitName?: string; + totalProductPrice?: number; + remark: string; + stockCount?: number; + taxPercent?: number; + taxPrice?: number; + totalPrice?: number; + warehouseId?: number; + } +} + +/** 查询采购退货分页 */ +export function getPurchaseReturnPage(params: any) { + return requestClient.get>( + '/erp/purchase-return/page', + { + params, + }, + ); +} + +/** 查询采购退货详情 */ +export function getPurchaseReturn(id: number) { + return requestClient.get( + `/erp/purchase-return/get?id=${id}`, + ); +} + +/** 新增采购退货 */ +export function createPurchaseReturn( + data: ErpPurchaseReturnApi.PurchaseReturn, +) { + return requestClient.post('/erp/purchase-return/create', data); +} + +/** 修改采购退货 */ +export function updatePurchaseReturn( + data: ErpPurchaseReturnApi.PurchaseReturn, +) { + return requestClient.put('/erp/purchase-return/update', data); +} + +/** 更新采购退货的状态 */ +export function updatePurchaseReturnStatus(id: number, status: number) { + return requestClient.put('/erp/purchase-return/update-status', null, { + params: { id, status }, + }); +} + +/** 删除采购退货 */ +export function deletePurchaseReturn(ids: number[]) { + return requestClient.delete('/erp/purchase-return/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出采购退货 Excel */ +export function exportPurchaseReturn(params: any) { + return requestClient.download('/erp/purchase-return/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/purchase/supplier/index.ts b/apps/web-antd/src/api/erp/purchase/supplier/index.ts new file mode 100644 index 000000000..42c4196b0 --- /dev/null +++ b/apps/web-antd/src/api/erp/purchase/supplier/index.ts @@ -0,0 +1,66 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpSupplierApi { + /** 供应商信息 */ + export interface Supplier { + id?: number; // 供应商编号 + name: string; // 供应商名称 + contact: string; // 联系人 + mobile: string; // 手机号码 + telephone: string; // 联系电话 + email: string; // 电子邮箱 + fax: string; // 传真 + remark: string; // 备注 + status: number; // 开启状态 + sort: number; // 排序 + taxNo: string; // 纳税人识别号 + taxPercent: number; // 税率 + bankName: string; // 开户行 + bankAccount: string; // 开户账号 + bankAddress: string; // 开户地址 + } +} + +/** 查询供应商分页 */ +export function getSupplierPage(params: PageParam) { + return requestClient.get>( + '/erp/supplier/page', + { params }, + ); +} + +/** 获得供应商精简列表 */ +export function getSupplierSimpleList() { + return requestClient.get( + '/erp/supplier/simple-list', + ); +} + +/** 查询供应商详情 */ +export function getSupplier(id: number) { + return requestClient.get( + `/erp/supplier/get?id=${id}`, + ); +} + +/** 新增供应商 */ +export function createSupplier(data: ErpSupplierApi.Supplier) { + return requestClient.post('/erp/supplier/create', data); +} + +/** 修改供应商 */ +export function updateSupplier(data: ErpSupplierApi.Supplier) { + return requestClient.put('/erp/supplier/update', data); +} + +/** 删除供应商 */ +export function deleteSupplier(id: number) { + return requestClient.delete(`/erp/supplier/delete?id=${id}`); +} + +/** 导出供应商 Excel */ +export function exportSupplier(params: any) { + return requestClient.download('/erp/supplier/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/sale/customer/index.ts b/apps/web-antd/src/api/erp/sale/customer/index.ts new file mode 100644 index 000000000..1bcd1b31a --- /dev/null +++ b/apps/web-antd/src/api/erp/sale/customer/index.ts @@ -0,0 +1,66 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpCustomerApi { + /** 客户信息 */ + export interface Customer { + id?: number; // 客户编号 + name: string; // 客户名称 + contact: string; // 联系人 + mobile: string; // 手机号码 + telephone: string; // 联系电话 + email: string; // 电子邮箱 + fax: string; // 传真 + remark: string; // 备注 + status: number; // 开启状态 + sort: number; // 排序 + taxNo: string; // 纳税人识别号 + taxPercent: number; // 税率 + bankName: string; // 开户行 + bankAccount: string; // 开户账号 + bankAddress: string; // 开户地址 + } +} + +/** 查询客户分页 */ +export function getCustomerPage(params: PageParam) { + return requestClient.get>( + '/erp/customer/page', + { params }, + ); +} + +/** 查询客户精简列表 */ +export function getCustomerSimpleList() { + return requestClient.get( + '/erp/customer/simple-list', + ); +} + +/** 查询客户详情 */ +export function getCustomer(id: number) { + return requestClient.get( + `/erp/customer/get?id=${id}`, + ); +} + +/** 新增客户 */ +export function createCustomer(data: ErpCustomerApi.Customer) { + return requestClient.post('/erp/customer/create', data); +} + +/** 修改客户 */ +export function updateCustomer(data: ErpCustomerApi.Customer) { + return requestClient.put('/erp/customer/update', data); +} + +/** 删除客户 */ +export function deleteCustomer(id: number) { + return requestClient.delete(`/erp/customer/delete?id=${id}`); +} + +/** 导出客户 Excel */ +export function exportCustomer(params: any) { + return requestClient.download('/erp/customer/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/sale/order/index.ts b/apps/web-antd/src/api/erp/sale/order/index.ts new file mode 100644 index 000000000..c190a3dc1 --- /dev/null +++ b/apps/web-antd/src/api/erp/sale/order/index.ts @@ -0,0 +1,98 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpSaleOrderApi { + /** 销售订单信息 */ + export interface SaleOrder { + id?: number; // 订单工单编号 + no: string; // 销售订单号 + customerId: number; // 客户编号 + accountId?: number; // 收款账户编号 + orderTime: Date; // 订单时间 + totalCount: number; // 合计数量 + totalPrice: number; // 合计金额,单位:元 + status: number; // 状态 + remark: string; // 备注 + outCount: number; // 销售出库数量 + fileUrl?: string; // 附件地址 + inCount?: number; // 采购入库数量 + returnCount: number; // 销售退货数量 + totalProductPrice?: number; // 产品金额,单位:元 + discountPercent?: number; // 优惠率,百分比 + discountPrice?: number; // 优惠金额,单位:元 + depositPrice?: number; // 定金金额,单位:元 + items?: SaleOrderItem[]; // 销售订单产品明细列表 + } + + /** 销售订单项 */ + export interface SaleOrderItem { + id?: number; // 订单项编号 + orderId?: number; // 采购订单编号 + productId?: number; // 产品编号 + productName?: string; // 产品名称 + productBarCode?: string; // 产品条码 + productUnitId?: number; // 产品单位编号 + productUnitName?: string; // 产品单位名称 + productPrice?: number; // 产品单价,单位:元 + totalProductPrice?: number; // 产品总价,单位:元 + count?: number; // 数量 + totalPrice?: number; // 总价,单位:元 + taxPercent?: number; // 税率,百分比 + taxPrice?: number; // 税额,单位:元 + totalTaxPrice?: number; // 含税总价,单位:元 + remark?: string; // 备注 + stockCount?: number; // 库存数量(显示字段) + } +} + +/** 查询销售订单分页 */ +export function getSaleOrderPage(params: PageParam) { + return requestClient.get>( + '/erp/sale-order/page', + { params }, + ); +} + +/** 查询销售订单详情 */ +export function getSaleOrder(id: number) { + return requestClient.get( + `/erp/sale-order/get?id=${id}`, + ); +} + +/** 查询销售订单项列表 */ +export function getSaleOrderItemListByOrderId(orderId: number) { + return requestClient.get( + `/erp/sale-order/item/list-by-order-id?orderId=${orderId}`, + ); +} + +/** 新增销售订单 */ +export function createSaleOrder(data: ErpSaleOrderApi.SaleOrder) { + return requestClient.post('/erp/sale-order/create', data); +} + +/** 修改销售订单 */ +export function updateSaleOrder(data: ErpSaleOrderApi.SaleOrder) { + return requestClient.put('/erp/sale-order/update', data); +} + +/** 更新销售订单的状态 */ +export function updateSaleOrderStatus(id: number, status: number) { + return requestClient.put('/erp/sale-order/update-status', null, { + params: { id, status }, + }); +} + +/** 删除销售订单 */ +export function deleteSaleOrder(ids: number[]) { + return requestClient.delete('/erp/sale-order/delete', { + params: { ids: ids.join(',') }, + }); +} + +/** 导出销售订单 Excel */ +export function exportSaleOrder(params: any) { + return requestClient.download('/erp/sale-order/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/sale/out/index.ts b/apps/web-antd/src/api/erp/sale/out/index.ts new file mode 100644 index 000000000..a3e335af6 --- /dev/null +++ b/apps/web-antd/src/api/erp/sale/out/index.ts @@ -0,0 +1,95 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpSaleOutApi { + /** 销售出库信息 */ + export interface SaleOut { + id?: number; // 销售出库编号 + no?: string; // 销售出库号 + customerId?: number; // 客户编号 + saleUserId?: number; // 客户编号 + outTime?: Date; // 出库时间 + totalCount?: number; // 合计数量 + totalPrice?: number; // 合计金额,单位:元 + status?: number; // 状态 + remark?: string; // 备注 + discountPercent?: number; // 折扣百分比 + discountPrice?: number; // 折扣金额 + otherPrice?: number; // 其他费用 + totalProductPrice?: number; // 合计商品金额 + taxPrice?: number; // 合计税额 + totalTaxPrice?: number; // 合计税额 + fileUrl?: string; // 附件地址 + items?: SaleOutItem[]; + } + + /** 销售出库项 */ + export interface SaleOutItem { + count?: number; + id?: number; + orderItemId?: number; + productBarCode?: string; + productId?: number; + productName: string; + productPrice: number; + productUnitId?: number; + productUnitName?: string; + totalProductPrice?: number; + remark: string; + stockCount?: number; + taxPercent?: number; + taxPrice?: number; + totalPrice?: number; + warehouseId?: number; + outCount?: number; + } +} + +/** 查询销售出库分页 */ +export function getSaleOutPage(params: PageParam) { + return requestClient.get>( + '/erp/sale-out/page', + { + params, + }, + ); +} + +/** 查询销售出库详情 */ +export function getSaleOut(id: number) { + return requestClient.get(`/erp/sale-out/get?id=${id}`); +} + +/** 新增销售出库 */ +export function createSaleOut(data: ErpSaleOutApi.SaleOut) { + return requestClient.post('/erp/sale-out/create', data); +} + +/** 修改销售出库 */ +export function updateSaleOut(data: ErpSaleOutApi.SaleOut) { + return requestClient.put('/erp/sale-out/update', data); +} + +/** 更新销售出库的状态 */ +export function updateSaleOutStatus(id: number, status: number) { + return requestClient.put('/erp/sale-out/update-status', null, { + params: { id, status }, + }); +} + +/** 删除销售出库 */ +export function deleteSaleOut(ids: number[]) { + return requestClient.delete('/erp/sale-out/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出销售出库 Excel */ +export function exportSaleOut(params: any) { + return requestClient.download('/erp/sale-out/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/sale/return/index.ts b/apps/web-antd/src/api/erp/sale/return/index.ts new file mode 100644 index 000000000..a0624cba2 --- /dev/null +++ b/apps/web-antd/src/api/erp/sale/return/index.ts @@ -0,0 +1,96 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpSaleReturnApi { + /** 销售退货信息 */ + export interface SaleReturn { + id?: number; // 销售退货编号 + no?: string; // 销售退货号 + customerId?: number; // 客户编号 + returnTime?: Date; // 退货时间 + totalCount?: number; // 合计数量 + totalPrice?: number; // 合计金额,单位:元 + status?: number; // 状态 + remark?: string; // 备注 + discountPercent?: number; // 折扣百分比 + discountPrice?: number; // 折扣金额 + otherPrice?: number; // 其他费用 + totalProductPrice?: number; // 合计商品金额 + taxPrice?: number; // 合计税额 + totalTaxPrice?: number; // 合计税额 + fileUrl?: string; // 附件地址 + items?: SaleReturnItem[]; + } + + /** 销售退货项 */ + export interface SaleReturnItem { + count?: number; + id?: number; + orderItemId?: number; + productBarCode?: string; + productId?: number; + productName: string; + productPrice: number; + productUnitId?: number; + productUnitName?: string; + totalProductPrice?: number; + remark: string; + stockCount?: number; + taxPercent?: number; + taxPrice?: number; + totalPrice?: number; + warehouseId?: number; + returnCount?: number; + } +} + +/** 查询销售退货分页 */ +export function getSaleReturnPage(params: PageParam) { + return requestClient.get>( + '/erp/sale-return/page', + { + params, + }, + ); +} + +/** 查询销售退货详情 */ +export function getSaleReturn(id: number) { + return requestClient.get( + `/erp/sale-return/get?id=${id}`, + ); +} + +/** 新增销售退货 */ +export function createSaleReturn(data: ErpSaleReturnApi.SaleReturn) { + return requestClient.post('/erp/sale-return/create', data); +} + +/** 修改销售退货 */ +export function updateSaleReturn(data: ErpSaleReturnApi.SaleReturn) { + return requestClient.put('/erp/sale-return/update', data); +} + +/** 更新销售退货的状态 */ +export function updateSaleReturnStatus(id: number, status: number) { + return requestClient.put('/erp/sale-return/update-status', null, { + params: { id, status }, + }); +} + +/** 删除销售退货 */ +export function deleteSaleReturn(ids: number[]) { + return requestClient.delete('/erp/sale-return/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出销售退货 Excel */ +export function exportSaleReturn(params: any) { + return requestClient.download('/erp/sale-return/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/statistics/purchase/index.ts b/apps/web-antd/src/api/erp/statistics/purchase/index.ts new file mode 100644 index 000000000..126b6f283 --- /dev/null +++ b/apps/web-antd/src/api/erp/statistics/purchase/index.ts @@ -0,0 +1,31 @@ +import { requestClient } from '#/api/request'; + +export namespace ErpPurchaseStatisticsApi { + /** 采购全局统计 */ + export interface PurchaseSummaryRespVO { + todayPrice: number; // 今日采购金额 + yesterdayPrice: number; // 昨日采购金额 + monthPrice: number; // 本月采购金额 + yearPrice: number; // 今年采购金额 + } + + /** 采购时间段统计 */ + export interface PurchaseTimeSummaryRespVO { + time: string; // 时间 + price: number; // 采购金额 + } +} + +/** 获得采购统计 */ +export function getPurchaseSummary() { + return requestClient.get( + '/erp/purchase-statistics/summary', + ); +} + +/** 获得采购时间段统计 */ +export function getPurchaseTimeSummary() { + return requestClient.get< + ErpPurchaseStatisticsApi.PurchaseTimeSummaryRespVO[] + >('/erp/purchase-statistics/time-summary'); +} diff --git a/apps/web-antd/src/api/erp/statistics/sale/index.ts b/apps/web-antd/src/api/erp/statistics/sale/index.ts new file mode 100644 index 000000000..209c1c938 --- /dev/null +++ b/apps/web-antd/src/api/erp/statistics/sale/index.ts @@ -0,0 +1,31 @@ +import { requestClient } from '#/api/request'; + +export namespace ErpSaleStatisticsApi { + /** 销售全局统计 */ + export interface SaleSummaryRespVO { + todayPrice: number; // 今日销售金额 + yesterdayPrice: number; // 昨日销售金额 + monthPrice: number; // 本月销售金额 + yearPrice: number; // 今年销售金额 + } + + /** 销售时间段统计 */ + export interface SaleTimeSummaryRespVO { + time: string; // 时间 + price: number; // 销售金额 + } +} + +/** 获得销售统计 */ +export function getSaleSummary() { + return requestClient.get( + '/erp/sale-statistics/summary', + ); +} + +/** 获得销售时间段统计 */ +export function getSaleTimeSummary() { + return requestClient.get( + '/erp/sale-statistics/time-summary', + ); +} diff --git a/apps/web-antd/src/api/erp/stock/check/index.ts b/apps/web-antd/src/api/erp/stock/check/index.ts new file mode 100644 index 000000000..b6f9d0bea --- /dev/null +++ b/apps/web-antd/src/api/erp/stock/check/index.ts @@ -0,0 +1,87 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpStockCheckApi { + /** 库存盘点单信息 */ + export interface StockCheck { + id?: number; // 盘点编号 + no: string; // 盘点单号 + checkTime: Date; // 盘点时间 + totalCount: number; // 合计数量 + totalPrice: number; // 合计金额,单位:元 + status: number; // 状态 + remark: string; // 备注 + fileUrl?: string; // 附件 + productNames?: string; // 产品信息 + creatorName?: string; // 创建人 + items?: StockCheckItem[]; // 盘点产品清单 + } + + /** 库存盘点项 */ + export interface StockCheckItem { + id?: number; // 编号 + warehouseId?: number; // 仓库编号 + productId?: number; // 产品编号 + productName?: string; // 产品名称 + productUnitId?: number; // 产品单位编号 + productUnitName?: string; // 产品单位名称 + productBarCode?: string; // 产品条码 + count?: number; // 盈亏数量 + actualCount?: number; // 实际库存 + productPrice?: number; // 产品单价 + totalPrice?: number; // 总价 + stockCount?: number; // 账面库存 + remark?: string; // 备注 + } +} + +/** 查询库存盘点单分页 */ +export function getStockCheckPage(params: PageParam) { + return requestClient.get>( + '/erp/stock-check/page', + { + params, + }, + ); +} + +/** 查询库存盘点单详情 */ +export function getStockCheck(id: number) { + return requestClient.get( + `/erp/stock-check/get?id=${id}`, + ); +} + +/** 新增库存盘点单 */ +export function createStockCheck(data: ErpStockCheckApi.StockCheck) { + return requestClient.post('/erp/stock-check/create', data); +} + +/** 修改库存盘点单 */ +export function updateStockCheck(data: ErpStockCheckApi.StockCheck) { + return requestClient.put('/erp/stock-check/update', data); +} + +/** 更新库存盘点单的状态 */ +export function updateStockCheckStatus(id: number, status: number) { + return requestClient.put('/erp/stock-check/update-status', null, { + params: { id, status }, + }); +} + +/** 删除库存盘点 */ +export function deleteStockCheck(ids: number[]) { + return requestClient.delete('/erp/stock-check/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出库存盘点单 Excel */ +export function exportStockCheck(params: any) { + return requestClient.download('/erp/stock-check/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/stock/in/index.ts b/apps/web-antd/src/api/erp/stock/in/index.ts new file mode 100644 index 000000000..e68c05b36 --- /dev/null +++ b/apps/web-antd/src/api/erp/stock/in/index.ts @@ -0,0 +1,86 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpStockInApi { + /** 其它入库单信息 */ + export interface StockIn { + id?: number; // 入库编号 + no: string; // 入库单号 + supplierId: number; // 供应商编号 + supplierName?: string; // 供应商名称 + inTime: Date; // 入库时间 + totalCount: number; // 合计数量 + totalPrice: number; // 合计金额,单位:元 + status: number; // 状态 + remark: string; // 备注 + fileUrl?: string; // 附件 + productNames?: string; // 产品信息 + creatorName?: string; // 创建人 + items?: StockInItem[]; // 入库产品清单 + } + + /** 其它入库单产品信息 */ + export interface StockInItem { + id?: number; // 编号 + warehouseId: number; // 仓库编号 + productId: number; // 产品编号 + productName?: string; // 产品名称 + productUnitId?: number; // 产品单位编号 + productUnitName?: string; // 产品单位名称 + productBarCode?: string; // 产品条码 + count: number; // 数量 + productPrice: number; // 产品单价 + totalPrice: number; // 总价 + stockCount?: number; // 库存数量 + remark?: string; // 备注 + } +} + +/** 查询其它入库单分页 */ +export function getStockInPage(params: PageParam) { + return requestClient.get>( + '/erp/stock-in/page', + { + params, + }, + ); +} + +/** 查询其它入库单详情 */ +export function getStockIn(id: number) { + return requestClient.get(`/erp/stock-in/get?id=${id}`); +} + +/** 新增其它入库单 */ +export function createStockIn(data: ErpStockInApi.StockIn) { + return requestClient.post('/erp/stock-in/create', data); +} + +/** 修改其它入库单 */ +export function updateStockIn(data: ErpStockInApi.StockIn) { + return requestClient.put('/erp/stock-in/update', data); +} + +/** 更新其它入库单的状态 */ +export function updateStockInStatus(id: number, status: number) { + return requestClient.put('/erp/stock-in/update-status', null, { + params: { id, status }, + }); +} + +/** 删除其它入库单 */ +export function deleteStockIn(ids: number[]) { + return requestClient.delete('/erp/stock-in/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出其它入库单 Excel */ +export function exportStockIn(params: any) { + return requestClient.download('/erp/stock-in/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/stock/move/index.ts b/apps/web-antd/src/api/erp/stock/move/index.ts new file mode 100644 index 000000000..0120a9b45 --- /dev/null +++ b/apps/web-antd/src/api/erp/stock/move/index.ts @@ -0,0 +1,87 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpStockMoveApi { + /** 库存调拨单信息 */ + export interface StockMove { + id?: number; // 调拨编号 + no: string; // 调拨单号 + outTime: Date; // 调拨时间 + totalCount: number; // 合计数量 + totalPrice: number; // 合计金额,单位:元 + status: number; // 状态 + remark: string; // 备注 + fileUrl?: string; // 附件 + fromWarehouseId?: number; // 来源仓库编号 + createTime: Date; // 创建时间 + creator: string; // 创建人 + creatorName: string; // 创建人名称 + productNames: string; // 产品名称 + items?: StockMoveItem[]; // 子表信息 + } + + /** 库存调拨单子表信息 */ + export interface StockMoveItem { + count: number; // 数量 + fromWarehouseId?: number; // 来源仓库ID + id?: number; // ID + productBarCode: string; // 产品条形码 + productId?: number; // 产品ID + productName?: string; // 产品名称 + productPrice: number; // 产品单价 + productUnitName?: string; // 产品单位 + remark?: string; // 备注 + stockCount: number; // 库存数量 + toWarehouseId?: number; // 目标仓库ID + totalPrice?: number; // 总价 + } +} + +/** 查询库存调拨单分页 */ +export function getStockMovePage(params: PageParam) { + return requestClient.get>( + '/erp/stock-move/page', + { + params, + }, + ); +} + +/** 查询库存调拨单详情 */ +export function getStockMove(id: number) { + return requestClient.get( + `/erp/stock-move/get?id=${id}`, + ); +} + +/** 新增库存调拨单 */ +export function createStockMove(data: ErpStockMoveApi.StockMove) { + return requestClient.post('/erp/stock-move/create', data); +} + +/** 修改库存调拨单 */ +export function updateStockMove(data: ErpStockMoveApi.StockMove) { + return requestClient.put('/erp/stock-move/update', data); +} + +/** 更新库存调拨单的状态 */ +export function updateStockMoveStatus(id: number, status: number) { + return requestClient.put('/erp/stock-move/update-status', null, { + params: { id, status }, + }); +} + +/** 删除库存调拨单 */ +export function deleteStockMove(ids: number[]) { + return requestClient.delete('/erp/stock-move/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出库存调拨单 Excel */ +export function exportStockMove(params: any) { + return requestClient.download('/erp/stock-move/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/stock/out/index.ts b/apps/web-antd/src/api/erp/stock/out/index.ts new file mode 100644 index 000000000..1ecb89233 --- /dev/null +++ b/apps/web-antd/src/api/erp/stock/out/index.ts @@ -0,0 +1,85 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpStockOutApi { + /** 其它出库单信息 */ + export interface StockOut { + id?: number; // 出库编号 + no: string; // 出库单号 + customerId: number; // 客户编号 + outTime: Date; // 出库时间 + totalCount: number; // 合计数量 + totalPrice: number; // 合计金额,单位:元 + status: number; // 状态 + remark: string; // 备注 + fileUrl?: string; // 附件 + items?: StockOutItem[]; // 出库产品清单 + } + + /** 其它出库单产品信息 */ + export interface StockOutItem { + id?: number; // 编号 + warehouseId?: number; // 仓库编号 + productId?: number; // 产品编号 + productName?: string; // 产品名称 + productUnitId?: number; // 产品单位编号 + productUnitName?: string; // 产品单位名称 + productBarCode?: string; // 产品条码 + count: number; // 数量 + productPrice: number; // 产品单价 + totalPrice: number; // 总价 + stockCount?: number; // 库存数量 + remark?: string; // 备注 + } +} + +/** 查询其它出库单分页 */ +export function getStockOutPage(params: PageParam) { + return requestClient.get>( + '/erp/stock-out/page', + { + params, + }, + ); +} + +/** 查询其它出库单详情 */ +export function getStockOut(id: number) { + return requestClient.get( + `/erp/stock-out/get?id=${id}`, + ); +} + +/** 新增其它出库单 */ +export function createStockOut(data: ErpStockOutApi.StockOut) { + return requestClient.post('/erp/stock-out/create', data); +} + +/** 修改其它出库单 */ +export function updateStockOut(data: ErpStockOutApi.StockOut) { + return requestClient.put('/erp/stock-out/update', data); +} + +/** 更新其它出库单的状态 */ +export function updateStockOutStatus(id: number, status: number) { + return requestClient.put('/erp/stock-out/update-status', null, { + params: { id, status }, + }); +} + +/** 删除其它出库单 */ +export function deleteStockOut(ids: number[]) { + return requestClient.delete('/erp/stock-out/delete', { + params: { + ids: ids.join(','), + }, + }); +} + +/** 导出其它出库单 Excel */ +export function exportStockOut(params: any) { + return requestClient.download('/erp/stock-out/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/stock/record/index.ts b/apps/web-antd/src/api/erp/stock/record/index.ts new file mode 100644 index 000000000..30bc9dae0 --- /dev/null +++ b/apps/web-antd/src/api/erp/stock/record/index.ts @@ -0,0 +1,31 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpStockRecordApi { + /** 产品库存明细 */ + export interface StockRecord { + id?: number; // 编号 + productId: number; // 产品编号 + warehouseId: number; // 仓库编号 + count: number; // 出入库数量 + totalCount: number; // 总库存量 + bizType: number; // 业务类型 + bizId: number; // 业务编号 + bizItemId: number; // 业务项编号 + bizNo: string; // 业务单号 + } +} + +/** 查询产品库存明细分页 */ +export function getStockRecordPage(params: PageParam) { + return requestClient.get>( + '/erp/stock-record/page', + { params }, + ); +} + +/** 导出产品库存明细 Excel */ +export function exportStockRecord(params: any) { + return requestClient.download('/erp/stock-record/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/erp/stock/stock/index.ts b/apps/web-antd/src/api/erp/stock/stock/index.ts new file mode 100644 index 000000000..7335c6d34 --- /dev/null +++ b/apps/web-antd/src/api/erp/stock/stock/index.ts @@ -0,0 +1,51 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpStockApi { + /** 产品库存信息 */ + export interface Stock { + id?: number; // 编号 + productId: number; // 产品编号 + warehouseId: number; // 仓库编号 + count: number; // 库存数量 + } + + /** 产品库存查询参数 */ + export interface StockQueryReqVO { + productId: number; + warehouseId: number; + } +} + +/** 查询产品库存分页 */ +export function getStockPage(params: PageParam) { + return requestClient.get>('/erp/stock/page', { + params, + }); +} + +/** 获得产品库存数量 */ +export function getStockCount(productId: number, warehouseId?: number) { + const params: any = { productId }; + if (warehouseId !== undefined) { + params.warehouseId = warehouseId; + } + return requestClient.get('/erp/stock/get-count', { + params, + }); +} + +/** 导出产品库存 Excel */ +export function exportStock(params: any) { + return requestClient.download('/erp/stock/export-excel', { + params, + }); +} + +/** 获取库存数量 */ +export function getWarehouseStockCount(params: ErpStockApi.StockQueryReqVO) { + return requestClient.get('/erp/stock/get-count', { + params, + }); +} diff --git a/apps/web-antd/src/api/erp/stock/warehouse/index.ts b/apps/web-antd/src/api/erp/stock/warehouse/index.ts new file mode 100644 index 000000000..e5d6e1a49 --- /dev/null +++ b/apps/web-antd/src/api/erp/stock/warehouse/index.ts @@ -0,0 +1,71 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ErpWarehouseApi { + /** 仓库信息 */ + export interface Warehouse { + id?: number; // 仓库编号 + name: string; // 仓库名称 + address: string; // 仓库地址 + sort: number; // 排序 + remark: string; // 备注 + principal: string; // 负责人 + warehousePrice: number; // 仓储费,单位:元 + truckagePrice: number; // 搬运费,单位:元 + status: number; // 开启状态 + defaultStatus: boolean; // 是否默认 + } +} + +/** 查询仓库分页 */ +export function getWarehousePage(params: PageParam) { + return requestClient.get>( + '/erp/warehouse/page', + { params }, + ); +} + +/** 查询仓库精简列表 */ +export function getWarehouseSimpleList() { + return requestClient.get( + '/erp/warehouse/simple-list', + ); +} + +/** 查询仓库详情 */ +export function getWarehouse(id: number) { + return requestClient.get( + `/erp/warehouse/get?id=${id}`, + ); +} + +/** 新增仓库 */ +export function createWarehouse(data: ErpWarehouseApi.Warehouse) { + return requestClient.post('/erp/warehouse/create', data); +} + +/** 修改仓库 */ +export function updateWarehouse(data: ErpWarehouseApi.Warehouse) { + return requestClient.put('/erp/warehouse/update', data); +} + +/** 修改仓库默认状态 */ +export function updateWarehouseDefaultStatus( + id: number, + defaultStatus: boolean, +) { + return requestClient.put('/erp/warehouse/update-default-status', null, { + params: { id, defaultStatus }, + }); +} + +/** 删除仓库 */ +export function deleteWarehouse(id: number) { + return requestClient.delete(`/erp/warehouse/delete?id=${id}`); +} + +/** 导出仓库 Excel */ +export function exportWarehouse(params: any) { + return requestClient.download('/erp/warehouse/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/infra/codegen/index.ts b/apps/web-antd/src/api/infra/codegen/index.ts new file mode 100644 index 000000000..992d41d2d --- /dev/null +++ b/apps/web-antd/src/api/infra/codegen/index.ts @@ -0,0 +1,168 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace InfraCodegenApi { + /** 代码生成表定义 */ + export interface CodegenTable { + id: number; + tableId: number; + isParentMenuIdValid: boolean; + dataSourceConfigId: number; + scene: number; + tableName: string; + tableComment: string; + remark: string; + moduleName: string; + businessName: string; + className: string; + classComment: string; + author: string; + createTime: Date; + updateTime: Date; + templateType: number; + parentMenuId: number; + } + + /** 代码生成字段定义 */ + export interface CodegenColumn { + id: number; + tableId: number; + columnName: string; + dataType: string; + columnComment: string; + nullable: number; + primaryKey: number; + ordinalPosition: number; + javaType: string; + javaField: string; + dictType: string; + example: string; + createOperation: number; + updateOperation: number; + listOperation: number; + listOperationCondition: string; + listOperationResult: number; + htmlType: string; + } + + /** 数据库表定义 */ + export interface DatabaseTable { + name: string; + comment: string; + } + + /** 代码生成详情 */ + export interface CodegenDetail { + table: CodegenTable; + columns: CodegenColumn[]; + } + + /** 代码预览 */ + export interface CodegenPreview { + filePath: string; + code: string; + } + + /** 更新代码生成请求 */ + export interface CodegenUpdateReqVO { + table: any | CodegenTable; + columns: CodegenColumn[]; + } + + /** 创建代码生成请求 */ + export interface CodegenCreateListReqVO { + dataSourceConfigId?: number; + tableNames: string[]; + } +} + +/** 查询列表代码生成表定义 */ +export function getCodegenTableList(dataSourceConfigId: number) { + return requestClient.get( + '/infra/codegen/table/list?', + { + params: { dataSourceConfigId }, + }, + ); +} + +/** 查询列表代码生成表定义 */ +export function getCodegenTablePage(params: PageParam) { + return requestClient.get>( + '/infra/codegen/table/page', + { params }, + ); +} + +/** 查询详情代码生成表定义 */ +export function getCodegenTable(tableId: number) { + return requestClient.get( + '/infra/codegen/detail', + { + params: { tableId }, + }, + ); +} + +/** 修改代码生成表定义 */ +export function updateCodegenTable(data: InfraCodegenApi.CodegenUpdateReqVO) { + return requestClient.put('/infra/codegen/update', data); +} + +/** 基于数据库的表结构,同步数据库的表和字段定义 */ +export function syncCodegenFromDB(tableId: number) { + return requestClient.put( + '/infra/codegen/sync-from-db', + {}, + { + params: { tableId }, + }, + ); +} + +/** 预览生成代码 */ +export function previewCodegen(tableId: number) { + return requestClient.get( + '/infra/codegen/preview', + { + params: { tableId }, + }, + ); +} + +/** 下载生成代码 */ +export function downloadCodegen(tableId: number) { + return requestClient.download('/infra/codegen/download', { + params: { tableId }, + }); +} + +/** 获得表定义 */ +export function getSchemaTableList(params: any) { + return requestClient.get( + '/infra/codegen/db/table/list', + { params }, + ); +} + +/** 基于数据库的表结构,创建代码生成器的表定义 */ +export function createCodegenList( + data: InfraCodegenApi.CodegenCreateListReqVO, +) { + return requestClient.post('/infra/codegen/create-list', data); +} + +/** 删除代码生成表定义 */ +export function deleteCodegenTable(tableId: number) { + return requestClient.delete('/infra/codegen/delete', { + params: { tableId }, + }); +} + +/** 批量删除代码生成表定义 */ +export function deleteCodegenTableList(tableIds: number[]) { + return requestClient.delete( + `/infra/codegen/delete-list?tableIds=${tableIds.join(',')}`, + ); +} diff --git a/apps/web-antd/src/api/infra/data-source-config/index.ts b/apps/web-antd/src/api/infra/data-source-config/index.ts new file mode 100644 index 000000000..81a3b0e3d --- /dev/null +++ b/apps/web-antd/src/api/infra/data-source-config/index.ts @@ -0,0 +1,53 @@ +import { requestClient } from '#/api/request'; + +export namespace InfraDataSourceConfigApi { + /** 数据源配置信息 */ + export interface DataSourceConfig { + id?: number; + name: string; + url: string; + username: string; + password: string; + createTime?: Date; + } +} + +/** 查询数据源配置列表 */ +export function getDataSourceConfigList() { + return requestClient.get( + '/infra/data-source-config/list', + ); +} + +/** 查询数据源配置详情 */ +export function getDataSourceConfig(id: number) { + return requestClient.get( + `/infra/data-source-config/get?id=${id}`, + ); +} + +/** 新增数据源配置 */ +export function createDataSourceConfig( + data: InfraDataSourceConfigApi.DataSourceConfig, +) { + return requestClient.post('/infra/data-source-config/create', data); +} + +/** 修改数据源配置 */ +export function updateDataSourceConfig( + data: InfraDataSourceConfigApi.DataSourceConfig, +) { + return requestClient.put('/infra/data-source-config/update', data); +} + +/** 删除数据源配置 */ +export function deleteDataSourceConfig(id: number) { + return requestClient.delete(`/infra/data-source-config/delete?id=${id}`); +} + +/** 批量删除数据源配置 */ +export function deleteDataSourceConfigList(ids: number[]) { + return requestClient.delete( + `/infra/data-source-config/delete-list?ids=${ids.join(',')}`, + ); +} diff --git a/apps/web-antd/src/api/infra/demo/demo01/index.ts b/apps/web-antd/src/api/infra/demo/demo01/index.ts new file mode 100644 index 000000000..e180a76c4 --- /dev/null +++ b/apps/web-antd/src/api/infra/demo/demo01/index.ts @@ -0,0 +1,61 @@ +import type { Dayjs } from 'dayjs'; + +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace Demo01ContactApi { + /** 示例联系人信息 */ + export interface Demo01Contact { + id: number; // 编号 + name?: string; // 名字 + sex?: number; // 性别 + birthday?: Dayjs | string; // 出生年 + description?: string; // 简介 + avatar: string; // 头像 + } +} + +/** 查询示例联系人分页 */ +export function getDemo01ContactPage(params: PageParam) { + return requestClient.get>( + '/infra/demo01-contact/page', + { params }, + ); +} + +/** 查询示例联系人详情 */ +export function getDemo01Contact(id: number) { + return requestClient.get( + `/infra/demo01-contact/get?id=${id}`, + ); +} + +/** 新增示例联系人 */ +export function createDemo01Contact(data: Demo01ContactApi.Demo01Contact) { + return requestClient.post('/infra/demo01-contact/create', data); +} + +/** 修改示例联系人 */ +export function updateDemo01Contact(data: Demo01ContactApi.Demo01Contact) { + return requestClient.put('/infra/demo01-contact/update', data); +} + +/** 删除示例联系人 */ +export function deleteDemo01Contact(id: number) { + return requestClient.delete(`/infra/demo01-contact/delete?id=${id}`); +} + +/** 批量删除示例联系人 */ +export function deleteDemo01ContactList(ids: number[]) { + return requestClient.delete( + `/infra/demo01-contact/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出示例联系人 */ +export function exportDemo01Contact(params: any) { + return requestClient.download('/infra/demo01-contact/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/infra/demo/demo02/index.ts b/apps/web-antd/src/api/infra/demo/demo02/index.ts new file mode 100644 index 000000000..b4edd746e --- /dev/null +++ b/apps/web-antd/src/api/infra/demo/demo02/index.ts @@ -0,0 +1,48 @@ +import { requestClient } from '#/api/request'; + +export namespace Demo02CategoryApi { + /** 示例分类信息 */ + export interface Demo02Category { + id: number; // 编号 + name?: string; // 名字 + parentId?: number; // 父级编号 + children?: Demo02Category[]; + } +} + +/** 查询示例分类列表 */ +export function getDemo02CategoryList(params: any) { + return requestClient.get( + '/infra/demo02-category/list', + { params }, + ); +} + +/** 查询示例分类详情 */ +export function getDemo02Category(id: number) { + return requestClient.get( + `/infra/demo02-category/get?id=${id}`, + ); +} + +/** 新增示例分类 */ +export function createDemo02Category(data: Demo02CategoryApi.Demo02Category) { + return requestClient.post('/infra/demo02-category/create', data); +} + +/** 修改示例分类 */ +export function updateDemo02Category(data: Demo02CategoryApi.Demo02Category) { + return requestClient.put('/infra/demo02-category/update', data); +} + +/** 删除示例分类 */ +export function deleteDemo02Category(id: number) { + return requestClient.delete(`/infra/demo02-category/delete?id=${id}`); +} + +/** 导出示例分类 */ +export function exportDemo02Category(params: any) { + return requestClient.download('/infra/demo02-category/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/infra/demo/demo03/erp/index.ts b/apps/web-antd/src/api/infra/demo/demo03/erp/index.ts new file mode 100644 index 000000000..6dc7e352a --- /dev/null +++ b/apps/web-antd/src/api/infra/demo/demo03/erp/index.ts @@ -0,0 +1,168 @@ +import type { Dayjs } from 'dayjs'; + +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace Demo03StudentApi { + /** 学生课程信息 */ + export interface Demo03Course { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + score?: number; // 分数 + } + + /** 学生班级信息 */ + export interface Demo03Grade { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + teacher?: string; // 班主任 + } + + /** 学生信息 */ + export interface Demo03Student { + id: number; // 编号 + name?: string; // 名字 + sex?: number; // 性别 + birthday?: Dayjs | string; // 出生日期 + description?: string; // 简介 + } +} + +/** 查询学生分页 */ +export function getDemo03StudentPage(params: PageParam) { + return requestClient.get>( + '/infra/demo03-student-erp/page', + { params }, + ); +} + +/** 查询学生详情 */ +export function getDemo03Student(id: number) { + return requestClient.get( + `/infra/demo03-student-erp/get?id=${id}`, + ); +} + +/** 新增学生 */ +export function createDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.post('/infra/demo03-student-erp/create', data); +} + +/** 修改学生 */ +export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.put('/infra/demo03-student-erp/update', data); +} + +/** 删除学生 */ +export function deleteDemo03Student(id: number) { + return requestClient.delete(`/infra/demo03-student-erp/delete?id=${id}`); +} + +/** 批量删除学生 */ +export function deleteDemo03StudentList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出学生 */ +export function exportDemo03Student(params: any) { + return requestClient.download('/infra/demo03-student-erp/export-excel', { + params, + }); +} + +// ==================== 子表(学生课程) ==================== + +/** 获得学生课程分页 */ +export function getDemo03CoursePage(params: PageParam) { + return requestClient.get>( + `/infra/demo03-student-erp/demo03-course/page`, + { params }, + ); +} +/** 新增学生课程 */ +export function createDemo03Course(data: Demo03StudentApi.Demo03Course) { + return requestClient.post( + `/infra/demo03-student-erp/demo03-course/create`, + data, + ); +} + +/** 修改学生课程 */ +export function updateDemo03Course(data: Demo03StudentApi.Demo03Course) { + return requestClient.put( + `/infra/demo03-student-erp/demo03-course/update`, + data, + ); +} + +/** 删除学生课程 */ +export function deleteDemo03Course(id: number) { + return requestClient.delete( + `/infra/demo03-student-erp/demo03-course/delete?id=${id}`, + ); +} + +/** 批量删除学生课程 */ +export function deleteDemo03CourseList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 获得学生课程 */ +export function getDemo03Course(id: number) { + return requestClient.get( + `/infra/demo03-student-erp/demo03-course/get?id=${id}`, + ); +} + +// ==================== 子表(学生班级) ==================== + +/** 获得学生班级分页 */ +export function getDemo03GradePage(params: PageParam) { + return requestClient.get>( + `/infra/demo03-student-erp/demo03-grade/page`, + { params }, + ); +} +/** 新增学生班级 */ +export function createDemo03Grade(data: Demo03StudentApi.Demo03Grade) { + return requestClient.post( + `/infra/demo03-student-erp/demo03-grade/create`, + data, + ); +} + +/** 修改学生班级 */ +export function updateDemo03Grade(data: Demo03StudentApi.Demo03Grade) { + return requestClient.put( + `/infra/demo03-student-erp/demo03-grade/update`, + data, + ); +} + +/** 删除学生班级 */ +export function deleteDemo03Grade(id: number) { + return requestClient.delete( + `/infra/demo03-student-erp/demo03-grade/delete?id=${id}`, + ); +} + +/** 批量删除学生班级 */ +export function deleteDemo03GradeList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 获得学生班级 */ +export function getDemo03Grade(id: number) { + return requestClient.get( + `/infra/demo03-student-erp/demo03-grade/get?id=${id}`, + ); +} diff --git a/apps/web-antd/src/api/infra/demo/demo03/inner/index.ts b/apps/web-antd/src/api/infra/demo/demo03/inner/index.ts new file mode 100644 index 000000000..02bfbcc66 --- /dev/null +++ b/apps/web-antd/src/api/infra/demo/demo03/inner/index.ts @@ -0,0 +1,96 @@ +import type { Dayjs } from 'dayjs'; + +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace Demo03StudentApi { + /** 学生课程信息 */ + export interface Demo03Course { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + score?: number; // 分数 + } + + /** 学生班级信息 */ + export interface Demo03Grade { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + teacher?: string; // 班主任 + } + + /** 学生信息 */ + export interface Demo03Student { + id: number; // 编号 + name?: string; // 名字 + sex?: number; // 性别 + birthday?: Dayjs | string; // 出生日期 + description?: string; // 简介 + demo03courses?: Demo03Course[]; + demo03grade?: Demo03Grade; + } +} + +/** 查询学生分页 */ +export function getDemo03StudentPage(params: PageParam) { + return requestClient.get>( + '/infra/demo03-student-inner/page', + { params }, + ); +} + +/** 查询学生详情 */ +export function getDemo03Student(id: number) { + return requestClient.get( + `/infra/demo03-student-inner/get?id=${id}`, + ); +} + +/** 新增学生 */ +export function createDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.post('/infra/demo03-student-inner/create', data); +} + +/** 修改学生 */ +export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.put('/infra/demo03-student-inner/update', data); +} + +/** 删除学生 */ +export function deleteDemo03Student(id: number) { + return requestClient.delete(`/infra/demo03-student-inner/delete?id=${id}`); +} + +/** 批量删除学生 */ +export function deleteDemo03StudentList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出学生 */ +export function exportDemo03Student(params: any) { + return requestClient.download('/infra/demo03-student-inner/export-excel', { + params, + }); +} + +// ==================== 子表(学生课程) ==================== + +/** 获得学生课程列表 */ +export function getDemo03CourseListByStudentId(studentId: number) { + return requestClient.get( + `/infra/demo03-student-inner/demo03-course/list-by-student-id?studentId=${studentId}`, + ); +} + +// ==================== 子表(学生班级) ==================== + +/** 获得学生班级 */ +export function getDemo03GradeByStudentId(studentId: number) { + return requestClient.get( + `/infra/demo03-student-inner/demo03-grade/get-by-student-id?studentId=${studentId}`, + ); +} diff --git a/apps/web-antd/src/api/infra/demo/demo03/normal/index.ts b/apps/web-antd/src/api/infra/demo/demo03/normal/index.ts new file mode 100644 index 000000000..2a085a153 --- /dev/null +++ b/apps/web-antd/src/api/infra/demo/demo03/normal/index.ts @@ -0,0 +1,96 @@ +import type { Dayjs } from 'dayjs'; + +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace Demo03StudentApi { + /** 学生课程信息 */ + export interface Demo03Course { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + score?: number; // 分数 + } + + /** 学生班级信息 */ + export interface Demo03Grade { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + teacher?: string; // 班主任 + } + + /** 学生信息 */ + export interface Demo03Student { + id: number; // 编号 + name?: string; // 名字 + sex?: number; // 性别 + birthday?: Dayjs | string; // 出生日期 + description?: string; // 简介 + demo03courses?: Demo03Course[]; + demo03grade?: Demo03Grade; + } +} + +/** 查询学生分页 */ +export function getDemo03StudentPage(params: PageParam) { + return requestClient.get>( + '/infra/demo03-student-normal/page', + { params }, + ); +} + +/** 查询学生详情 */ +export function getDemo03Student(id: number) { + return requestClient.get( + `/infra/demo03-student-normal/get?id=${id}`, + ); +} + +/** 新增学生 */ +export function createDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.post('/infra/demo03-student-normal/create', data); +} + +/** 修改学生 */ +export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.put('/infra/demo03-student-normal/update', data); +} + +/** 删除学生 */ +export function deleteDemo03Student(id: number) { + return requestClient.delete(`/infra/demo03-student-normal/delete?id=${id}`); +} + +/** 批量删除学生 */ +export function deleteDemo03StudentList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出学生 */ +export function exportDemo03Student(params: any) { + return requestClient.download('/infra/demo03-student-normal/export-excel', { + params, + }); +} + +// ==================== 子表(学生课程) ==================== + +/** 获得学生课程列表 */ +export function getDemo03CourseListByStudentId(studentId: number) { + return requestClient.get( + `/infra/demo03-student-normal/demo03-course/list-by-student-id?studentId=${studentId}`, + ); +} + +// ==================== 子表(学生班级) ==================== + +/** 获得学生班级 */ +export function getDemo03GradeByStudentId(studentId: number) { + return requestClient.get( + `/infra/demo03-student-normal/demo03-grade/get-by-student-id?studentId=${studentId}`, + ); +} diff --git a/apps/web-antd/src/api/infra/file-config/index.ts b/apps/web-antd/src/api/infra/file-config/index.ts new file mode 100644 index 000000000..1a2fb7707 --- /dev/null +++ b/apps/web-antd/src/api/infra/file-config/index.ts @@ -0,0 +1,83 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace InfraFileConfigApi { + /** 文件客户端配置 */ + export interface FileClientConfig { + basePath: string; + host?: string; + port?: number; + username?: string; + password?: string; + mode?: string; + endpoint?: string; + bucket?: string; + accessKey?: string; + accessSecret?: string; + pathStyle?: boolean; + enablePublicAccess?: boolean; + domain: string; + } + + /** 文件配置信息 */ + export interface FileConfig { + id?: number; + name: string; + storage?: number; + master: boolean; + visible: boolean; + config: FileClientConfig; + remark: string; + createTime?: Date; + } +} + +/** 查询文件配置列表 */ +export function getFileConfigPage(params: PageParam) { + return requestClient.get>( + '/infra/file-config/page', + { + params, + }, + ); +} + +/** 查询文件配置详情 */ +export function getFileConfig(id: number) { + return requestClient.get( + `/infra/file-config/get?id=${id}`, + ); +} + +/** 更新文件配置为主配置 */ +export function updateFileConfigMaster(id: number) { + return requestClient.put(`/infra/file-config/update-master?id=${id}`); +} + +/** 新增文件配置 */ +export function createFileConfig(data: InfraFileConfigApi.FileConfig) { + return requestClient.post('/infra/file-config/create', data); +} + +/** 修改文件配置 */ +export function updateFileConfig(data: InfraFileConfigApi.FileConfig) { + return requestClient.put('/infra/file-config/update', data); +} + +/** 删除文件配置 */ +export function deleteFileConfig(id: number) { + return requestClient.delete(`/infra/file-config/delete?id=${id}`); +} + +/** 批量删除文件配置 */ +export function deleteFileConfigList(ids: number[]) { + return requestClient.delete( + `/infra/file-config/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 测试文件配置 */ +export function testFileConfig(id: number) { + return requestClient.get(`/infra/file-config/test?id=${id}`); +} diff --git a/apps/web-antd/src/api/infra/file/index.ts b/apps/web-antd/src/api/infra/file/index.ts new file mode 100644 index 000000000..edf06933b --- /dev/null +++ b/apps/web-antd/src/api/infra/file/index.ts @@ -0,0 +1,78 @@ +import type { AxiosRequestConfig, PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +/** Axios 上传进度事件 */ +export type AxiosProgressEvent = AxiosRequestConfig['onUploadProgress']; + +export namespace InfraFileApi { + /** 文件信息 */ + export interface File { + id?: number; + configId?: number; + path: string; + name?: string; + url?: string; + size?: number; + type?: string; + createTime?: Date; + } + + /** 文件预签名地址 */ + export interface FilePresignedUrlRespVO { + configId: number; // 文件配置编号 + uploadUrl: string; // 文件上传 URL + url: string; // 文件 URL + path: string; // 文件路径 + } + + /** 上传文件 */ + export interface FileUploadReqVO { + file: globalThis.File; + directory?: string; + } +} + +/** 查询文件列表 */ +export function getFilePage(params: PageParam) { + return requestClient.get>('/infra/file/page', { + params, + }); +} + +/** 删除文件 */ +export function deleteFile(id: number) { + return requestClient.delete(`/infra/file/delete?id=${id}`); +} + +/** 批量删除文件 */ +export function deleteFileList(ids: number[]) { + return requestClient.delete(`/infra/file/delete-list?ids=${ids.join(',')}`); +} + +/** 获取文件预签名地址 */ +export function getFilePresignedUrl(name: string, directory?: string) { + return requestClient.get( + '/infra/file/presigned-url', + { + params: { name, directory }, + }, + ); +} + +/** 创建文件 */ +export function createFile(data: InfraFileApi.File) { + return requestClient.post('/infra/file/create', data); +} + +/** 上传文件 */ +export function uploadFile( + data: InfraFileApi.FileUploadReqVO, + onUploadProgress?: AxiosProgressEvent, +) { + // 特殊:由于 upload 内部封装,即使 directory 为 undefined,也会传递给后端 + if (!data.directory) { + delete data.directory; + } + return requestClient.upload('/infra/file/upload', data, { onUploadProgress }); +} diff --git a/apps/web-antd/src/api/infra/job/index.ts b/apps/web-antd/src/api/infra/job/index.ts new file mode 100644 index 000000000..04848dd8b --- /dev/null +++ b/apps/web-antd/src/api/infra/job/index.ts @@ -0,0 +1,77 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace InfraJobApi { + /** 任务信息 */ + export interface Job { + id?: number; + name: string; + status: number; + handlerName: string; + handlerParam: string; + cronExpression: string; + retryCount: number; + retryInterval: number; + monitorTimeout: number; + createTime?: Date; + nextTimes?: Date[]; + } +} + +/** 查询任务列表 */ +export function getJobPage(params: PageParam) { + return requestClient.get>('/infra/job/page', { + params, + }); +} + +/** 查询任务详情 */ +export function getJob(id: number) { + return requestClient.get(`/infra/job/get?id=${id}`); +} + +/** 新增任务 */ +export function createJob(data: InfraJobApi.Job) { + return requestClient.post('/infra/job/create', data); +} + +/** 修改定时任务调度 */ +export function updateJob(data: InfraJobApi.Job) { + return requestClient.put('/infra/job/update', data); +} + +/** 删除定时任务调度 */ +export function deleteJob(id: number) { + return requestClient.delete(`/infra/job/delete?id=${id}`); +} + +/** 批量删除定时任务调度 */ +export function deleteJobList(ids: number[]) { + return requestClient.delete(`/infra/job/delete-list?ids=${ids.join(',')}`); +} + +/** 导出定时任务调度 */ +export function exportJob(params: any) { + return requestClient.download('/infra/job/export-excel', { params }); +} + +/** 任务状态修改 */ +export function updateJobStatus(id: number, status: number) { + return requestClient.put('/infra/job/update-status', undefined, { + params: { + id, + status, + }, + }); +} + +/** 定时任务立即执行一次 */ +export function runJob(id: number) { + return requestClient.put(`/infra/job/trigger?id=${id}`); +} + +/** 获得定时任务的下 n 次执行时间 */ +export function getJobNextTimes(id: number) { + return requestClient.get(`/infra/job/get_next_times?id=${id}`); +} diff --git a/apps/web-antd/src/api/iot/alert/config/index.ts b/apps/web-antd/src/api/iot/alert/config/index.ts new file mode 100644 index 000000000..26e114c0c --- /dev/null +++ b/apps/web-antd/src/api/iot/alert/config/index.ts @@ -0,0 +1,94 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AlertConfigApi { + /** IoT 告警配置 VO */ + export interface AlertConfig { + id?: number; + name: string; + description?: string; + level?: number; + status?: number; + sceneRuleIds?: number[]; + receiveUserIds?: number[]; + receiveUserNames?: string; + receiveTypes?: number[]; + createTime?: Date; + updateTime?: Date; + } +} + +/** IoT 告警配置 */ +export interface AlertConfig { + id?: number; + name?: string; + description?: string; + level?: number; + status?: number; + sceneRuleIds?: number[]; + receiveUserIds?: number[]; + receiveUserNames?: string; + receiveTypes?: number[]; + createTime?: Date; + updateTime?: Date; +} + +/** 查询告警配置分页 */ +export function getAlertConfigPage(params: PageParam) { + return requestClient.get>( + '/iot/alert-config/page', + { params }, + ); +} + +/** 查询告警配置详情 */ +export function getAlertConfig(id: number) { + return requestClient.get( + `/iot/alert-config/get?id=${id}`, + ); +} + +/** 查询所有告警配置列表 */ +export function getAlertConfigList() { + return requestClient.get( + '/iot/alert-config/list', + ); +} + +/** 新增告警配置 */ +export function createAlertConfig(data: AlertConfig) { + return requestClient.post('/iot/alert-config/create', data); +} + +/** 修改告警配置 */ +export function updateAlertConfig(data: AlertConfig) { + return requestClient.put('/iot/alert-config/update', data); +} + +/** 删除告警配置 */ +export function deleteAlertConfig(id: number) { + return requestClient.delete(`/iot/alert-config/delete?id=${id}`); +} + +/** 批量删除告警配置 */ +export function deleteAlertConfigList(ids: number[]) { + return requestClient.delete('/iot/alert-config/delete-list', { + params: { ids: ids.join(',') }, + }); +} + +/** 启用/禁用告警配置 */ +export function toggleAlertConfig(id: number, enabled: boolean) { + return requestClient.put(`/iot/alert-config/toggle`, { + id, + enabled, + }); +} + +/** 获取告警配置简单列表 */ +export function getSimpleAlertConfigList() { + return requestClient.get( + '/iot/alert-config/simple-list', + ); +} diff --git a/apps/web-antd/src/api/iot/alert/record/index.ts b/apps/web-antd/src/api/iot/alert/record/index.ts new file mode 100644 index 000000000..6d2afcfcf --- /dev/null +++ b/apps/web-antd/src/api/iot/alert/record/index.ts @@ -0,0 +1,82 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace AlertRecordApi { + /** IoT 告警记录 VO */ + export interface AlertRecord { + id?: number; + configId?: number; + configName?: string; + configLevel?: number; + deviceId?: number; + deviceName?: string; + productId?: number; + productName?: string; + deviceMessage?: string; + processStatus?: boolean; + processRemark?: string; + processTime?: Date; + createTime?: Date; + } +} + +/** IoT 告警记录 */ +export interface AlertRecord { + id?: number; + configId?: number; + configName?: string; + configLevel?: number; + deviceId?: number; + deviceName?: string; + productId?: number; + productName?: string; + deviceMessage?: string; + processStatus?: boolean; + processRemark?: string; + processTime?: Date; + createTime?: Date; +} + +/** 查询告警记录分页 */ +export function getAlertRecordPage(params: PageParam) { + return requestClient.get>( + '/iot/alert-record/page', + { params }, + ); +} + +/** 查询告警记录详情 */ +export function getAlertRecord(id: number) { + return requestClient.get( + `/iot/alert-record/get?id=${id}`, + ); +} + +/** 处理告警记录 */ +export function processAlertRecord(id: number, remark?: string) { + return requestClient.put('/iot/alert-record/process', { + id, + remark, + }); +} + +/** 批量处理告警记录 */ +export function batchProcessAlertRecord(ids: number[], remark?: string) { + return requestClient.put('/iot/alert-record/batch-process', { + ids, + remark, + }); +} + +/** 删除告警记录 */ +export function deleteAlertRecord(id: number) { + return requestClient.delete(`/iot/alert-record/delete?id=${id}`); +} + +/** 批量删除告警记录 */ +export function deleteAlertRecordList(ids: number[]) { + return requestClient.delete('/iot/alert-record/delete-list', { + params: { ids: ids.join(',') }, + }); +} diff --git a/apps/web-antd/src/api/iot/device/device/index.ts b/apps/web-antd/src/api/iot/device/device/index.ts new file mode 100644 index 000000000..7bd59886f --- /dev/null +++ b/apps/web-antd/src/api/iot/device/device/index.ts @@ -0,0 +1,198 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace IotDeviceApi { + // TODO @haohao:需要跟后端对齐,必要的 ReqVO、RespVO + /** 设备 */ + export interface Device { + id?: number; // 设备 ID,主键,自增 + deviceName: string; // 设备名称 + productId: number; // 产品编号 + productKey?: string; // 产品标识 + deviceType?: number; // 设备类型 + nickname?: string; // 设备备注名称 + gatewayId?: number; // 网关设备 ID + state?: number; // 设备状态 + status?: number; // 设备状态(兼容字段) + onlineTime?: Date; // 最后上线时间 + offlineTime?: Date; // 最后离线时间 + activeTime?: Date; // 设备激活时间 + createTime?: Date; // 创建时间 + ip?: string; // 设备的 IP 地址 + firmwareVersion?: string; // 设备的固件版本 + deviceSecret?: string; // 设备密钥,用于设备认证,需安全存储 + mqttClientId?: string; // MQTT 客户端 ID + mqttUsername?: string; // MQTT 用户名 + mqttPassword?: string; // MQTT 密码 + authType?: string; // 认证类型 + locationType?: number; // 定位类型 + latitude?: number; // 设备位置的纬度 + longitude?: number; // 设备位置的经度 + areaId?: number; // 地区编码 + address?: string; // 设备详细地址 + serialNumber?: string; // 设备序列号 + config?: string; // 设备配置 + groupIds?: number[]; // 添加分组 ID + picUrl?: string; // 设备图片 + location?: string; // 位置信息(格式:经度,纬度) + } + + /** IoT 设备属性详细 VO */ + export interface DevicePropertyDetail { + identifier: string; // 属性标识符 + value: string; // 最新值 + updateTime: Date; // 更新时间 + name: string; // 属性名称 + dataType: string; // 数据类型 + dataSpecs: any; // 数据定义 + dataSpecsList: any[]; // 数据定义列表 + } + + /** 设备属性 VO */ + export interface DeviceProperty { + identifier: string; // 属性标识符 + value: string; // 最新值 + updateTime: Date; // 更新时间 + } + + /** 设备认证参数 VO */ + export interface DeviceAuthInfo { + clientId: string; // 客户端 ID + username: string; // 用户名 + password: string; // 密码 + } + + /** 设备发送消息 Request VO */ + export interface DeviceMessageSendReq { + deviceId: number; // 设备编号 + method: string; // 请求方法 + params?: any; // 请求参数 + } + + /** 设备分组更新请求 */ + export interface DeviceGroupUpdateReq { + ids: number[]; // 设备 ID 列表 + groupIds: number[]; // 分组 ID 列表 + } +} + +/** IoT 设备状态枚举 */ +// TODO @haohao:packages/constants/src/biz-iot-enum.ts 枚举; +export enum DeviceStateEnum { + INACTIVE = 0, // 未激活 + OFFLINE = 2, // 离线 + ONLINE = 1, // 在线 +} + +/** 查询设备分页 */ +export function getDevicePage(params: PageParam) { + return requestClient.get>( + '/iot/device/page', + { params }, + ); +} + +/** 查询设备详情 */ +export function getDevice(id: number) { + return requestClient.get(`/iot/device/get?id=${id}`); +} + +/** 新增设备 */ +export function createDevice(data: IotDeviceApi.Device) { + return requestClient.post('/iot/device/create', data); +} + +/** 修改设备 */ +export function updateDevice(data: IotDeviceApi.Device) { + return requestClient.put('/iot/device/update', data); +} + +/** 修改设备分组 */ +export function updateDeviceGroup(data: IotDeviceApi.DeviceGroupUpdateReq) { + return requestClient.put('/iot/device/update-group', data); +} + +/** 删除单个设备 */ +export function deleteDevice(id: number) { + return requestClient.delete(`/iot/device/delete?id=${id}`); +} + +/** 删除多个设备 */ +export function deleteDeviceList(ids: number[]) { + return requestClient.delete('/iot/device/delete-list', { + params: { ids: ids.join(',') }, + }); +} + +/** 导出设备 */ +export function exportDeviceExcel(params: any) { + return requestClient.download('/iot/device/export-excel', { params }); +} + +/** 获取设备数量 */ +export function getDeviceCount(productId: number) { + return requestClient.get(`/iot/device/count?productId=${productId}`); +} + +/** 获取设备的精简信息列表 */ +export function getSimpleDeviceList(deviceType?: number, productId?: number) { + return requestClient.get('/iot/device/simple-list', { + params: { deviceType, productId }, + }); +} + +/** 根据产品编号,获取设备的精简信息列表 */ +export function getDeviceListByProductId(productId: number) { + return requestClient.get('/iot/device/simple-list', { + params: { productId }, + }); +} + +/** 获取导入模板 */ +export function importDeviceTemplate() { + return requestClient.download('/iot/device/get-import-template'); +} + +/** 获取设备属性最新数据 */ +export function getLatestDeviceProperties(params: any) { + return requestClient.get( + '/iot/device/property/get-latest', + { params }, + ); +} + +/** 获取设备属性历史数据 */ +export function getHistoryDevicePropertyList(params: any) { + return requestClient.get>( + '/iot/device/property/history-list', + { params }, + ); +} + +/** 获取设备认证信息 */ +export function getDeviceAuthInfo(id: number) { + return requestClient.get( + '/iot/device/get-auth-info', + { params: { id } }, + ); +} + +/** 查询设备消息分页 */ +export function getDeviceMessagePage(params: PageParam) { + return requestClient.get>('/iot/device/message/page', { + params, + }); +} + +/** 查询设备消息配对分页 */ +export function getDeviceMessagePairPage(params: PageParam) { + return requestClient.get>('/iot/device/message/pair-page', { + params, + }); +} + +/** 发送设备消息 */ +export function sendDeviceMessage(params: IotDeviceApi.DeviceMessageSendReq) { + return requestClient.post('/iot/device/message/send', params); +} diff --git a/apps/web-antd/src/api/iot/device/group/index.ts b/apps/web-antd/src/api/iot/device/group/index.ts new file mode 100644 index 000000000..2c11411ae --- /dev/null +++ b/apps/web-antd/src/api/iot/device/group/index.ts @@ -0,0 +1,51 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace IotDeviceGroupApi { + /** 设备分组 */ + export interface DeviceGroup { + id?: number; // 分组 ID + name: string; // 分组名字 + status?: number; // 分组状态 + description?: string; // 分组描述 + deviceCount?: number; // 设备数量 + } +} + +/** 查询设备分组分页 */ +export function getDeviceGroupPage(params: PageParam) { + return requestClient.get>( + '/iot/device-group/page', + { params }, + ); +} + +/** 查询设备分组详情 */ +export function getDeviceGroup(id: number) { + return requestClient.get( + `/iot/device-group/get?id=${id}`, + ); +} + +/** 新增设备分组 */ +export function createDeviceGroup(data: IotDeviceGroupApi.DeviceGroup) { + return requestClient.post('/iot/device-group/create', data); +} + +/** 修改设备分组 */ +export function updateDeviceGroup(data: IotDeviceGroupApi.DeviceGroup) { + return requestClient.put('/iot/device-group/update', data); +} + +/** 删除设备分组 */ +export function deleteDeviceGroup(id: number) { + return requestClient.delete(`/iot/device-group/delete?id=${id}`); +} + +/** 获取设备分组的精简信息列表 */ +export function getSimpleDeviceGroupList() { + return requestClient.get( + '/iot/device-group/simple-list', + ); +} diff --git a/apps/web-antd/src/api/iot/ota/firmware/index.ts b/apps/web-antd/src/api/iot/ota/firmware/index.ts new file mode 100644 index 000000000..1b8ae5b69 --- /dev/null +++ b/apps/web-antd/src/api/iot/ota/firmware/index.ts @@ -0,0 +1,90 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace IoTOtaFirmwareApi { + /** IoT OTA 固件 VO */ + export interface Firmware { + id?: number; + name: string; + version: string; + productId: number; + productName?: string; + description?: string; + fileUrl?: string; + fileMd5?: string; + fileSize?: number; + status?: number; + createTime?: Date; + updateTime?: Date; + } +} + +/** IoT OTA 固件 */ +export interface IoTOtaFirmware { + id?: number; + name?: string; + version?: string; + productId?: number; + productName?: string; + description?: string; + fileUrl?: string; + fileMd5?: string; + fileSize?: number; + status?: number; + createTime?: Date; + updateTime?: Date; +} + +/** 查询 OTA 固件分页 */ +export function getOtaFirmwarePage(params: PageParam) { + return requestClient.get>( + '/iot/ota/firmware/page', + { params }, + ); +} + +/** 查询 OTA 固件详情 */ +export function getOtaFirmware(id: number) { + return requestClient.get( + `/iot/ota/firmware/get?id=${id}`, + ); +} + +/** 新增 OTA 固件 */ +export function createOtaFirmware(data: IoTOtaFirmware) { + return requestClient.post('/iot/ota/firmware/create', data); +} + +/** 修改 OTA 固件 */ +export function updateOtaFirmware(data: IoTOtaFirmware) { + return requestClient.put('/iot/ota/firmware/update', data); +} + +/** 删除 OTA 固件 */ +export function deleteOtaFirmware(id: number) { + return requestClient.delete(`/iot/ota/firmware/delete?id=${id}`); +} + +/** 批量删除 OTA 固件 */ +export function deleteOtaFirmwareList(ids: number[]) { + return requestClient.delete('/iot/ota/firmware/delete-list', { + params: { ids: ids.join(',') }, + }); +} + +/** 更新 OTA 固件状态 */ +export function updateOtaFirmwareStatus(id: number, status: number) { + return requestClient.put(`/iot/ota/firmware/update-status`, { + id, + status, + }); +} + +/** 根据产品 ID 查询固件列表 */ +export function getOtaFirmwareListByProductId(productId: number) { + return requestClient.get( + '/iot/ota/firmware/list-by-product-id', + { params: { productId } }, + ); +} diff --git a/apps/web-antd/src/api/iot/ota/task/index.ts b/apps/web-antd/src/api/iot/ota/task/index.ts new file mode 100644 index 000000000..25ef2429f --- /dev/null +++ b/apps/web-antd/src/api/iot/ota/task/index.ts @@ -0,0 +1,98 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace IoTOtaTaskApi { + /** IoT OTA 升级任务 VO */ + export interface Task { + id?: number; + name: string; + description?: string; + firmwareId: number; + firmwareName?: string; + productId?: number; + productName?: string; + deviceScope?: number; + deviceIds?: number[]; + status?: number; + successCount?: number; + failureCount?: number; + pendingCount?: number; + createTime?: Date; + updateTime?: Date; + } +} + +/** IoT OTA 升级任务 */ +export interface OtaTask { + id?: number; + name?: string; + description?: string; + firmwareId?: number; + firmwareName?: string; + productId?: number; + productName?: string; + deviceScope?: number; + deviceIds?: number[]; + status?: number; + successCount?: number; + failureCount?: number; + pendingCount?: number; + createTime?: Date; + updateTime?: Date; +} + +/** 查询 OTA 升级任务分页 */ +export function getOtaTaskPage(params: PageParam) { + return requestClient.get>( + '/iot/ota/task/page', + { params }, + ); +} + +/** 查询 OTA 升级任务详情 */ +export function getOtaTask(id: number) { + return requestClient.get(`/iot/ota/task/get?id=${id}`); +} + +/** 新增 OTA 升级任务 */ +export function createOtaTask(data: OtaTask) { + return requestClient.post('/iot/ota/task/create', data); +} + +/** 修改 OTA 升级任务 */ +export function updateOtaTask(data: OtaTask) { + return requestClient.put('/iot/ota/task/update', data); +} + +/** 删除 OTA 升级任务 */ +export function deleteOtaTask(id: number) { + return requestClient.delete(`/iot/ota/task/delete?id=${id}`); +} + +/** 批量删除 OTA 升级任务 */ +export function deleteOtaTaskList(ids: number[]) { + return requestClient.delete('/iot/ota/task/delete-list', { + params: { ids: ids.join(',') }, + }); +} + +/** 取消 OTA 升级任务 */ +export function cancelOtaTask(id: number) { + return requestClient.put(`/iot/ota/task/cancel?id=${id}`); +} + +/** 启动 OTA 升级任务 */ +export function startOtaTask(id: number) { + return requestClient.put(`/iot/ota/task/start?id=${id}`); +} + +/** 暂停 OTA 升级任务 */ +export function pauseOtaTask(id: number) { + return requestClient.put(`/iot/ota/task/pause?id=${id}`); +} + +/** 恢复 OTA 升级任务 */ +export function resumeOtaTask(id: number) { + return requestClient.put(`/iot/ota/task/resume?id=${id}`); +} diff --git a/apps/web-antd/src/api/iot/ota/task/record/index.ts b/apps/web-antd/src/api/iot/ota/task/record/index.ts new file mode 100644 index 000000000..2d66a422f --- /dev/null +++ b/apps/web-antd/src/api/iot/ota/task/record/index.ts @@ -0,0 +1,102 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace IoTOtaTaskRecordApi { + /** IoT OTA 升级任务记录 VO */ + export interface TaskRecord { + id?: number; + taskId: number; + taskName?: string; + deviceId: number; + deviceName?: string; + firmwareId?: number; + firmwareName?: string; + firmwareVersion?: string; + status?: number; + progress?: number; + errorMessage?: string; + startTime?: Date; + endTime?: Date; + createTime?: Date; + } +} + +// TODO @AI:这里应该拿到 IoTOtaTaskRecordApi 里 +/** IoT OTA 升级任务记录 */ +export interface OtaTaskRecord { + id?: number; + taskId?: number; + taskName?: string; + deviceId?: number; + deviceName?: string; + firmwareId?: number; + firmwareName?: string; + firmwareVersion?: string; + status?: number; + progress?: number; + errorMessage?: string; + startTime?: Date; + endTime?: Date; + createTime?: Date; +} + +/** 查询 OTA 升级任务记录分页 */ +export function getOtaTaskRecordPage(params: PageParam) { + return requestClient.get>( + '/iot/ota/task/record/page', + { params }, + ); +} + +/** 查询 OTA 升级任务记录详情 */ +export function getOtaTaskRecord(id: number) { + return requestClient.get( + `/iot/ota/task/record/get?id=${id}`, + ); +} + +/** 根据任务 ID 查询记录列表 */ +export function getOtaTaskRecordListByTaskId(taskId: number) { + return requestClient.get( + '/iot/ota/task/record/list-by-task-id', + { params: { taskId } }, + ); +} + +/** 根据设备 ID 查询记录列表 */ +export function getOtaTaskRecordListByDeviceId(deviceId: number) { + return requestClient.get( + '/iot/ota/task/record/list-by-device-id', + { params: { deviceId } }, + ); +} + +/** 根据固件 ID 查询记录列表 */ +export function getOtaTaskRecordListByFirmwareId(firmwareId: number) { + return requestClient.get( + '/iot/ota/task/record/list-by-firmware-id', + { params: { firmwareId } }, + ); +} + +/** 重试升级任务记录 */ +export function retryOtaTaskRecord(id: number) { + return requestClient.put(`/iot/ota/task/record/retry?id=${id}`); +} + +/** 取消升级任务记录 */ +export function cancelOtaTaskRecord(id: number) { + return requestClient.put(`/iot/ota/task/record/cancel?id=${id}`); +} + +/** 获取升级任务记录状态统计 */ +export function getOtaTaskRecordStatusStatistics( + firmwareId?: number, + taskId?: number, +) { + return requestClient.get>( + '/iot/ota/task/record/get-status-statistics', + { params: { firmwareId, taskId } }, + ); +} diff --git a/apps/web-antd/src/api/iot/product/category/index.ts b/apps/web-antd/src/api/iot/product/category/index.ts new file mode 100644 index 000000000..24d02ed3b --- /dev/null +++ b/apps/web-antd/src/api/iot/product/category/index.ts @@ -0,0 +1,57 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace IotProductCategoryApi { + /** 产品分类 */ + export interface ProductCategory { + id?: number; // 分类 ID + name: string; // 分类名称 + parentId?: number; // 父级分类 ID + sort?: number; // 分类排序 + status?: number; // 分类状态 + description?: string; // 分类描述 + createTime?: string; // 创建时间 + } +} + +/** 查询产品分类分页 */ +export function getProductCategoryPage(params: PageParam) { + return requestClient.get>( + '/iot/product-category/page', + { params }, + ); +} + +/** 查询产品分类详情 */ +export function getProductCategory(id: number) { + return requestClient.get( + `/iot/product-category/get?id=${id}`, + ); +} + +/** 新增产品分类 */ +export function createProductCategory( + data: IotProductCategoryApi.ProductCategory, +) { + return requestClient.post('/iot/product-category/create', data); +} + +/** 修改产品分类 */ +export function updateProductCategory( + data: IotProductCategoryApi.ProductCategory, +) { + return requestClient.put('/iot/product-category/update', data); +} + +/** 刪除产品分类 */ +export function deleteProductCategory(id: number) { + return requestClient.delete(`/iot/product-category/delete?id=${id}`); +} + +/** 获取产品分类精简列表 */ +export function getSimpleProductCategoryList() { + return requestClient.get( + '/iot/product-category/simple-list', + ); +} diff --git a/apps/web-antd/src/api/iot/product/product/index.ts b/apps/web-antd/src/api/iot/product/product/index.ts new file mode 100644 index 000000000..28b2f7b2e --- /dev/null +++ b/apps/web-antd/src/api/iot/product/product/index.ts @@ -0,0 +1,101 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace IotProductApi { + /** 产品 */ + export interface Product { + id?: number; // 产品编号 + name: string; // 产品名称 + productKey?: string; // 产品标识 + protocolId?: number; // 协议编号 + protocolType?: number; // 接入协议类型 + categoryId?: number; // 产品所属品类标识符 + categoryName?: string; // 产品所属品类名称 + icon?: string; // 产品图标 + picUrl?: string; // 产品图片 + description?: string; // 产品描述 + status?: number; // 产品状态 + deviceType?: number; // 设备类型 + locationType?: number; // 定位类型 + netType?: number; // 联网方式 + codecType?: string; // 数据格式(编解码器类型) + dataFormat?: number; // 数据格式 + validateType?: number; // 认证方式 + deviceCount?: number; // 设备数量 + createTime?: Date; // 创建时间 + } +} + +// TODO @haohao:packages/constants/src/biz-iot-enum.ts 枚举; + +/** IOT 产品设备类型枚举类 */ +export enum DeviceTypeEnum { + DEVICE = 0, // 直连设备 + GATEWAY = 2, // 网关设备 + GATEWAY_SUB = 1, // 网关子设备 +} + +/** IOT 产品定位类型枚举类 */ +export enum LocationTypeEnum { + IP = 1, // IP 定位 + MANUAL = 3, // 手动定位 + MODULE = 2, // 设备定位 +} + +/** IOT 数据格式(编解码器类型)枚举类 */ +export enum CodecTypeEnum { + ALINK = 'Alink', // 阿里云 Alink 协议 +} + +/** 查询产品分页 */ +export function getProductPage(params: PageParam) { + return requestClient.get>( + '/iot/product/page', + { params }, + ); +} + +/** 查询产品详情 */ +export function getProduct(id: number) { + return requestClient.get(`/iot/product/get?id=${id}`); +} + +/** 新增产品 */ +export function createProduct(data: IotProductApi.Product) { + return requestClient.post('/iot/product/create', data); +} + +/** 修改产品 */ +export function updateProduct(data: IotProductApi.Product) { + return requestClient.put('/iot/product/update', data); +} + +/** 删除产品 */ +export function deleteProduct(id: number) { + return requestClient.delete(`/iot/product/delete?id=${id}`); +} + +/** 导出产品 Excel */ +export function exportProduct(params: any) { + return requestClient.download('/iot/product/export-excel', { params }); +} + +/** 更新产品状态 */ +export function updateProductStatus(id: number, status: number) { + return requestClient.put( + `/iot/product/update-status?id=${id}&status=${status}`, + ); +} + +/** 查询产品(精简)列表 */ +export function getSimpleProductList() { + return requestClient.get('/iot/product/simple-list'); +} + +/** 根据 ProductKey 获取产品信息 */ +export function getProductByKey(productKey: string) { + return requestClient.get('/iot/product/get-by-key', { + params: { productKey }, + }); +} diff --git a/apps/web-antd/src/api/iot/rule/data/rule/index.ts b/apps/web-antd/src/api/iot/rule/data/rule/index.ts new file mode 100644 index 000000000..55e50184a --- /dev/null +++ b/apps/web-antd/src/api/iot/rule/data/rule/index.ts @@ -0,0 +1,83 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace DataRuleApi { + /** IoT 数据流转规则 VO */ + export interface Rule { + id?: number; + name: string; + description?: string; + status?: number; + productId?: number; + productKey?: string; + sourceConfigs?: SourceConfig[]; + sinkIds?: number[]; + createTime?: Date; + } + + /** IoT 数据源配置 */ + export interface SourceConfig { + productId?: number; + productKey?: string; + deviceId?: number; + type?: string; + topic?: string; + } +} + +/** IoT 数据流转规则 */ +export interface DataRule { + id?: number; + name?: string; + description?: string; + status?: number; + productId?: number; + productKey?: string; + sourceConfigs?: any[]; + sinkIds?: number[]; + createTime?: Date; +} + +/** 查询数据流转规则分页 */ +export function getDataRulePage(params: PageParam) { + return requestClient.get>( + '/iot/data-rule/page', + { params }, + ); +} + +/** 查询数据流转规则详情 */ +export function getDataRule(id: number) { + return requestClient.get(`/iot/data-rule/get?id=${id}`); +} + +/** 新增数据流转规则 */ +export function createDataRule(data: DataRule) { + return requestClient.post('/iot/data-rule/create', data); +} + +/** 修改数据流转规则 */ +export function updateDataRule(data: DataRule) { + return requestClient.put('/iot/data-rule/update', data); +} + +/** 删除数据流转规则 */ +export function deleteDataRule(id: number) { + return requestClient.delete(`/iot/data-rule/delete?id=${id}`); +} + +/** 批量删除数据流转规则 */ +export function deleteDataRuleList(ids: number[]) { + return requestClient.delete('/iot/data-rule/delete-list', { + params: { ids: ids.join(',') }, + }); +} + +/** 更新数据流转规则状态 */ +export function updateDataRuleStatus(id: number, status: number) { + return requestClient.put(`/iot/data-rule/update-status`, { + id, + status, + }); +} diff --git a/apps/web-antd/src/api/iot/rule/data/sink/index.ts b/apps/web-antd/src/api/iot/rule/data/sink/index.ts new file mode 100644 index 000000000..4614c2a6e --- /dev/null +++ b/apps/web-antd/src/api/iot/rule/data/sink/index.ts @@ -0,0 +1,148 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace DataSinkApi { + /** IoT 数据流转目的 VO */ + export interface Sink { + id?: number; + name: string; + description?: string; + status?: number; + type: string; + config?: any; + createTime?: Date; + } +} + +/** IoT 数据流转目的 */ +export interface DataSinkVO { + id?: number; + name?: string; + description?: string; + status?: number; + type?: string; + config?: any; + createTime?: Date; +} + +/** IoT 数据目的类型枚举 */ +export enum IotDataSinkTypeEnum { + HTTP = 'HTTP', + KAFKA = 'KAFKA', + MQTT = 'MQTT', + RABBITMQ = 'RABBITMQ', + REDIS_STREAM = 'REDIS_STREAM', + ROCKETMQ = 'ROCKETMQ', +} + +/** HTTP 配置 */ +export interface HttpConfig { + url?: string; + method?: string; + headers?: Record; + timeout?: number; +} + +/** MQTT 配置 */ +export interface MqttConfig { + broker?: string; + port?: number; + topic?: string; + username?: string; + password?: string; + clientId?: string; + qos?: number; +} + +/** Kafka 配置 */ +export interface KafkaMQConfig { + bootstrapServers?: string; + topic?: string; + acks?: string; + retries?: number; + batchSize?: number; +} + +/** RabbitMQ 配置 */ +export interface RabbitMQConfig { + host?: string; + port?: number; + virtualHost?: string; + username?: string; + password?: string; + exchange?: string; + routingKey?: string; + queue?: string; +} + +/** RocketMQ 配置 */ +export interface RocketMQConfig { + nameServer?: string; + topic?: string; + tag?: string; + producerGroup?: string; +} + +/** Redis Stream 配置 */ +export interface RedisStreamMQConfig { + host?: string; + port?: number; + password?: string; + database?: number; + streamKey?: string; + maxLen?: number; +} + +/** 查询数据流转目的分页 */ +export function getDataSinkPage(params: PageParam) { + return requestClient.get>( + '/iot/data-sink/page', + { params }, + ); +} + +/** 查询数据流转目的详情 */ +export function getDataSink(id: number) { + return requestClient.get(`/iot/data-sink/get?id=${id}`); +} + +/** 查询所有数据流转目的列表 */ +export function getDataSinkList() { + return requestClient.get('/iot/data-sink/list'); +} + +/** 查询数据流转目的简单列表 */ +export function getDataSinkSimpleList() { + return requestClient.get('/iot/data-sink/simple-list'); +} + +/** 新增数据流转目的 */ +export function createDataSink(data: DataSinkVO) { + return requestClient.post('/iot/data-sink/create', data); +} + +/** 修改数据流转目的 */ +export function updateDataSink(data: DataSinkVO) { + return requestClient.put('/iot/data-sink/update', data); +} + +/** 删除数据流转目的 */ +export function deleteDataSink(id: number) { + return requestClient.delete(`/iot/data-sink/delete?id=${id}`); +} + +/** 批量删除数据流转目的 */ +export function deleteDataSinkList(ids: number[]) { + return requestClient.delete('/iot/data-sink/delete-list', { + params: { ids: ids.join(',') }, + }); +} + +/** 更新数据流转目的状态 */ +export function updateDataSinkStatus(id: number, status: number) { + return requestClient.put(`/iot/data-sink/update-status`, { + id, + status, + }); +} diff --git a/apps/web-antd/src/api/iot/rule/scene/index.ts b/apps/web-antd/src/api/iot/rule/scene/index.ts new file mode 100644 index 000000000..9a8acf758 --- /dev/null +++ b/apps/web-antd/src/api/iot/rule/scene/index.ts @@ -0,0 +1,159 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace RuleSceneApi { + /** 场景联动规则 */ + export interface SceneRule { + id?: number; + name: string; + description?: string; + status?: number; + triggers?: Trigger[]; + actions?: Action[]; + createTime?: Date; + } + + /** 场景联动规则的触发器 */ + export interface Trigger { + type?: string; + productId?: number; + deviceId?: number; + identifier?: string; + operator?: string; + value?: any; + cronExpression?: string; + conditionGroups?: TriggerConditionGroup[]; + } + + /** 场景联动规则的触发条件组 */ + export interface TriggerConditionGroup { + conditions?: TriggerCondition[]; + operator?: string; + } + + /** 场景联动规则的触发条件 */ + export interface TriggerCondition { + productId?: number; + deviceId?: number; + identifier?: string; + operator?: string; + value?: any; + type?: string; + } + + /** 场景联动规则的动作 */ + export interface Action { + type?: string; + productId?: number; + deviceId?: number; + identifier?: string; + value?: any; + alertConfigId?: number; + } +} + +// TODO @haohao:貌似下面的,和 RuleSceneApi 重复了。 +/** IoT 场景联动规则 */ +export interface IotSceneRule { + id?: number; + name?: string; + description?: string; + status?: number; + triggers?: Trigger[]; + actions?: Action[]; + createTime?: Date; +} + +/** IoT 场景联动规则触发器 */ +export interface Trigger { + type?: string; + productId?: number; + deviceId?: number; + identifier?: string; + operator?: string; + value?: any; + cronExpression?: string; + conditionGroups?: TriggerConditionGroup[]; +} + +/** IoT 场景联动规则触发条件组 */ +export interface TriggerConditionGroup { + conditions?: TriggerCondition[]; + operator?: string; +} + +/** IoT 场景联动规则触发条件 */ +export interface TriggerCondition { + productId?: number; + deviceId?: number; + identifier?: string; + operator?: string; + value?: any; + type?: string; + param?: string; +} + +/** IoT 场景联动规则动作 */ +export interface Action { + type?: string; + productId?: number; + deviceId?: number; + identifier?: string; + value?: any; + alertConfigId?: number; + params?: string; +} + +/** 查询场景联动规则分页 */ +export function getSceneRulePage(params: PageParam) { + return requestClient.get>( + '/iot/scene-rule/page', + { params }, + ); +} + +/** 查询场景联动规则详情 */ +export function getSceneRule(id: number) { + return requestClient.get( + `/iot/scene-rule/get?id=${id}`, + ); +} + +/** 新增场景联动规则 */ +export function createSceneRule(data: IotSceneRule) { + return requestClient.post('/iot/scene-rule/create', data); +} + +/** 修改场景联动规则 */ +export function updateSceneRule(data: IotSceneRule) { + return requestClient.put('/iot/scene-rule/update', data); +} + +/** 删除场景联动规则 */ +export function deleteSceneRule(id: number) { + return requestClient.delete(`/iot/scene-rule/delete?id=${id}`); +} + +/** 批量删除场景联动规则 */ +// TODO @haohao:貌似用上。 +export function deleteSceneRuleList(ids: number[]) { + return requestClient.delete('/iot/scene-rule/delete-list', { + params: { ids: ids.join(',') }, + }); +} + +/** 更新场景联动规则状态 */ +export function updateSceneRuleStatus(id: number, status: number) { + return requestClient.put(`/iot/scene-rule/update-status`, { + id, + status, + }); +} + +/** 获取场景联动规则简单列表 */ +export function getSimpleRuleSceneList() { + return requestClient.get( + '/iot/scene-rule/simple-list', + ); +} diff --git a/apps/web-antd/src/api/iot/statistics/index.ts b/apps/web-antd/src/api/iot/statistics/index.ts new file mode 100644 index 000000000..9f3a04266 --- /dev/null +++ b/apps/web-antd/src/api/iot/statistics/index.ts @@ -0,0 +1,71 @@ +import { requestClient } from '#/api/request'; + +export namespace IotStatisticsApi { + // TODO @haohao:需要跟后端对齐,必要的 ReqVO、RespVO + /** 统计摘要数据 */ + export interface StatisticsSummary { + productCategoryCount: number; + productCount: number; + deviceCount: number; + deviceMessageCount: number; + productCategoryTodayCount: number; + productTodayCount: number; + deviceTodayCount: number; + deviceMessageTodayCount: number; + deviceOnlineCount: number; + deviceOfflineCount: number; + deviceInactiveCount: number; + productCategoryDeviceCounts: Record; + } + + /** 时间戳-数值的键值对类型 */ + export interface TimeValueItem { + [key: string]: number; + } + + /** 消息统计数据类型 */ + export interface DeviceMessageSummary { + statType: number; + upstreamCounts: TimeValueItem[]; + downstreamCounts: TimeValueItem[]; + } + + /** 消息统计数据项(按日期) */ + export interface DeviceMessageSummaryByDate { + time: string; + upstreamCount: number; + downstreamCount: number; + } + + /** 消息统计接口参数 */ + export interface DeviceMessageReq { + interval: number; + times?: string[]; + } +} + +/** 获取 IoT 统计摘要数据 */ +export function getStatisticsSummary() { + return requestClient.get( + '/iot/statistics/get-summary', + ); +} + +/** 获取设备消息的数据统计(按日期) */ +export function getDeviceMessageSummaryByDate( + params: IotStatisticsApi.DeviceMessageReq, +) { + return requestClient.get( + '/iot/statistics/get-device-message-summary-by-date', + { params }, + ); +} + +// TODO @haohao:貌似这里,没用到?是不是后面哪里用,或者可以删除哈? +/** 获取设备消息统计摘要 */ +export function getDeviceMessageSummary(statType: number) { + return requestClient.get( + '/iot/statistics/get-device-message-summary', + { params: { statType } }, + ); +} diff --git a/apps/web-antd/src/api/iot/thingmodel/index.ts b/apps/web-antd/src/api/iot/thingmodel/index.ts new file mode 100644 index 000000000..9e89b869c --- /dev/null +++ b/apps/web-antd/src/api/iot/thingmodel/index.ts @@ -0,0 +1,186 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace ThingModelApi { + /** IoT 物模型数据 VO */ + export interface ThingModel { + id?: number; + productId?: number; + productKey?: string; + identifier: string; + name: string; + desc?: string; + type: string; + property?: ThingModelProperty; + event?: ThingModelEvent; + service?: ThingModelService; + } + + /** IoT 物模型属性 */ + export interface Property { + identifier: string; + name: string; + accessMode: string; + dataType: string; + dataSpecs?: any; + dataSpecsList?: any[]; + desc?: string; + } + + /** IoT 物模型服务 */ + export interface Service { + identifier: string; + name: string; + callType: string; + inputData?: any[]; + outputData?: any[]; + desc?: string; + } + + /** IoT 物模型事件 */ + export interface Event { + identifier: string; + name: string; + type: string; + outputData?: any[]; + desc?: string; + } +} + +/** IoT 物模型数据 */ +export interface ThingModelData { + id?: number; + productId?: number; + productKey?: string; + identifier?: string; + name?: string; + desc?: string; + type?: string; + dataType?: string; + property?: ThingModelProperty; + event?: ThingModelEvent; + service?: ThingModelService; +} + +/** IoT 物模型属性 */ +export interface ThingModelProperty { + identifier?: string; + name?: string; + accessMode?: string; + dataType?: string; + dataSpecs?: any; + dataSpecsList?: any[]; + desc?: string; +} + +/** IoT 物模型服务 */ +export interface ThingModelService { + identifier?: string; + name?: string; + callType?: string; + inputData?: any[]; + outputData?: any[]; + desc?: string; +} + +/** IoT 物模型事件 */ +export interface ThingModelEvent { + identifier?: string; + name?: string; + type?: string; + outputData?: any[]; + desc?: string; +} + +/** IoT 数据定义(数值型) */ +export interface DataSpecsNumberData { + min?: number | string; + max?: number | string; + step?: number | string; + unit?: string; + unitName?: string; +} + +/** IoT 数据定义(枚举/布尔型) */ +export interface DataSpecsEnumOrBoolData { + value: number | string; + name: string; +} + +/** IoT 物模型表单校验规则 */ +export interface ThingModelFormRules { + [key: string]: any; +} + +/** 验证布尔型名称 */ +export function validateBoolName(_rule: any, value: any, callback: any) { + if (value) { + callback(); + } else { + callback(new Error('枚举描述不能为空')); + } +} + +/** 查询产品物模型分页 */ +export function getThingModelPage(params: PageParam) { + return requestClient.get>( + '/iot/thing-model/page', + { params }, + ); +} + +/** 查询产品物模型详情 */ +export function getThingModel(id: number) { + return requestClient.get( + `/iot/thing-model/get?id=${id}`, + ); +} + +/** 根据产品 ID 查询物模型列表 */ +export function getThingModelListByProductId(productId: number) { + return requestClient.get( + '/iot/thing-model/list', + { params: { productId } }, + ); +} + +/** 新增物模型 */ +export function createThingModel(data: ThingModelData) { + return requestClient.post('/iot/thing-model/create', data); +} + +/** 修改物模型 */ +export function updateThingModel(data: ThingModelData) { + return requestClient.put('/iot/thing-model/update', data); +} + +/** 删除物模型 */ +export function deleteThingModel(id: number) { + return requestClient.delete(`/iot/thing-model/delete?id=${id}`); +} + +/** 获取物模型 TSL */ +export function getThingModelTSL(productId: number) { + return requestClient.get( + '/iot/thing-model/get-tsl', + { params: { productId } }, + ); +} + +/** 导入物模型 TSL +export function importThingModelTSL(productId: number, tslData: any) { + return requestClient.post('/iot/thing-model/import-tsl', { + productId, + tslData, + }); +} + */ + +/** 导出物模型 TSL +export function exportThingModelTSL(productId: number) { + return requestClient.get('/iot/thing-model/export-tsl', { + params: { productId }, + }); +} + */ diff --git a/apps/web-antd/src/api/mall/product/brand/index.ts b/apps/web-antd/src/api/mall/product/brand/index.ts new file mode 100644 index 000000000..d5483e218 --- /dev/null +++ b/apps/web-antd/src/api/mall/product/brand/index.ts @@ -0,0 +1,52 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallBrandApi { + /** 商品品牌 */ + export interface Brand { + id?: number; // 品牌编号 + name: string; // 品牌名称 + picUrl: string; // 品牌图片 + sort?: number; // 品牌排序 + description?: string; // 品牌描述 + status: number; // 开启状态 + } +} + +/** 创建商品品牌 */ +export function createBrand(data: MallBrandApi.Brand) { + return requestClient.post('/product/brand/create', data); +} + +/** 更新商品品牌 */ +export function updateBrand(data: MallBrandApi.Brand) { + return requestClient.put('/product/brand/update', data); +} + +/** 删除商品品牌 */ +export function deleteBrand(id: number) { + return requestClient.delete(`/product/brand/delete?id=${id}`); +} + +/** 获得商品品牌 */ +export function getBrand(id: number) { + return requestClient.get(`/product/brand/get?id=${id}`); +} + +/** 获得商品品牌列表 */ +export function getBrandPage(params: PageParam) { + return requestClient.get>( + '/product/brand/page', + { + params, + }, + ); +} + +/** 获得商品品牌精简信息列表 */ +export function getSimpleBrandList() { + return requestClient.get( + '/product/brand/list-all-simple', + ); +} diff --git a/apps/web-antd/src/api/mall/product/category/index.ts b/apps/web-antd/src/api/mall/product/category/index.ts new file mode 100644 index 000000000..a67662a7a --- /dev/null +++ b/apps/web-antd/src/api/mall/product/category/index.ts @@ -0,0 +1,45 @@ +import { requestClient } from '#/api/request'; + +export namespace MallCategoryApi { + /** 产品分类 */ + export interface Category { + id?: number; // 分类编号 + parentId?: number; // 父分类编号 + name: string; // 分类名称 + picUrl: string; // 移动端分类图 + sort: number; // 分类排序 + status: number; // 开启状态 + } +} + +/** 创建商品分类 */ +export function createCategory(data: MallCategoryApi.Category) { + return requestClient.post('/product/category/create', data); +} + +/** 更新商品分类 */ +export function updateCategory(data: MallCategoryApi.Category) { + return requestClient.put('/product/category/update', data); +} + +/** 删除商品分类 */ +export function deleteCategory(id: number) { + return requestClient.delete(`/product/category/delete?id=${id}`); +} + +/** 获得商品分类 */ +export function getCategory(id: number) { + return requestClient.get( + `/product/category/get?id=${id}`, + ); +} + +/** 获得商品分类列表 */ +export function getCategoryList(params: any) { + return requestClient.get( + '/product/category/list', + { + params, + }, + ); +} diff --git a/apps/web-antd/src/api/mall/product/comment/index.ts b/apps/web-antd/src/api/mall/product/comment/index.ts new file mode 100644 index 000000000..788f94a9a --- /dev/null +++ b/apps/web-antd/src/api/mall/product/comment/index.ts @@ -0,0 +1,80 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallCommentApi { + /** 商品评论 */ + export interface Comment { + id: number; // 评论编号 + userId: number; // 用户编号 + userNickname: string; // 用户昵称 + userAvatar: string; // 用户头像 + anonymous: boolean; // 是否匿名 + orderId: number; // 订单编号 + orderItemId: number; // 订单项编号 + spuId: number; // 商品SPU编号 + spuName: string; // 商品名称 + skuId: number; // 商品SKU编号 + visible: boolean; // 是否可见 + scores: number; // 总评分 + descriptionScores: number; // 描述评分 + benefitScores: number; // 服务评分 + content: string; // 评论内容 + picUrls: string[]; // 评论图片 + replyStatus: boolean; // 是否回复 + replyUserId: number; // 回复人编号 + replyContent: string; // 回复内容 + replyTime: Date; // 回复时间 + createTime: Date; // 创建时间 + skuProperties: { + propertyId: number; // 属性 ID + propertyName: string; // 属性名称 + valueId: number; // 属性值 ID + valueName: string; // 属性值名称 + }[]; // SKU 属性数组 + } + + /** 评论可见性更新请求 */ + export interface CommentVisibleUpdateReqVO { + id: number; // 评论编号 + visible: boolean; // 是否可见 + } + + /** 评论回复请求 */ + export interface CommentReplyReqVO { + id: number; // 评论编号 + replyContent: string; // 回复内容 + } +} + +/** 查询商品评论列表 */ +export function getCommentPage(params: PageParam) { + return requestClient.get>( + '/product/comment/page', + { params }, + ); +} + +/** 查询商品评论详情 */ +export function getComment(id: number) { + return requestClient.get( + `/product/comment/get?id=${id}`, + ); +} + +/** 添加自评 */ +export function createComment(data: MallCommentApi.Comment) { + return requestClient.post('/product/comment/create', data); +} + +/** 显示 / 隐藏评论 */ +export function updateCommentVisible( + data: MallCommentApi.CommentVisibleUpdateReqVO, +) { + return requestClient.put('/product/comment/update-visible', data); +} + +/** 商家回复 */ +export function replyComment(data: MallCommentApi.CommentReplyReqVO) { + return requestClient.put('/product/comment/reply', data); +} diff --git a/apps/web-antd/src/api/mall/product/favorite/index.ts b/apps/web-antd/src/api/mall/product/favorite/index.ts new file mode 100644 index 000000000..d9d47f3fc --- /dev/null +++ b/apps/web-antd/src/api/mall/product/favorite/index.ts @@ -0,0 +1,20 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallFavoriteApi { + /** 商品收藏 */ + export interface Favorite { + id?: number; // 收藏编号 + userId?: string; // 用户编号 + spuId?: number; // 商品 SPU 编号 + } +} + +/** 获得商品收藏列表 */ +export function getFavoritePage(params: PageParam) { + return requestClient.get>( + '/product/favorite/page', + { params }, + ); +} diff --git a/apps/web-antd/src/api/mall/product/history/index.ts b/apps/web-antd/src/api/mall/product/history/index.ts new file mode 100644 index 000000000..30a892d52 --- /dev/null +++ b/apps/web-antd/src/api/mall/product/history/index.ts @@ -0,0 +1,21 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallHistoryApi { + /** 商品浏览记录 */ + export interface BrowseHistory { + id?: number; // 记录编号 + userId?: number; // 用户编号 + spuId?: number; // 商品 SPU 编号 + createTime?: Date; // 浏览时间 + } +} + +/** 获得商品浏览记录分页 */ +export function getBrowseHistoryPage(params: PageParam) { + return requestClient.get>( + '/product/browse-history/page', + { params }, + ); +} diff --git a/apps/web-antd/src/api/mall/product/property/index.ts b/apps/web-antd/src/api/mall/product/property/index.ts new file mode 100644 index 000000000..0834e345e --- /dev/null +++ b/apps/web-antd/src/api/mall/product/property/index.ts @@ -0,0 +1,97 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallPropertyApi { + /** 商品属性 */ + export interface Property { + id?: number; // 属性编号 + name: string; // 名称 + remark?: string; // 备注 + } + + /** 属性值 */ + export interface PropertyValue { + id?: number; // 属性值编号 + propertyId?: number; // 属性项的编号 + name: string; // 名称 + remark?: string; // 备注 + } +} + +/** 创建属性项 */ +export function createProperty(data: MallPropertyApi.Property) { + return requestClient.post('/product/property/create', data); +} + +/** 更新属性项 */ +export function updateProperty(data: MallPropertyApi.Property) { + return requestClient.put('/product/property/update', data); +} + +/** 删除属性项 */ +export function deleteProperty(id: number) { + return requestClient.delete(`/product/property/delete?id=${id}`); +} + +/** 获得属性项 */ +export function getProperty(id: number) { + return requestClient.get( + `/product/property/get?id=${id}`, + ); +} + +/** 获得属性项分页 */ +export function getPropertyPage(params: PageParam) { + return requestClient.get>( + '/product/property/page', + { params }, + ); +} + +/** 获得属性项精简列表 */ +export function getPropertySimpleList() { + return requestClient.get( + '/product/property/simple-list', + ); +} + +/** 获得属性值分页 */ +export function getPropertyValuePage(params: PageParam) { + return requestClient.get>( + '/product/property/value/page', + { params }, + ); +} + +/** 获得属性值 */ +export function getPropertyValue(id: number) { + return requestClient.get( + `/product/property/value/get?id=${id}`, + ); +} + +/** 创建属性值 */ +export function createPropertyValue(data: MallPropertyApi.PropertyValue) { + return requestClient.post('/product/property/value/create', data); +} + +/** 更新属性值 */ +export function updatePropertyValue(data: MallPropertyApi.PropertyValue) { + return requestClient.put('/product/property/value/update', data); +} + +/** 删除属性值 */ +export function deletePropertyValue(id: number) { + return requestClient.delete(`/product/property/value/delete?id=${id}`); +} + +/** 获得属性值精简列表 */ +export function getPropertyValueSimpleList(propertyId: number) { + return requestClient.get( + '/product/property/value/simple-list', + { + params: { propertyId }, + }, + ); +} diff --git a/apps/web-antd/src/api/mall/product/spu/index.ts b/apps/web-antd/src/api/mall/product/spu/index.ts new file mode 100644 index 000000000..0c9f4ce94 --- /dev/null +++ b/apps/web-antd/src/api/mall/product/spu/index.ts @@ -0,0 +1,122 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallSpuApi { + /** 商品 SPU */ + export interface Spu { + id?: number; // 商品编号 + name?: string; // 商品名称 + categoryId?: number; // 商品分类 + keyword?: string; // 关键字 + unit?: number; // 单位 + picUrl?: string; // 商品封面图 + sliderPicUrls?: string[]; // 商品轮播图 + introduction?: string; // 商品简介 + deliveryTypes?: number[]; // 配送方式 + deliveryTemplateId?: number; // 运费模版 + brandId?: number; // 商品品牌编号 + specType?: boolean; // 商品规格 + subCommissionType?: boolean; // 分销类型 + skus?: Sku[]; // sku数组 + description?: string; // 商品详情 + sort?: number; // 商品排序 + giveIntegral?: number; // 赠送积分 + virtualSalesCount?: number; // 虚拟销量 + price?: number; // 商品价格 + combinationPrice?: number; // 商品拼团价格 + seckillPrice?: number; // 商品秒杀价格 + salesCount?: number; // 商品销量 + marketPrice?: number; // 市场价 + costPrice?: number; // 成本价 + stock?: number; // 商品库存 + createTime?: Date; // 商品创建时间 + status?: number; // 商品状态 + browseCount?: number; // 浏览量 + } + + /** 商品 SKU */ + export interface Sku { + id?: number; // 商品 SKU 编号 + name?: string; // 商品 SKU 名称 + spuId?: number; // SPU 编号 + properties?: Property[]; // 属性数组 + price?: number | string; // 商品价格 + marketPrice?: number | string; // 市场价 + costPrice?: number | string; // 成本价 + barCode?: string; // 商品条码 + picUrl?: string; // 图片地址 + stock?: number; // 库存 + weight?: number; // 商品重量,单位:kg 千克 + volume?: number; // 商品体积,单位:m^3 平米 + firstBrokeragePrice?: number | string; // 一级分销的佣金 + secondBrokeragePrice?: number | string; // 二级分销的佣金 + salesCount?: number; // 商品销量 + } + + /** 商品属性 */ + export interface Property { + propertyId?: number; // 属性编号 + propertyName?: string; // 属性名称 + valueId?: number; // 属性值编号 + valueName?: string; // 属性值名称 + } + + /** 商品状态更新请求 */ + export interface SpuStatusUpdateReqVO { + id: number; // 商品编号 + status: number; // 商品状态 + } +} + +/** 获得商品 SPU 列表 */ +export function getSpuPage(params: PageParam) { + return requestClient.get>('/product/spu/page', { + params, + }); +} + +/** 获得商品 SPU 列表 tabsCount */ +export function getTabsCount() { + return requestClient.get>('/product/spu/get-count'); +} + +/** 创建商品 SPU */ +export function createSpu(data: MallSpuApi.Spu) { + return requestClient.post('/product/spu/create', data); +} + +/** 更新商品 SPU */ +export function updateSpu(data: MallSpuApi.Spu) { + return requestClient.put('/product/spu/update', data); +} + +/** 更新商品 SPU 状态 */ +export function updateStatus(data: MallSpuApi.SpuStatusUpdateReqVO) { + return requestClient.put('/product/spu/update-status', data); +} + +/** 获得商品 SPU */ +export function getSpu(id: number) { + return requestClient.get(`/product/spu/get-detail?id=${id}`); +} + +/** 获得商品 SPU 详情列表 */ +export function getSpuDetailList(ids: number[]) { + return requestClient.get(`/product/spu/list?spuIds=${ids}`); +} + +/** 删除商品 SPU */ +export function deleteSpu(id: number) { + return requestClient.delete(`/product/spu/delete?id=${id}`); +} + +/** 导出商品 SPU Excel */ +export function exportSpu(params: PageParam) { + return requestClient.download('/product/spu/export-excel', { params }); +} + +/** 获得商品 SPU 精简列表 */ +export function getSpuSimpleList() { + return requestClient.get('/product/spu/list-all-simple'); +} diff --git a/apps/web-antd/src/api/mall/promotion/article/category/index.ts b/apps/web-antd/src/api/mall/promotion/article/category/index.ts new file mode 100644 index 000000000..49297af3f --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/article/category/index.ts @@ -0,0 +1,55 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallArticleCategoryApi { + /** 文章分类 */ + export interface ArticleCategory { + id: number; // 分类编号 + name: string; // 分类名称 + picUrl: string; // 分类图片 + status: number; // 状态 + sort: number; // 排序 + } +} + +/** 查询文章分类列表 */ +export function getArticleCategoryPage(params: PageParam) { + return requestClient.get>( + '/promotion/article-category/page', + { params }, + ); +} + +/** 查询文章分类精简信息列表 */ +export function getSimpleArticleCategoryList() { + return requestClient.get( + '/promotion/article-category/list-all-simple', + ); +} + +/** 查询文章分类详情 */ +export function getArticleCategory(id: number) { + return requestClient.get( + `/promotion/article-category/get?id=${id}`, + ); +} + +/** 新增文章分类 */ +export function createArticleCategory( + data: MallArticleCategoryApi.ArticleCategory, +) { + return requestClient.post('/promotion/article-category/create', data); +} + +/** 修改文章分类 */ +export function updateArticleCategory( + data: MallArticleCategoryApi.ArticleCategory, +) { + return requestClient.put('/promotion/article-category/update', data); +} + +/** 删除文章分类 */ +export function deleteArticleCategory(id: number) { + return requestClient.delete(`/promotion/article-category/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/article/index.ts b/apps/web-antd/src/api/mall/promotion/article/index.ts new file mode 100644 index 000000000..cf2f90b7a --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/article/index.ts @@ -0,0 +1,52 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallArticleApi { + /** 文章管理 */ + export interface Article { + id: number; // 文章编号 + categoryId: number; // 分类编号 + title: string; // 文章标题 + author: string; // 作者 + picUrl: string; // 封面图 + introduction: string; // 文章简介 + browseCount: string; // 浏览数量 + sort: number; // 排序 + status: number; // 状态 + spuId: number; // 商品编号 + recommendHot: boolean; // 是否热门 + recommendBanner: boolean; // 是否轮播图 + content: string; // 文章内容 + } +} + +/** 查询文章管理列表 */ +export function getArticlePage(params: PageParam) { + return requestClient.get>( + '/promotion/article/page', + { params }, + ); +} + +/** 查询文章管理详情 */ +export function getArticle(id: number) { + return requestClient.get( + `/promotion/article/get?id=${id}`, + ); +} + +/** 新增文章管理 */ +export function createArticle(data: MallArticleApi.Article) { + return requestClient.post('/promotion/article/create', data); +} + +/** 修改文章管理 */ +export function updateArticle(data: MallArticleApi.Article) { + return requestClient.put('/promotion/article/update', data); +} + +/** 删除文章管理 */ +export function deleteArticle(id: number) { + return requestClient.delete(`/promotion/article/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/banner/index.ts b/apps/web-antd/src/api/mall/promotion/banner/index.ts new file mode 100644 index 000000000..d3b68156b --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/banner/index.ts @@ -0,0 +1,47 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallBannerApi { + /** Banner 信息 */ + export interface Banner { + id: number; // Banner 编号 + title: string; // Banner 标题 + picUrl: string; // Banner 图片 + status: number; // 状态 + url: string; // 链接地址 + position: number; // Banner 位置 + sort: number; // 排序 + memo: string; // 备注 + } +} + +/** 查询 Banner 管理列表 */ +export function getBannerPage(params: PageParam) { + return requestClient.get>( + '/promotion/banner/page', + { params }, + ); +} + +/** 查询 Banner 管理详情 */ +export function getBanner(id: number) { + return requestClient.get( + `/promotion/banner/get?id=${id}`, + ); +} + +/** 新增 Banner 管理 */ +export function createBanner(data: MallBannerApi.Banner) { + return requestClient.post('/promotion/banner/create', data); +} + +/** 修改 Banner 管理 */ +export function updateBanner(data: MallBannerApi.Banner) { + return requestClient.put('/promotion/banner/update', data); +} + +/** 删除 Banner 管理 */ +export function deleteBanner(id: number) { + return requestClient.delete(`/promotion/banner/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/bargain/bargainActivity.ts b/apps/web-antd/src/api/mall/promotion/bargain/bargainActivity.ts new file mode 100644 index 000000000..f13b64916 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/bargain/bargainActivity.ts @@ -0,0 +1,72 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallBargainActivityApi { + /** 砍价活动 */ + export interface BargainActivity { + id?: number; // 活动编号 + name?: string; // 活动名称 + startTime?: Date; // 开始时间 + endTime?: Date; // 结束时间 + status?: number; // 状态 + helpMaxCount?: number; // 达到该人数,才能砍到低价 + bargainCount?: number; // 最大帮砍次数 + totalLimitCount?: number; // 最大购买次数 + spuId: number; // 商品 SPU 编号 + skuId: number; // 商品 SKU 编号 + bargainFirstPrice: number; // 砍价起始价格,单位分 + bargainMinPrice: number; // 砍价底价 + stock: number; // 活动库存 + randomMinPrice?: number; // 用户每次砍价的最小金额,单位:分 + randomMaxPrice?: number; // 用户每次砍价的最大金额,单位:分 + } + + /** 砍价活动所需属性。选择的商品和属性的时候使用方便使用活动的通用封装 */ + export interface BargainProduct { + spuId: number; // 商品 SPU 编号 + skuId: number; // 商品 SKU 编号 + bargainFirstPrice: number; // 砍价起始价格,单位分 + bargainMinPrice: number; // 砍价底价 + stock: number; // 活动库存 + } +} + +/** 查询砍价活动列表 */ +export function getBargainActivityPage(params: PageParam) { + return requestClient.get>( + '/promotion/bargain-activity/page', + { params }, + ); +} + +/** 查询砍价活动详情 */ +export function getBargainActivity(id: number) { + return requestClient.get( + `/promotion/bargain-activity/get?id=${id}`, + ); +} + +/** 新增砍价活动 */ +export function createBargainActivity( + data: MallBargainActivityApi.BargainActivity, +) { + return requestClient.post('/promotion/bargain-activity/create', data); +} + +/** 修改砍价活动 */ +export function updateBargainActivity( + data: MallBargainActivityApi.BargainActivity, +) { + return requestClient.put('/promotion/bargain-activity/update', data); +} + +/** 关闭砍价活动 */ +export function closeBargainActivity(id: number) { + return requestClient.put(`/promotion/bargain-activity/close?id=${id}`); +} + +/** 删除砍价活动 */ +export function deleteBargainActivity(id: number) { + return requestClient.delete(`/promotion/bargain-activity/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/bargain/bargainHelp.ts b/apps/web-antd/src/api/mall/promotion/bargain/bargainHelp.ts new file mode 100644 index 000000000..488d34f42 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/bargain/bargainHelp.ts @@ -0,0 +1,22 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallBargainHelpApi { + /** 砍价记录 */ + export interface BargainHelp { + id: number; // 记录编号 + record: number; // 砍价记录编号 + userId: number; // 用户编号 + reducePrice: number; // 砍掉金额 + endTime: Date; // 结束时间 + } +} + +/** 查询砍价记录列表 */ +export function getBargainHelpPage(params: PageParam) { + return requestClient.get>( + '/promotion/bargain-help/page', + { params }, + ); +} diff --git a/apps/web-antd/src/api/mall/promotion/bargain/bargainRecord.ts b/apps/web-antd/src/api/mall/promotion/bargain/bargainRecord.ts new file mode 100644 index 000000000..345606b8e --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/bargain/bargainRecord.ts @@ -0,0 +1,27 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallBargainRecordApi { + /** 砍价记录 */ + export interface BargainRecord { + id: number; // 记录编号 + activityId: number; // 活动编号 + userId: number; // 用户编号 + spuId: number; // 商品 SPU 编号 + skuId: number; // 商品 SKU 编号 + bargainFirstPrice: number; // 砍价起始价格 + bargainPrice: number; // 砍价价格 + status: number; // 状态 + orderId: number; // 订单编号 + endTime: Date; // 结束时间 + } +} + +/** 查询砍价记录列表 */ +export function getBargainRecordPage(params: PageParam) { + return requestClient.get>( + '/promotion/bargain-record/page', + { params }, + ); +} diff --git a/apps/web-antd/src/api/mall/promotion/combination/combinationActivity.ts b/apps/web-antd/src/api/mall/promotion/combination/combinationActivity.ts new file mode 100644 index 000000000..e497ae204 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/combination/combinationActivity.ts @@ -0,0 +1,79 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallCombinationActivityApi { + /** 拼团活动 */ + export interface CombinationActivity { + id?: number; // 活动编号 + name?: string; // 活动名称 + spuId?: number; // 商品 SPU 编号 + totalLimitCount?: number; // 总限购数量 + singleLimitCount?: number; // 单次限购数量 + startTime?: Date; // 开始时间 + endTime?: Date; // 结束时间 + userSize?: number; // 用户数量 + totalCount?: number; // 总数量 + successCount?: number; // 成功数量 + orderUserCount?: number; // 订单用户数量 + virtualGroup?: number; // 虚拟成团 + status?: number; // 状态 + limitDuration?: number; // 限制时长 + combinationPrice?: number; // 拼团价格 + products: CombinationProduct[]; // 商品列表 + } + + /** 拼团活动所需属性 */ + export interface CombinationProduct { + spuId: number; // 商品 SPU 编号 + skuId: number; // 商品 SKU 编号 + combinationPrice: number; // 拼团价格 + } +} + +/** 查询拼团活动列表 */ +export function getCombinationActivityPage(params: PageParam) { + return requestClient.get< + PageResult + >('/promotion/combination-activity/page', { params }); +} + +/** 查询拼团活动详情 */ +export function getCombinationActivity(id: number) { + return requestClient.get( + `/promotion/combination-activity/get?id=${id}`, + ); +} + +/** 获得拼团活动列表,基于活动编号数组 */ +export function getCombinationActivityListByIds(ids: number[]) { + return requestClient.get( + `/promotion/combination-activity/list-by-ids?ids=${ids}`, + ); +} + +/** 新增拼团活动 */ +export function createCombinationActivity( + data: MallCombinationActivityApi.CombinationActivity, +) { + return requestClient.post('/promotion/combination-activity/create', data); +} + +/** 修改拼团活动 */ +export function updateCombinationActivity( + data: MallCombinationActivityApi.CombinationActivity, +) { + return requestClient.put('/promotion/combination-activity/update', data); +} + +/** 关闭拼团活动 */ +export function closeCombinationActivity(id: number) { + return requestClient.put(`/promotion/combination-activity/close?id=${id}`); +} + +/** 删除拼团活动 */ +export function deleteCombinationActivity(id: number) { + return requestClient.delete( + `/promotion/combination-activity/delete?id=${id}`, + ); +} diff --git a/apps/web-antd/src/api/mall/promotion/combination/combinationRecord.ts b/apps/web-antd/src/api/mall/promotion/combination/combinationRecord.ts new file mode 100644 index 000000000..3faa2e7bb --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/combination/combinationRecord.ts @@ -0,0 +1,44 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallCombinationRecordApi { + /** 拼团记录 */ + export interface CombinationRecord { + id: number; // 拼团记录编号 + activityId: number; // 拼团活动编号 + nickname: string; // 用户昵称 + avatar: string; // 用户头像 + headId: number; // 团长编号 + expireTime: string; // 过期时间 + userSize: number; // 可参团人数 + userCount: number; // 已参团人数 + status: number; // 拼团状态 + spuName: string; // 商品名字 + picUrl: string; // 商品图片 + virtualGroup: boolean; // 是否虚拟成团 + startTime: string; // 开始时间 (订单付款后开始的时间) + endTime: string; // 结束时间(成团时间/失败时间) + } + + /** 拼团记录概要信息 */ + export interface RecordSummary { + pendingCount: number; // 待成团数量 + successCount: number; // 已成团数量 + failCount: number; // 已失败数量 + } +} + +/** 查询拼团记录列表 */ +export function getCombinationRecordPage(params: PageParam) { + return requestClient.get< + PageResult + >('/promotion/combination-record/page', { params }); +} + +/** 获得拼团记录的概要信息 */ +export function getCombinationRecordSummary() { + return requestClient.get( + '/promotion/combination-record/get-summary', + ); +} diff --git a/apps/web-antd/src/api/mall/promotion/coupon/coupon.ts b/apps/web-antd/src/api/mall/promotion/coupon/coupon.ts new file mode 100644 index 000000000..99792e4ed --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/coupon/coupon.ts @@ -0,0 +1,49 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallCouponApi { + /** 优惠券 */ + export interface Coupon { + id: number; // 优惠券编号 + name: string; // 优惠券名称 + status: number; // 优惠券状态 + type: number; // 优惠券类型 + price: number; // 优惠券金额 + usePrice: number; // 使用门槛 + productScope: number; // 商品范围 + productSpuIds: number[]; // 商品编号数组 + validityType: number; // 有效期类型 + validStartTime: Date; // 固定日期-生效开始时间 + validEndTime: Date; // 固定日期-生效结束时间 + fixedStartTerm: number; // 领取日期-开始天数 + fixedEndTerm: number; // 领取日期-结束天数 + takeLimitCount: number; // 每人限领个数 + usePriceEnabled: boolean; // 是否设置满多少金额可用 + productCategoryIds: number[]; // 商品分类编号数组 + } + + /** 发送优惠券 */ + export interface CouponSendReqVO { + templateId: number; // 优惠券编号 + userIds: number[]; // 用户编号数组 + } +} + +/** 删除优惠劵 */ +export function deleteCoupon(id: number) { + return requestClient.delete(`/promotion/coupon/delete?id=${id}`); +} + +/** 获得优惠劵分页 */ +export function getCouponPage(params: PageParam) { + return requestClient.get>( + '/promotion/coupon/page', + { params }, + ); +} + +/** 发送优惠券 */ +export function sendCoupon(data: MallCouponApi.CouponSendReqVO) { + return requestClient.post('/promotion/coupon/send', data); +} diff --git a/apps/web-antd/src/api/mall/promotion/coupon/couponTemplate.ts b/apps/web-antd/src/api/mall/promotion/coupon/couponTemplate.ts new file mode 100644 index 000000000..d5453ed79 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/coupon/couponTemplate.ts @@ -0,0 +1,78 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallCouponTemplateApi { + /** 优惠券模板 */ + export interface CouponTemplate { + id: number; // 模板编号 + name: string; // 模板名称 + status: number; // 状态 + totalCount: number; // 发放数量 + takeLimitCount: number; // 每人限领个数 + takeType: number; // 领取方式 + usePrice: number; // 使用门槛 + productScope: number; // 商品范围 + productScopeValues: number[]; // 商品范围值 + validityType: number; // 有效期类型 + validStartTime: Date; // 固定日期-生效开始时间 + validEndTime: Date; // 固定日期-生效结束时间 + fixedStartTerm: number; // 领取日期-开始天数 + fixedEndTerm: number; // 领取日期-结束天数 + discountType: number; // 优惠类型 + discountPercent?: number; // 折扣百分比 + discountPrice: number; // 优惠金额 + discountLimitPrice?: number; // 折扣上限 + takeCount: number; // 已领取数量 + useCount: number; // 已使用数量 + } +} + +/** 创建优惠劵模板 */ +export function createCouponTemplate( + data: MallCouponTemplateApi.CouponTemplate, +) { + return requestClient.post('/promotion/coupon-template/create', data); +} + +/** 更新优惠劵模板 */ +export function updateCouponTemplate( + data: MallCouponTemplateApi.CouponTemplate, +) { + return requestClient.put('/promotion/coupon-template/update', data); +} + +/** 更新优惠劵模板的状态 */ +export function updateCouponTemplateStatus(id: number, status: number) { + return requestClient.put('/promotion/coupon-template/update-status', { + id, + status, + }); +} + +/** 删除优惠劵模板 */ +export function deleteCouponTemplate(id: number) { + return requestClient.delete(`/promotion/coupon-template/delete?id=${id}`); +} + +/** 获得优惠劵模板 */ +export function getCouponTemplate(id: number) { + return requestClient.get( + `/promotion/coupon-template/get?id=${id}`, + ); +} + +/** 获得优惠劵模板分页 */ +export function getCouponTemplatePage(params: PageParam) { + return requestClient.get>( + '/promotion/coupon-template/page', + { params }, + ); +} + +/** 获得优惠劵模板列表 */ +export function getCouponTemplateList(ids: number[]) { + return requestClient.get( + `/promotion/coupon-template/list?ids=${ids}`, + ); +} diff --git a/apps/web-antd/src/api/mall/promotion/discount/discountActivity.ts b/apps/web-antd/src/api/mall/promotion/discount/discountActivity.ts new file mode 100644 index 000000000..1236140f4 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/discount/discountActivity.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallDiscountActivityApi { + /** 限时折扣相关属性 */ + export interface DiscountProduct { + spuId: number; // 商品 SPU 编号 + skuId: number; // 商品 SKU 编号 + discountType: number; // 折扣类型 + discountPercent: number; // 折扣百分比 + discountPrice: number; // 折扣价格 + } + + /** 限时折扣活动 */ + export interface DiscountActivity { + id?: number; // 活动编号 + spuId?: number; // 商品 SPU 编号 + name?: string; // 活动名称 + status?: number; // 状态 + remark?: string; // 备注 + startTime?: Date; // 开始时间 + endTime?: Date; // 结束时间 + products?: DiscountProduct[]; // 商品列表 + } +} + +/** 查询限时折扣活动列表 */ +export function getDiscountActivityPage(params: PageParam) { + return requestClient.get< + PageResult + >('/promotion/discount-activity/page', { params }); +} + +/** 查询限时折扣活动详情 */ +export function getDiscountActivity(id: number) { + return requestClient.get( + `/promotion/discount-activity/get?id=${id}`, + ); +} + +/** 新增限时折扣活动 */ +export function createDiscountActivity( + data: MallDiscountActivityApi.DiscountActivity, +) { + return requestClient.post('/promotion/discount-activity/create', data); +} + +/** 修改限时折扣活动 */ +export function updateDiscountActivity( + data: MallDiscountActivityApi.DiscountActivity, +) { + return requestClient.put('/promotion/discount-activity/update', data); +} + +/** 关闭限时折扣活动 */ +export function closeDiscountActivity(id: number) { + return requestClient.put(`/promotion/discount-activity/close?id=${id}`); +} + +/** 删除限时折扣活动 */ +export function deleteDiscountActivity(id: number) { + return requestClient.delete(`/promotion/discount-activity/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/diy/page.ts b/apps/web-antd/src/api/mall/promotion/diy/page.ts new file mode 100644 index 000000000..02726dfd5 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/diy/page.ts @@ -0,0 +1,55 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallDiyPageApi { + /** 装修页面 */ + export interface DiyPage { + id?: number; // 页面编号 + templateId?: number; // 模板编号 + name: string; // 页面名称 + remark: string; // 备注 + previewPicUrls: string[]; // 预览图片地址数组 + property: string; // 页面属性 + } +} + +/** 查询装修页面列表 */ +export function getDiyPagePage(params: PageParam) { + return requestClient.get>( + '/promotion/diy-page/page', + { params }, + ); +} + +/** 查询装修页面详情 */ +export function getDiyPage(id: number) { + return requestClient.get( + `/promotion/diy-page/get?id=${id}`, + ); +} + +/** 新增装修页面 */ +export function createDiyPage(data: MallDiyPageApi.DiyPage) { + return requestClient.post('/promotion/diy-page/create', data); +} + +/** 修改装修页面 */ +export function updateDiyPage(data: MallDiyPageApi.DiyPage) { + return requestClient.put('/promotion/diy-page/update', data); +} + +/** 删除装修页面 */ +export function deleteDiyPage(id: number) { + return requestClient.delete(`/promotion/diy-page/delete?id=${id}`); +} + +/** 获得装修页面属性 */ +export function getDiyPageProperty(id: number) { + return requestClient.get(`/promotion/diy-page/get-property?id=${id}`); +} + +/** 更新装修页面属性 */ +export function updateDiyPageProperty(data: MallDiyPageApi.DiyPage) { + return requestClient.put('/promotion/diy-page/update-property', data); +} diff --git a/apps/web-antd/src/api/mall/promotion/diy/template.ts b/apps/web-antd/src/api/mall/promotion/diy/template.ts new file mode 100644 index 000000000..9b7596e85 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/diy/template.ts @@ -0,0 +1,72 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { MallDiyPageApi } from './page'; + +import { requestClient } from '#/api/request'; + +export namespace MallDiyTemplateApi { + /** 装修模板 */ + export interface DiyTemplate { + id?: number; // 模板编号 + name: string; // 模板名称 + used: boolean; // 是否使用 + usedTime?: Date; // 使用时间 + remark: string; // 备注 + previewPicUrls: string[]; // 预览图片地址数组 + property: string; // 模板属性 + } + + /** 装修模板属性(包含页面列表) */ + export interface DiyTemplateProperty extends DiyTemplate { + pages: MallDiyPageApi.DiyPage[]; // 页面列表 + } +} + +/** 查询装修模板列表 */ +export function getDiyTemplatePage(params: PageParam) { + return requestClient.get>( + '/promotion/diy-template/page', + { params }, + ); +} + +/** 查询装修模板详情 */ +export function getDiyTemplate(id: number) { + return requestClient.get( + `/promotion/diy-template/get?id=${id}`, + ); +} + +/** 新增装修模板 */ +export function createDiyTemplate(data: MallDiyTemplateApi.DiyTemplate) { + return requestClient.post('/promotion/diy-template/create', data); +} + +/** 修改装修模板 */ +export function updateDiyTemplate(data: MallDiyTemplateApi.DiyTemplate) { + return requestClient.put('/promotion/diy-template/update', data); +} + +/** 删除装修模板 */ +export function deleteDiyTemplate(id: number) { + return requestClient.delete(`/promotion/diy-template/delete?id=${id}`); +} + +/** 使用装修模板 */ +export function useDiyTemplate(id: number) { + return requestClient.put(`/promotion/diy-template/use?id=${id}`); +} + +/** 获得装修模板属性 */ +export function getDiyTemplateProperty(id: number) { + return requestClient.get( + `/promotion/diy-template/get-property?id=${id}`, + ); +} + +/** 更新装修模板属性 */ +export function updateDiyTemplateProperty( + data: MallDiyTemplateApi.DiyTemplate, +) { + return requestClient.put('/promotion/diy-template/update-property', data); +} diff --git a/apps/web-antd/src/api/mall/promotion/kefu/conversation/index.ts b/apps/web-antd/src/api/mall/promotion/kefu/conversation/index.ts new file mode 100644 index 000000000..2b647a065 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/kefu/conversation/index.ts @@ -0,0 +1,54 @@ +import { requestClient } from '#/api/request'; + +export namespace MallKefuConversationApi { + /** 客服会话 */ + export interface Conversation { + id: number; // 编号 + userId: number; // 会话所属用户 + userAvatar: string; // 会话所属用户头像 + userNickname: string; // 会话所属用户昵称 + lastMessageTime: Date; // 最后聊天时间 + lastMessageContent: string; // 最后聊天内容 + lastMessageContentType: number; // 最后发送的消息类型 + adminPinned: boolean; // 管理端置顶 + userDeleted: boolean; // 用户是否可见 + adminDeleted: boolean; // 管理员是否可见 + adminUnreadMessageCount: number; // 管理员未读消息数 + createTime?: string; // 创建时间 + } + + /** 会话置顶请求 */ + export interface ConversationPinnedUpdateReqVO { + id: number; // 会话编号 + pinned: boolean; // 是否置顶 + } +} + +/** 获得客服会话列表 */ +export function getConversationList() { + return requestClient.get( + '/promotion/kefu-conversation/list', + ); +} + +/** 获得客服会话 */ +export function getConversation(id: number) { + return requestClient.get( + `/promotion/kefu-conversation/get?id=${id}`, + ); +} + +/** 客服会话置顶 */ +export function updateConversationPinned( + data: MallKefuConversationApi.ConversationPinnedUpdateReqVO, +) { + return requestClient.put( + '/promotion/kefu-conversation/update-conversation-pinned', + data, + ); +} + +/** 删除客服会话 */ +export function deleteConversation(id: number) { + return requestClient.delete(`/promotion/kefu-conversation/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/kefu/message/index.ts b/apps/web-antd/src/api/mall/promotion/kefu/message/index.ts new file mode 100644 index 000000000..f70c9f3ff --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/kefu/message/index.ts @@ -0,0 +1,47 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallKefuMessageApi { + /** 客服消息 */ + export interface Message { + id: number; // 编号 + conversationId: number; // 会话编号 + senderId: number; // 发送人编号 + senderAvatar: string; // 发送人头像 + senderType: number; // 发送人类型 + receiverId: number; // 接收人编号 + receiverType: number; // 接收人类型 + contentType: number; // 消息类型 + content: string; // 消息内容 + readStatus: boolean; // 是否已读 + createTime: Date; // 创建时间 + } + + /** 发送消息请求 */ + export interface MessageSend { + conversationId: number; // 会话编号 + contentType: number; // 消息类型 + content: string; // 消息内容 + } +} + +/** 发送客服消息 */ +export function sendKeFuMessage(data: MallKefuMessageApi.MessageSend) { + return requestClient.post('/promotion/kefu-message/send', data); +} + +/** 更新客服消息已读状态 */ +export function updateKeFuMessageReadStatus(conversationId: number) { + return requestClient.put( + `/promotion/kefu-message/update-read-status?conversationId=${conversationId}`, + ); +} + +/** 获得消息列表(流式加载) */ +export function getKeFuMessageList(params: PageParam) { + return requestClient.get>( + '/promotion/kefu-message/list', + { params }, + ); +} diff --git a/apps/web-antd/src/api/mall/promotion/point/index.ts b/apps/web-antd/src/api/mall/promotion/point/index.ts new file mode 100644 index 000000000..3cf24a4b2 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/point/index.ts @@ -0,0 +1,88 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { MallSpuApi } from '#/api/mall/product/spu'; + +import { requestClient } from '#/api/request'; + +export namespace MallPointActivityApi { + /** 积分商城商品 */ + export interface PointProduct { + id?: number; // 积分商城商品编号 + activityId?: number; // 积分商城活动 id + spuId?: number; // 商品 SPU 编号 + skuId: number; // 商品 SKU 编号 + count: number; // 可兑换数量 + point: number; // 兑换积分 + price: number; // 兑换金额,单位:分 + stock: number; // 积分商城商品库存 + activityStatus?: number; // 积分商城商品状态 + } + + /** 积分商城活动 */ + export interface PointActivity { + id: number; // 积分商城活动编号 + spuId: number; // 积分商城活动商品 + status: number; // 活动状态 + stock: number; // 积分商城活动库存 + totalStock: number; // 积分商城活动总库存 + remark?: string; // 备注 + sort: number; // 排序 + createTime: string; // 创建时间 + products: PointProduct[]; // 积分商城商品 + spuName: string; // 商品名称 + picUrl: string; // 商品主图 + marketPrice: number; // 商品市场价,单位:分 + point: number; // 兑换积分 + price: number; // 兑换金额,单位:分 + } + + /** 扩展 SPU 配置(带积分信息) */ + export interface SpuExtensionWithPoint extends MallSpuApi.Spu { + pointStock: number; // 积分商城活动库存 + pointTotalStock: number; // 积分商城活动总库存 + point: number; // 兑换积分 + pointPrice: number; // 兑换金额,单位:分 + } +} + +/** 查询积分商城活动分页 */ +export function getPointActivityPage(params: PageParam) { + return requestClient.get>( + '/promotion/point-activity/page', + { params }, + ); +} + +/** 查询积分商城活动详情 */ +export function getPointActivity(id: number) { + return requestClient.get( + `/promotion/point-activity/get?id=${id}`, + ); +} + +/** 查询积分商城活动列表,基于活动编号数组 */ +export function getPointActivityListByIds(ids: number[]) { + return requestClient.get( + `/promotion/point-activity/list-by-ids?ids=${ids}`, + ); +} + +/** 新增积分商城活动 */ +export function createPointActivity(data: MallPointActivityApi.PointActivity) { + return requestClient.post('/promotion/point-activity/create', data); +} + +/** 修改积分商城活动 */ +export function updatePointActivity(data: MallPointActivityApi.PointActivity) { + return requestClient.put('/promotion/point-activity/update', data); +} + +/** 删除积分商城活动 */ +export function deletePointActivity(id: number) { + return requestClient.delete(`/promotion/point-activity/delete?id=${id}`); +} + +/** 关闭积分商城活动 */ +export function closePointActivity(id: number) { + return requestClient.put(`/promotion/point-activity/close?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/reward/rewardActivity.ts b/apps/web-antd/src/api/mall/promotion/reward/rewardActivity.ts new file mode 100644 index 000000000..e59cad300 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/reward/rewardActivity.ts @@ -0,0 +1,72 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallRewardActivityApi { + /** 优惠规则 */ + export interface RewardRule { + limit?: number; // 满足金额 + discountPrice?: number; // 优惠金额 + freeDelivery?: boolean; // 是否包邮 + point: number; // 赠送积分 + giveCouponTemplateCounts?: { + [key: number]: number; + }; // 赠送优惠券数量 + } + + /** 满减送活动 */ + export interface RewardActivity { + id?: number; // 活动编号 + name?: string; // 活动名称 + status?: number; // 活动状态 + startTime?: Date; // 开始时间 + endTime?: Date; // 结束时间 + startAndEndTime?: Date[]; // 开始和结束时间(仅前端使用) + remark?: string; // 备注 + conditionType?: number; // 条件类型 + productScope?: number; // 商品范围 + rules: RewardRule[]; // 优惠规则列表 + productScopeValues?: number[]; // 商品范围值(仅表单使用):值为品类编号列表、商品编号列表 + productCategoryIds?: number[]; // 商品分类编号列表(仅表单使用) + productSpuIds?: number[]; // 商品 SPU 编号列表(仅表单使用) + } +} + +/** 新增满减送活动 */ +export function createRewardActivity( + data: MallRewardActivityApi.RewardActivity, +) { + return requestClient.post('/promotion/reward-activity/create', data); +} + +/** 更新满减送活动 */ +export function updateRewardActivity( + data: MallRewardActivityApi.RewardActivity, +) { + return requestClient.put('/promotion/reward-activity/update', data); +} + +/** 查询满减送活动列表 */ +export function getRewardActivityPage(params: PageParam) { + return requestClient.get>( + '/promotion/reward-activity/page', + { params }, + ); +} + +/** 查询满减送活动详情 */ +export function getReward(id: number) { + return requestClient.get( + `/promotion/reward-activity/get?id=${id}`, + ); +} + +/** 删除满减送活动 */ +export function deleteRewardActivity(id: number) { + return requestClient.delete(`/promotion/reward-activity/delete?id=${id}`); +} + +/** 关闭满减送活动 */ +export function closeRewardActivity(id: number) { + return requestClient.put(`/promotion/reward-activity/close?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/seckill/seckillActivity.ts b/apps/web-antd/src/api/mall/promotion/seckill/seckillActivity.ts new file mode 100644 index 000000000..82297effd --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/seckill/seckillActivity.ts @@ -0,0 +1,81 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallSeckillActivityApi { + /** 秒杀商品 */ + export interface SeckillProduct { + skuId: number; // 商品 SKU 编号 + spuId: number; // 商品 SPU 编号 + seckillPrice: number; // 秒杀价格 + stock: number; // 秒杀库存 + } + + /** 秒杀活动 */ + export interface SeckillActivity { + id?: number; // 活动编号 + spuId?: number; // 商品 SPU 编号 + name?: string; // 活动名称 + status?: number; // 活动状态 + remark?: string; // 备注 + startTime?: Date; // 开始时间 + endTime?: Date; // 结束时间 + sort?: number; // 排序 + configIds?: string; // 配置编号 + orderCount?: number; // 订单数量 + userCount?: number; // 用户数量 + totalPrice?: number; // 总金额 + totalLimitCount?: number; // 总限购数量 + singleLimitCount?: number; // 单次限购数量 + stock?: number; // 秒杀库存 + totalStock?: number; // 秒杀总库存 + seckillPrice?: number; // 秒杀价格 + products?: SeckillProduct[]; // 秒杀商品列表 + } +} + +/** 查询秒杀活动列表 */ +export function getSeckillActivityPage(params: PageParam) { + return requestClient.get>( + '/promotion/seckill-activity/page', + { params }, + ); +} + +/** 查询秒杀活动列表,基于活动编号数组 */ +export function getSeckillActivityListByIds(ids: number[]) { + return requestClient.get( + `/promotion/seckill-activity/list-by-ids?ids=${ids}`, + ); +} + +/** 查询秒杀活动详情 */ +export function getSeckillActivity(id: number) { + return requestClient.get( + `/promotion/seckill-activity/get?id=${id}`, + ); +} + +/** 新增秒杀活动 */ +export function createSeckillActivity( + data: MallSeckillActivityApi.SeckillActivity, +) { + return requestClient.post('/promotion/seckill-activity/create', data); +} + +/** 修改秒杀活动 */ +export function updateSeckillActivity( + data: MallSeckillActivityApi.SeckillActivity, +) { + return requestClient.put('/promotion/seckill-activity/update', data); +} + +/** 关闭秒杀活动 */ +export function closeSeckillActivity(id: number) { + return requestClient.put(`/promotion/seckill-activity/close?id=${id}`); +} + +/** 删除秒杀活动 */ +export function deleteSeckillActivity(id: number) { + return requestClient.delete(`/promotion/seckill-activity/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/promotion/seckill/seckillConfig.ts b/apps/web-antd/src/api/mall/promotion/seckill/seckillConfig.ts new file mode 100644 index 000000000..76eb92ba7 --- /dev/null +++ b/apps/web-antd/src/api/mall/promotion/seckill/seckillConfig.ts @@ -0,0 +1,60 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallSeckillConfigApi { + /** 秒杀时段 */ + export interface SeckillConfig { + id: number; // 编号 + name: string; // 秒杀时段名称 + startTime: string; // 开始时间点 + endTime: string; // 结束时间点 + sliderPicUrls: string[]; // 秒杀轮播图 + status: number; // 活动状态 + } +} + +/** 查询秒杀时段分页 */ +export function getSeckillConfigPage(params: PageParam) { + return requestClient.get>( + '/promotion/seckill-config/page', + { params }, + ); +} + +/** 查询秒杀时段列表 */ +export function getSimpleSeckillConfigList() { + return requestClient.get( + '/promotion/seckill-config/list', + ); +} + +/** 查询秒杀时段详情 */ +export function getSeckillConfig(id: number) { + return requestClient.get( + `/promotion/seckill-config/get?id=${id}`, + ); +} + +/** 新增秒杀时段 */ +export function createSeckillConfig(data: MallSeckillConfigApi.SeckillConfig) { + return requestClient.post('/promotion/seckill-config/create', data); +} + +/** 修改秒杀时段 */ +export function updateSeckillConfig(data: MallSeckillConfigApi.SeckillConfig) { + return requestClient.put('/promotion/seckill-config/update', data); +} + +/** 删除秒杀时段 */ +export function deleteSeckillConfig(id: number) { + return requestClient.delete(`/promotion/seckill-config/delete?id=${id}`); +} + +/** 修改时段配置状态 */ +export function updateSeckillConfigStatus(id: number, status: number) { + return requestClient.put('/promotion/seckill-config/update-status', { + id, + status, + }); +} diff --git a/apps/web-antd/src/api/mall/statistics/common.ts b/apps/web-antd/src/api/mall/statistics/common.ts new file mode 100644 index 000000000..3b1fad6d7 --- /dev/null +++ b/apps/web-antd/src/api/mall/statistics/common.ts @@ -0,0 +1,5 @@ +/** 数据对照 Response */ +export interface DataComparisonRespVO { + value: T; + reference: T; +} diff --git a/apps/web-antd/src/api/mall/statistics/member.ts b/apps/web-antd/src/api/mall/statistics/member.ts new file mode 100644 index 000000000..e6bf9ffa7 --- /dev/null +++ b/apps/web-antd/src/api/mall/statistics/member.ts @@ -0,0 +1,136 @@ +import type { Dayjs } from 'dayjs'; + +import type { DataComparisonRespVO } from './common'; + +import { formatDateTime } from '@vben/utils'; + +import { requestClient } from '#/api/request'; + +export namespace MallMemberStatisticsApi { + /** 会员分析 Request */ + export interface MemberAnalyseReqVO { + times: Date[] | Dayjs[]; // 时间范围 + } + + /** 会员分析 Response */ + export interface AnalyseRespVO { + visitUserCount: number; // 访问用户数 + orderUserCount: number; // 下单用户数 + payUserCount: number; // 支付用户数 + atv: number; // 平均客单价 + comparison: DataComparisonRespVO; // 对照数据 + } + + /** 会员分析对照数据 Response */ + export interface AnalyseDataRespVO { + registerUserCount: number; // 注册用户数 + visitUserCount: number; // 访问用户数 + rechargeUserCount: number; // 充值用户数 + } + + /** 会员地区统计 Response */ + export interface AreaStatisticsRespVO { + areaId: number; // 地区ID + areaName: string; // 地区名称 + userCount: number; // 用户数 + orderCreateUserCount: number; // 下单用户数 + orderPayUserCount: number; // 支付用户数 + orderPayPrice: number; // 支付金额 + } + + /** 会员性别统计 Response */ + export interface SexStatisticsRespVO { + sex: number; // 性别 + userCount: number; // 用户数 + } + + /** 会员统计 Response */ + export interface SummaryRespVO { + userCount: number; // 用户数 + rechargeUserCount: number; // 充值用户数 + rechargePrice: number; // 充值金额 + expensePrice: number; // 消费金额 + } + + /** 会员终端统计 Response */ + export interface TerminalStatisticsRespVO { + terminal: number; // 终端 + userCount: number; // 用户数 + } + + /** 会员数量统计 Response */ + export interface MemberCountRespVO { + visitUserCount: string; // 用户访问量 + registerUserCount: number; // 注册用户数量 + } + + /** 会员注册数量 Response */ + export interface RegisterCountRespVO { + date: string; // 日期 + count: number; // 数量 + } +} + +/** 查询会员统计 */ +export function getMemberSummary() { + return requestClient.get( + '/statistics/member/summary', + ); +} + +/** 查询会员分析数据 */ +export function getMemberAnalyse( + params: MallMemberStatisticsApi.MemberAnalyseReqVO, +) { + return requestClient.get( + '/statistics/member/analyse', + { + params: { + times: [ + formatDateTime(params.times[0]), + formatDateTime(params.times[1]), + ], + }, + }, + ); +} + +/** 按照省份,查询会员统计列表 */ +export function getMemberAreaStatisticsList() { + return requestClient.get( + '/statistics/member/area-statistics-list', + ); +} + +/** 按照性别,查询会员统计列表 */ +export function getMemberSexStatisticsList() { + return requestClient.get( + '/statistics/member/sex-statistics-list', + ); +} + +/** 按照终端,查询会员统计列表 */ +export function getMemberTerminalStatisticsList() { + return requestClient.get( + '/statistics/member/terminal-statistics-list', + ); +} + +/** 获得用户数量量对照 */ +export function getUserCountComparison() { + return requestClient.get< + DataComparisonRespVO + >('/statistics/member/user-count-comparison'); +} + +/** 获得会员注册数量列表 */ +export function getMemberRegisterCountList(beginTime: Date, endTime: Date) { + return requestClient.get( + '/statistics/member/register-count-list', + { + params: { + times: [formatDateTime(beginTime), formatDateTime(endTime)], + }, + }, + ); +} diff --git a/apps/web-antd/src/api/mall/statistics/product.ts b/apps/web-antd/src/api/mall/statistics/product.ts new file mode 100644 index 000000000..a3746cc2b --- /dev/null +++ b/apps/web-antd/src/api/mall/statistics/product.ts @@ -0,0 +1,53 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { DataComparisonRespVO } from './common'; + +import { requestClient } from '#/api/request'; + +export namespace MallProductStatisticsApi { + /** 商品统计数据 */ + export interface ProductStatisticsRespVO { + id: number; // 编号 + day: string; // 统计日期 + spuId: number; // 商品 SPU 编号 + spuName: string; // 商品 SPU 名称 + spuPicUrl: string; // 商品 SPU 图片 + browseCount: number; // 浏览次数 + browseUserCount: number; // 浏览人数 + favoriteCount: number; // 收藏次数 + cartCount: number; // 加购次数 + orderCount: number; // 下单次数 + orderPayCount: number; // 支付次数 + orderPayPrice: number; // 支付金额 + afterSaleCount: number; // 售后次数 + afterSaleRefundPrice: number; // 退款金额 + browseConvertPercent: number; // 浏览转化率 + } +} + +/** 获得商品统计分析 */ +export function getProductStatisticsAnalyse(params: any) { + return requestClient.get< + DataComparisonRespVO + >('/statistics/product/analyse', { params }); +} + +/** 获得商品状况明细 */ +export function getProductStatisticsList(params: any) { + return requestClient.get( + '/statistics/product/list', + { params }, + ); +} + +/** 导出获得商品状况明细 Excel */ +export function exportProductStatisticsExcel(params: any) { + return requestClient.download('/statistics/product/export-excel', { params }); +} + +/** 获得商品排行榜分页 */ +export function getProductStatisticsRankPage(params: PageParam) { + return requestClient.get< + PageResult + >('/statistics/product/rank-page', { params }); +} diff --git a/apps/web-antd/src/api/mall/statistics/trade.ts b/apps/web-antd/src/api/mall/statistics/trade.ts new file mode 100644 index 000000000..05dc7f3ef --- /dev/null +++ b/apps/web-antd/src/api/mall/statistics/trade.ts @@ -0,0 +1,113 @@ +import type { DataComparisonRespVO } from './common'; + +import { formatDateTime } from '@vben/utils'; + +import { requestClient } from '#/api/request'; + +export namespace MallTradeStatisticsApi { + /** 交易状况 Request */ + export interface TradeTrendReqVO { + times: [Date, Date]; + } + + /** 交易统计 Response */ + export interface TradeSummaryRespVO { + yesterdayOrderCount: number; + monthOrderCount: number; + yesterdayPayPrice: number; + monthPayPrice: number; + } + + /** 交易状况统计 Response */ + export interface TradeTrendSummaryRespVO { + time: string; + turnoverPrice: number; + orderPayPrice: number; + rechargePrice: number; + expensePrice: number; + walletPayPrice: number; + brokerageSettlementPrice: number; + afterSaleRefundPrice: number; + } + + /** 交易订单数量 Response */ + export interface TradeOrderCountRespVO { + undelivered?: number; // 待发货 + pickUp?: number; // 待核销 + afterSaleApply?: number; // 退款中 + auditingWithdraw?: number; // 提现待审核 + } + + /** 交易订单统计 Response */ + export interface TradeOrderSummaryRespVO { + orderPayCount?: number; // 支付订单商品数 + orderPayPrice?: number; // 总支付金额,单位:分 + } + + /** 订单量趋势统计 Response */ + export interface TradeOrderTrendRespVO { + date: string; // 日期 + orderPayCount: number; // 订单数量 + orderPayPrice: number; // 订单支付金额 + } +} + +/** 查询交易统计 */ +export function getTradeStatisticsSummary() { + return requestClient.get< + DataComparisonRespVO + >('/statistics/trade/summary'); +} + +/** 获得交易状况统计 */ +export function getTradeStatisticsAnalyse( + params: MallTradeStatisticsApi.TradeTrendReqVO, +) { + return requestClient.get< + DataComparisonRespVO + >('/statistics/trade/analyse', { params }); +} + +/** 获得交易状况明细 */ +export function getTradeStatisticsList(params: any) { + return requestClient.get( + '/statistics/trade/list', + { params }, + ); +} + +/** 导出交易状况明细 */ +export function exportTradeStatisticsExcel(params: any) { + return requestClient.download('/statistics/trade/export-excel', { params }); +} + +/** 获得交易订单数量 */ +export function getOrderCount() { + return requestClient.get( + '/statistics/trade/order-count', + ); +} + +/** 获得交易订单数量对照 */ +export function getOrderComparison() { + return requestClient.get< + DataComparisonRespVO + >('/statistics/trade/order-comparison'); +} + +/** 获得订单量趋势统计 */ +export function getOrderCountTrendComparison( + type: number, + beginTime: Date, + endTime: Date, +) { + return requestClient.get< + DataComparisonRespVO[] + >('/statistics/trade/order-count-trend', { + params: { + type, + beginTime: formatDateTime(beginTime), + endTime: formatDateTime(endTime), + }, + }); +} diff --git a/apps/web-antd/src/api/mall/trade/afterSale/index.ts b/apps/web-antd/src/api/mall/trade/afterSale/index.ts new file mode 100644 index 000000000..97bafedaf --- /dev/null +++ b/apps/web-antd/src/api/mall/trade/afterSale/index.ts @@ -0,0 +1,98 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import type { MallOrderApi } from '#/api/mall/trade/order'; + +import { requestClient } from '#/api/request'; + +export namespace MallAfterSaleApi { + /** 交易售后 */ + export interface AfterSale { + id?: number; // 售后编号 + no?: string; // 售后单号 + status?: number; // 退款状态 + way?: number; // 售后方式 + type?: number; // 售后类型 + userId?: number; // 用户编号 + applyReason?: string; // 申请原因 + applyDescription?: string; // 补充描述 + applyPicUrls?: string[]; // 补充凭证图片 + orderId?: number; // 交易订单编号 + orderNo?: string; // 订单流水号 + orderItemId?: number; // 交易订单项编号 + spuId?: number; // 商品 SPU 编号 + spuName?: string; // 商品 SPU 名称 + skuId?: number; // 商品 SKU 编号 + properties?: { + propertyId?: number; // 属性的编号 + propertyName?: string; // 属性的名称 + valueId?: number; // 属性值的编号 + valueName?: string; // 属性值的名称 + }[]; // 属性数组 + picUrl?: string; // 商品图片 + count?: number; // 退货商品数量 + auditTime?: Date; // 审批时间 + auditUserId?: number; // 审批人 + auditReason?: string; // 审批备注 + refundPrice?: number; // 退款金额,单位:分 + payRefundId?: number; // 支付退款编号 + refundTime?: Date; // 退款时间 + logisticsId?: number; // 退货物流公司编号 + logisticsNo?: string; // 退货物流单号 + deliveryTime?: Date; // 退货时间 + receiveTime?: Date; // 收货时间 + receiveReason?: string; // 收货备注 + order?: MallOrderApi.Order; // 关联订单 + orderItem?: MallOrderApi.OrderItem; // 关联订单项 + logs?: any[]; // 关联售后日志 + } + + /** 拒绝售后请求 */ + export interface AfterSaleDisagreeReqVO { + /** 售后编号 */ + id: number; + /** 拒绝原因 */ + reason: string; + } +} + +/** 获得交易售后分页 */ +export function getAfterSalePage(params: PageParam) { + return requestClient.get>( + '/trade/after-sale/page', + { params }, + ); +} + +/** 获得交易售后详情 */ +export function getAfterSale(id: number) { + return requestClient.get( + `/trade/after-sale/get-detail?id=${id}`, + ); +} + +/** 同意售后 */ +export function agreeAfterSale(id: number) { + return requestClient.put(`/trade/after-sale/agree?id=${id}`); +} + +/** 拒绝售后 */ +export function disagreeAfterSale( + data: MallAfterSaleApi.AfterSaleDisagreeReqVO, +) { + return requestClient.put('/trade/after-sale/disagree', data); +} + +/** 确认收货 */ +export function receiveAfterSale(id: number) { + return requestClient.put(`/trade/after-sale/receive?id=${id}`); +} + +/** 拒绝收货 */ +export function refuseAfterSale(id: number) { + return requestClient.put(`/trade/after-sale/refuse?id=${id}`); +} + +/** 确认退款 */ +export function refundAfterSale(id: number) { + return requestClient.put(`/trade/after-sale/refund?id=${id}`); +} diff --git a/apps/web-antd/src/api/mall/trade/brokerage/record/index.ts b/apps/web-antd/src/api/mall/trade/brokerage/record/index.ts new file mode 100644 index 000000000..1b6e8b3a4 --- /dev/null +++ b/apps/web-antd/src/api/mall/trade/brokerage/record/index.ts @@ -0,0 +1,35 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallBrokerageRecordApi { + /** 佣金记录 */ + export interface BrokerageRecord { + id: number; // 编号 + userId: number; // 用户编号 + userNickname: string; // 用户昵称 + userAvatar: string; // 用户头像 + price: number; // 佣金金额,单位:分 + type: number; // 佣金类型 + orderId: number; // 关联订单编号 + orderNo: string; // 关联订单号 + createTime: Date; // 创建时间 + status: number; // 状态 + settlementTime: Date; // 结算时间 + } +} + +/** 查询佣金记录列表 */ +export function getBrokerageRecordPage(params: PageParam) { + return requestClient.get>( + '/trade/brokerage-record/page', + { params }, + ); +} + +/** 查询佣金记录详情 */ +export function getBrokerageRecord(id: number) { + return requestClient.get( + `/trade/brokerage-record/get?id=${id}`, + ); +} diff --git a/apps/web-antd/src/api/mall/trade/brokerage/user/index.ts b/apps/web-antd/src/api/mall/trade/brokerage/user/index.ts new file mode 100644 index 000000000..4ef235af8 --- /dev/null +++ b/apps/web-antd/src/api/mall/trade/brokerage/user/index.ts @@ -0,0 +1,87 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallBrokerageUserApi { + /** 分销用户 */ + export interface BrokerageUser { + id: number; // 编号 + bindUserId: number; // 推广员编号 + bindUserTime: Date; // 推广员绑定时间 + brokerageEnabled: boolean; // 是否启用分销 + brokerageTime: Date; // 分销资格时间 + price: number; // 可提现金额,单位:分 + frozenPrice: number; // 冻结金额,单位:分 + nickname: string; // 用户昵称 + avatar: string; // 用户头像 + } + + /** 创建分销用户请求 */ + export interface BrokerageUserCreateReqVO { + userId: number; // 用户编号 + bindUserId: number; // 推广员编号 + } + + /** 修改推广员请求 */ + export interface BrokerageUserUpdateReqVO { + id: number; // 用户编号 + bindUserId: number; // 推广员编号 + } + + /** 清除推广员请求 */ + export interface BrokerageUserClearBrokerageUserReqVO { + id: number; // 用户编号 + } + + /** 修改推广资格请求 */ + export interface BrokerageUserUpdateBrokerageEnabledReqVO { + id: number; // 用户编号 + enabled: boolean; // 是否启用分销 + } +} + +/** 创建分销用户 */ +export function createBrokerageUser( + data: MallBrokerageUserApi.BrokerageUserCreateReqVO, +) { + return requestClient.post('/trade/brokerage-user/create', data); +} + +/** 查询分销用户列表 */ +export function getBrokerageUserPage(params: PageParam) { + return requestClient.get>( + '/trade/brokerage-user/page', + { params }, + ); +} + +/** 查询分销用户详情 */ +export function getBrokerageUser(id: number) { + return requestClient.get( + `/trade/brokerage-user/get?id=${id}`, + ); +} + +/** 修改推广员 */ +export function updateBindUser( + data: MallBrokerageUserApi.BrokerageUserUpdateReqVO, +) { + return requestClient.put('/trade/brokerage-user/update-bind-user', data); +} + +/** 清除推广员 */ +export function clearBindUser( + data: MallBrokerageUserApi.BrokerageUserClearBrokerageUserReqVO, +) { + return requestClient.put('/trade/brokerage-user/clear-bind-user', data); +} + +/** 修改推广资格 */ +export function updateBrokerageEnabled( + data: MallBrokerageUserApi.BrokerageUserUpdateBrokerageEnabledReqVO, +) { + return requestClient.put( + '/trade/brokerage-user/update-brokerage-enable', + data, + ); +} diff --git a/apps/web-antd/src/api/mall/trade/brokerage/withdraw/index.ts b/apps/web-antd/src/api/mall/trade/brokerage/withdraw/index.ts new file mode 100644 index 000000000..0cf604bca --- /dev/null +++ b/apps/web-antd/src/api/mall/trade/brokerage/withdraw/index.ts @@ -0,0 +1,53 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallBrokerageWithdrawApi { + /** 佣金提现 */ + export interface BrokerageWithdraw { + id: number; // 编号 + userId: number; // 用户编号 + price: number; // 提现金额,单位:分 + feePrice: number; // 手续费,单位:分 + totalPrice: number; // 总金额,单位:分 + type: number; // 提现类型 + userName: string; // 用户名称 + userAccount: string; // 用户账号 + bankName: string; // 银行名称 + bankAddress: string; // 银行地址 + qrCodeUrl: string; // 收款码地址 + status: number; // 状态 + auditReason: string; // 审核备注 + auditTime: Date; // 审核时间 + remark: string; // 备注 + payTransferId?: number; // 支付转账编号 + transferChannelCode?: string; // 转账渠道编码 + transferTime?: Date; // 转账时间 + transferErrorMsg?: string; // 转账错误信息 + } + + /** 驳回申请请求 */ + export interface BrokerageWithdrawRejectReqVO { + id: number; // 编号 + auditReason: string; // 驳回原因 + } +} + +/** 查询佣金提现列表 */ +export function getBrokerageWithdrawPage(params: PageParam) { + return requestClient.get< + PageResult + >('/trade/brokerage-withdraw/page', { params }); +} + +/** 佣金提现 - 通过申请 */ +export function approveBrokerageWithdraw(id: number) { + return requestClient.put(`/trade/brokerage-withdraw/approve?id=${id}`); +} + +/** 审核佣金提现 - 驳回申请 */ +export function rejectBrokerageWithdraw( + data: MallBrokerageWithdrawApi.BrokerageWithdrawRejectReqVO, +) { + return requestClient.put('/trade/brokerage-withdraw/reject', data); +} diff --git a/apps/web-antd/src/api/mall/trade/delivery/express/index.ts b/apps/web-antd/src/api/mall/trade/delivery/express/index.ts new file mode 100644 index 000000000..af3911c1e --- /dev/null +++ b/apps/web-antd/src/api/mall/trade/delivery/express/index.ts @@ -0,0 +1,63 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallDeliveryExpressApi { + /** 快递公司 */ + export interface DeliveryExpress { + id: number; // 编号 + code: string; // 快递公司编码 + name: string; // 快递公司名称 + logo: string; // 快递公司 logo + sort: number; // 排序 + status: number; // 状态 + } +} + +/** 查询快递公司列表 */ +export function getDeliveryExpressPage(params: PageParam) { + return requestClient.get>( + '/trade/delivery/express/page', + { params }, + ); +} + +/** 查询快递公司详情 */ +export function getDeliveryExpress(id: number) { + return requestClient.get( + `/trade/delivery/express/get?id=${id}`, + ); +} + +/** 获得快递公司精简信息列表 */ +export function getSimpleDeliveryExpressList() { + return requestClient.get( + '/trade/delivery/express/list-all-simple', + ); +} + +/** 新增快递公司 */ +export function createDeliveryExpress( + data: MallDeliveryExpressApi.DeliveryExpress, +) { + return requestClient.post('/trade/delivery/express/create', data); +} + +/** 修改快递公司 */ +export function updateDeliveryExpress( + data: MallDeliveryExpressApi.DeliveryExpress, +) { + return requestClient.put('/trade/delivery/express/update', data); +} + +/** 删除快递公司 */ +export function deleteDeliveryExpress(id: number) { + return requestClient.delete(`/trade/delivery/express/delete?id=${id}`); +} + +/** 导出快递公司 Excel */ +export function exportDeliveryExpress(params: PageParam) { + return requestClient.download('/trade/delivery/express/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/mall/trade/delivery/expressTemplate/index.ts b/apps/web-antd/src/api/mall/trade/delivery/expressTemplate/index.ts new file mode 100644 index 000000000..92c7ad0e1 --- /dev/null +++ b/apps/web-antd/src/api/mall/trade/delivery/expressTemplate/index.ts @@ -0,0 +1,73 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallDeliveryExpressTemplateApi { + /** 快递运费模板 */ + export interface DeliveryExpressTemplate { + id: number; // 编号 + name: string; // 模板名称 + chargeMode: number; // 计费方式 + sort: number; // 排序 + charges: DeliveryExpressTemplateCharge[]; // 计费区域列表 + frees: DeliveryExpressTemplateFree[]; // 包邮区域列表 + } + + /** 运费模板计费 */ + export interface DeliveryExpressTemplateCharge { + areaIds: number[]; // 区域编号列表 + startCount: number; // 首件数量 + startPrice: number; // 首件价格,单位:分 + extraCount: number; // 续件数量 + extraPrice: number; // 续件价格,单位:分 + } + + /** 运费模板包邮 */ + export interface DeliveryExpressTemplateFree { + areaIds: number[]; // 区域编号列表 + freeCount: number; // 包邮件数 + freePrice: number; // 包邮金额,单位:分 + } +} + +/** 查询快递运费模板列表 */ +export function getDeliveryExpressTemplatePage(params: PageParam) { + return requestClient.get< + PageResult + >('/trade/delivery/express-template/page', { params }); +} + +/** 查询快递运费模板详情 */ +export function getDeliveryExpressTemplate(id: number) { + return requestClient.get( + `/trade/delivery/express-template/get?id=${id}`, + ); +} + +/** 查询快递运费模板详情 */ +export function getSimpleTemplateList() { + return requestClient.get< + MallDeliveryExpressTemplateApi.DeliveryExpressTemplate[] + >('/trade/delivery/express-template/list-all-simple'); +} + +/** 新增快递运费模板 */ +export function createDeliveryExpressTemplate( + data: MallDeliveryExpressTemplateApi.DeliveryExpressTemplate, +) { + return requestClient.post('/trade/delivery/express-template/create', data); +} + +/** 修改快递运费模板 */ +export function updateDeliveryExpressTemplate( + data: MallDeliveryExpressTemplateApi.DeliveryExpressTemplate, +) { + return requestClient.put('/trade/delivery/express-template/update', data); +} + +/** 删除快递运费模板 */ +export function deleteDeliveryExpressTemplate(id: number) { + return requestClient.delete( + `/trade/delivery/express-template/delete?id=${id}`, + ); +} diff --git a/apps/web-antd/src/api/mall/trade/delivery/pickUpStore/index.ts b/apps/web-antd/src/api/mall/trade/delivery/pickUpStore/index.ts new file mode 100644 index 000000000..6844f0039 --- /dev/null +++ b/apps/web-antd/src/api/mall/trade/delivery/pickUpStore/index.ts @@ -0,0 +1,78 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallDeliveryPickUpStoreApi { + /** 自提门店 */ + export interface DeliveryPickUpStore { + id: number; // 编号 + name: string; // 门店名称 + introduction: string; // 门店简介 + phone: string; // 联系电话 + areaId: number; // 区域编号 + detailAddress: string; // 详细地址 + logo: string; // 门店 logo + openingTime: string; // 营业开始时间 + closingTime: string; // 营业结束时间 + latitude: number; // 纬度 + longitude: number; // 经度 + status: number; // 状态 + rangeTime: any[]; // 营业时间,用于 fieldMappingTime + verifyUserIds?: number[]; // 绑定用户编号组数 + verifyUsers?: any[]; + } + + /** 绑定自提店员请求 */ + export interface DeliveryPickUpBindReqVO { + id?: number; + /** 用户编号列表 */ + verifyUserIds: number[]; + } +} + +/** 查询自提门店列表 */ +export function getDeliveryPickUpStorePage(params: PageParam) { + return requestClient.get< + PageResult + >('/trade/delivery/pick-up-store/page', { params }); +} + +/** 查询自提门店详情 */ +export function getDeliveryPickUpStore(id: number) { + return requestClient.get( + `/trade/delivery/pick-up-store/get?id=${id}`, + ); +} + +/** 查询自提门店精简列表 */ +export function getSimpleDeliveryPickUpStoreList() { + return requestClient.get( + '/trade/delivery/pick-up-store/simple-list', + ); +} + +/** 新增自提门店 */ +export function createDeliveryPickUpStore( + data: MallDeliveryPickUpStoreApi.DeliveryPickUpStore, +) { + return requestClient.post('/trade/delivery/pick-up-store/create', data); +} + +/** 修改自提门店 */ +export function updateDeliveryPickUpStore( + data: MallDeliveryPickUpStoreApi.DeliveryPickUpStore, +) { + return requestClient.put('/trade/delivery/pick-up-store/update', data); +} + +/** 删除自提门店 */ +export function deleteDeliveryPickUpStore(id: number) { + return requestClient.delete(`/trade/delivery/pick-up-store/delete?id=${id}`); +} + +/** 绑定自提店员 */ +export function bindDeliveryPickUpStore( + data: MallDeliveryPickUpStoreApi.DeliveryPickUpBindReqVO, +) { + return requestClient.post('/trade/delivery/pick-up-store/bind', data); +} diff --git a/apps/web-antd/src/api/mall/trade/order/index.ts b/apps/web-antd/src/api/mall/trade/order/index.ts new file mode 100644 index 000000000..cea0d4672 --- /dev/null +++ b/apps/web-antd/src/api/mall/trade/order/index.ts @@ -0,0 +1,209 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MallOrderApi { + /** 订单信息 */ + export interface Order { + id?: number; // 订单编号 + no?: string; // 订单流水号 + createTime?: Date; // 下单时间 + type?: number; // 订单类型 + terminal?: number; // 订单来源 + userId?: number; // 用户编号 + userIp?: string; // 用户 IP + userRemark?: string; // 用户备注 + status?: number; // 订单状态 + productCount?: number; // 购买的商品数量 + finishTime?: Date; // 订单完成时间 + cancelTime?: Date; // 订单取消时间 + cancelType?: number; // 取消类型 + remark?: string; // 商家备注 + payOrderId?: number; // 支付订单编号 + payStatus?: boolean; // 是否已支付 + payTime?: Date; // 付款时间 + payChannelCode?: string; // 支付渠道 + totalPrice?: number; // 商品原价(总) + discountPrice?: number; // 订单优惠(总) + deliveryPrice?: number; // 运费金额 + adjustPrice?: number; // 订单调价(总) + payPrice?: number; // 应付金额(总) + deliveryType?: number; // 发货方式 + pickUpStoreId?: number; // 自提门店编号 + pickUpVerifyCode?: string; // 自提核销码 + deliveryTemplateId?: number; // 配送模板编号 + logisticsId?: number; // 发货物流公司编号 + logisticsNo?: string; // 发货物流单号 + deliveryTime?: Date; // 发货时间 + receiveTime?: Date; // 收货时间 + receiverName?: string; // 收件人名称 + receiverMobile?: string; // 收件人手机 + receiverPostCode?: number; // 收件人邮编 + receiverAreaId?: number; // 收件人地区编号 + receiverAreaName?: string; // 收件人地区名字 + receiverDetailAddress?: string; // 收件人详细地址 + afterSaleStatus?: number; // 售后状态 + refundPrice?: number; // 退款金额 + couponId?: number; // 优惠劵编号 + couponPrice?: number; // 优惠劵减免金额 + pointPrice?: number; // 积分抵扣的金额 + vipPrice?: number; // VIP 减免金额 + items?: OrderItem[]; // 订单项列表 + user?: { + // 下单用户信息 + avatar?: string; // 用户头像 + id?: number; // 用户编号 + nickname?: string; // 用户昵称 + }; + brokerageUser?: { + // 推广用户信息 + avatar?: string; // 用户头像 + id?: number; // 用户编号 + nickname?: string; // 用户昵称 + }; // 推广用户信息 + logs?: OrderLog[]; // 订单操作日志 + } + + /** 订单项 */ + export interface OrderItem { + id?: number; // 编号 + userId?: number; // 用户编号 + orderId?: number; // 订单编号 + spuId?: number; // 商品 SPU 编号 + spuName?: string; // 商品 SPU 名称 + skuId?: number; // 商品 SKU 编号 + picUrl?: string; // 商品图片 + count?: number; // 购买数量 + originalPrice?: number; // 商品原价(总) + originalUnitPrice?: number; // 商品原价(单) + discountPrice?: number; // 商品优惠(总) + payPrice?: number; // 商品实付金额(总) + orderPartPrice?: number; // 子订单分摊金额(总) + orderDividePrice?: number; // 分摊后子订单实付金额(总) + afterSaleStatus?: number; // 售后状态 + properties?: { + propertyId?: number; // 属性的编号 + propertyName?: string; // 属性的名称 + valueId?: number; // 属性值的编号 + valueName?: string; // 属性值的名称 + }[]; // 属性数组 + price?: number; + } + + /** 订单日志 */ + export interface OrderLog { + content?: string; // 日志内容 + createTime?: Date; // 创建时间 + userType?: number; // 用户类型 + userId?: number; // 用户编号 + } + + /** 交易订单统计响应 */ + export interface OrderSummaryRespVO { + orderCount: number; // 订单数量 + orderPayPrice: number; // 订单金额 + afterSaleCount: number; // 退款单数 + afterSalePrice: number; // 退款金额 + } + + /** 订单发货请求 */ + export interface OrderUpdateDeliveryReqVO { + id?: number; // 订单编号 + expressType: string; // 发货方式 + logisticsId: number; // 物流公司编号 + logisticsNo: string; // 物流编号 + } + + /** 订单备注请求 */ + export interface OrderUpdateRemarkReqVO { + id: number; // 订单编号 + remark: string; // 备注 + } + + /** 订单调价请求 */ + export interface OrderUpdatePriceReqVO { + id: number; // 订单编号 + adjustPrice: number; // 调整金额,单位:分 + } + + /** 订单地址请求 */ + export interface OrderUpdateAddressReqVO { + id: number; // 订单编号 + receiverName: string; // 收件人名称 + receiverMobile: string; // 收件人手机 + receiverAreaId: number; // 收件人地区编号 + receiverDetailAddress: string; // 收件人详细地址 + } +} + +/** 查询交易订单列表 */ +export function getOrderPage(params: PageParam) { + return requestClient.get>( + '/trade/order/page', + { + params, + }, + ); +} + +/** 查询交易订单统计 */ +export function getOrderSummary(params: any) { + return requestClient.get( + '/trade/order/summary', + { + params, + }, + ); +} + +/** 查询交易订单详情 */ +export function getOrder(id: number) { + return requestClient.get( + `/trade/order/get-detail?id=${id}`, + ); +} + +/** 查询交易订单物流详情 */ +export function getExpressTrackList(id: number) { + return requestClient.get(`/trade/order/get-express-track-list?id=${id}`); +} + +/** 订单发货 */ +export function deliveryOrder(data: MallOrderApi.OrderUpdateDeliveryReqVO) { + return requestClient.put('/trade/order/delivery', data); +} + +/** 订单备注 */ +export function updateOrderRemark(data: MallOrderApi.OrderUpdateRemarkReqVO) { + return requestClient.put('/trade/order/update-remark', data); +} + +/** 订单调价 */ +export function updateOrderPrice(data: MallOrderApi.OrderUpdatePriceReqVO) { + return requestClient.put('/trade/order/update-price', data); +} + +/** 修改订单地址 */ +export function updateOrderAddress(data: MallOrderApi.OrderUpdateAddressReqVO) { + return requestClient.put('/trade/order/update-address', data); +} + +/** 订单核销 */ +export function pickUpOrder(id: number) { + return requestClient.put(`/trade/order/pick-up-by-id?id=${id}`); +} + +/** 订单核销 */ +export function pickUpOrderByVerifyCode(pickUpVerifyCode: string) { + return requestClient.put('/trade/order/pick-up-by-verify-code', undefined, { + params: { pickUpVerifyCode }, + }); +} + +/** 查询核销码对应的订单 */ +export function getOrderByPickUpVerifyCode(pickUpVerifyCode: string) { + return requestClient.get( + '/trade/order/get-by-pick-up-verify-code', + { params: { pickUpVerifyCode } }, + ); +} diff --git a/apps/web-antd/src/api/member/level/index.ts b/apps/web-antd/src/api/member/level/index.ts new file mode 100644 index 000000000..a856e505b --- /dev/null +++ b/apps/web-antd/src/api/member/level/index.ts @@ -0,0 +1,50 @@ +import { requestClient } from '#/api/request'; + +export namespace MemberLevelApi { + /** 会员等级信息 */ + export interface Level { + id?: number; + name: string; + experience: number; + value: number; + discountPercent: number; + icon: string; + bgUrl: string; + status: number; + createTime?: Date; + } +} + +/** 查询会员等级列表 */ +export function getLevelList(params: MemberLevelApi.Level) { + return requestClient.get('/member/level/list', { + params, + }); +} + +/** 查询会员等级详情 */ +export function getLevel(id: number) { + return requestClient.get(`/member/level/get?id=${id}`); +} + +/** 查询会员等级 - 精简信息列表 */ +export function getSimpleLevelList() { + return requestClient.get( + '/member/level/list-all-simple', + ); +} + +/** 新增会员等级 */ +export function createLevel(data: MemberLevelApi.Level) { + return requestClient.post('/member/level/create', data); +} + +/** 修改会员等级 */ +export function updateLevel(data: MemberLevelApi.Level) { + return requestClient.put('/member/level/update', data); +} + +/** 删除会员等级 */ +export function deleteLevel(id: number) { + return requestClient.delete(`/member/level/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/member/user/index.ts b/apps/web-antd/src/api/member/user/index.ts new file mode 100644 index 000000000..e02f332ed --- /dev/null +++ b/apps/web-antd/src/api/member/user/index.ts @@ -0,0 +1,70 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MemberUserApi { + /** 会员用户信息 */ + export interface User { + id?: number; + avatar?: string; + birthday?: number; + createTime?: number; + loginDate?: number; + loginIp: string; + mark: string; + mobile: string; + name?: string; + nickname?: string; + registerIp: string; + sex: number; + status: number; + areaId?: number; + areaName?: string; + levelName: string; + point?: number; + totalPoint?: number; + experience?: number; + } + + /** 会员用户等级更新信息 */ + export interface UserUpdateLevelReqVO { + id: number; + levelId: number; + } + + /** 会员用户积分更新信息 */ + export interface UserPointUpdateReqVO { + id: number; + point: number; + } +} + +/** 查询会员用户列表 */ +export function getUserPage(params: PageParam) { + return requestClient.get>( + '/member/user/page', + { + params, + }, + ); +} + +/** 查询会员用户详情 */ +export function getUser(id: number) { + return requestClient.get(`/member/user/get?id=${id}`); +} + +/** 修改会员用户 */ +export function updateUser(data: MemberUserApi.User) { + return requestClient.put('/member/user/update', data); +} + +/** 修改会员用户等级 */ +export function updateUserLevel(data: MemberUserApi.UserUpdateLevelReqVO) { + return requestClient.put('/member/user/update-level', data); +} + +/** 修改会员用户积分 */ +export function updateUserPoint(data: MemberUserApi.UserPointUpdateReqVO) { + return requestClient.put('/member/user/update-point', data); +} diff --git a/apps/web-antd/src/api/mp/account/index.ts b/apps/web-antd/src/api/mp/account/index.ts new file mode 100644 index 000000000..aa8557f15 --- /dev/null +++ b/apps/web-antd/src/api/mp/account/index.ts @@ -0,0 +1,66 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MpAccountApi { + /** 公众号账号信息 */ + export interface Account { + id: number; + name: string; + account?: string; + appId?: string; + appSecret?: string; + token?: string; + aesKey?: string; + qrCodeUrl?: string; + remark?: string; + createTime?: Date; + } +} + +/** 查询公众号账号列表 */ +export function getAccountPage(params: PageParam) { + return requestClient.get>( + '/mp/account/page', + { + params, + }, + ); +} + +/** 查询公众号账号详情 */ +export function getAccount(id: number) { + return requestClient.get(`/mp/account/get?id=${id}`); +} + +/** 查询公众号账号列表 */ +export function getSimpleAccountList() { + return requestClient.get( + '/mp/account/list-all-simple', + ); +} + +/** 新增公众号账号 */ +export function createAccount(data: MpAccountApi.Account) { + return requestClient.post('/mp/account/create', data); +} + +/** 修改公众号账号 */ +export function updateAccount(data: MpAccountApi.Account) { + return requestClient.put('/mp/account/update', data); +} + +/** 删除公众号账号 */ +export function deleteAccount(id: number) { + return requestClient.delete(`/mp/account/delete?id=${id}`); +} + +/** 生成公众号账号二维码 */ +export function generateAccountQrCode(id: number) { + return requestClient.put(`/mp/account/generate-qr-code?id=${id}`); +} + +/** 清空公众号账号 API 配额 */ +export function clearAccountQuota(id: number) { + return requestClient.put(`/mp/account/clear-quota?id=${id}`); +} diff --git a/apps/web-antd/src/api/mp/draft/index.ts b/apps/web-antd/src/api/mp/draft/index.ts new file mode 100644 index 000000000..f05d0117a --- /dev/null +++ b/apps/web-antd/src/api/mp/draft/index.ts @@ -0,0 +1,106 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MpDraftApi { + /** 草稿文章信息 */ + export interface Article { + title: string; + author: string; + digest: string; + content: string; + contentSourceUrl: string; + thumbMediaId: string; + showCoverPic: number; + needOpenComment: number; + onlyFansCanComment: number; + } + + /** 草稿信息 */ + export interface Draft { + id?: number; + accountId: number; + mediaId: string; + articles: Article[]; + createTime?: Date; + } + + /** 图文项(包含预览字段) */ + export interface NewsItem { + title: string; + thumbMediaId: string; + author: string; + digest: string; + showCoverPic: number; + content: string; + contentSourceUrl: string; + needOpenComment: number; + onlyFansCanComment: number; + thumbUrl: string; + picUrl?: string; // 用于预览封面 + } + + /** 图文列表 */ + export interface NewsItemList { + newsItem: NewsItem[]; + } + + /** 草稿文章(用于展示) */ + export interface DraftArticle { + mediaId: string; + content: NewsItemList; + updateTime: number; + } +} + +/** 创建空的图文项 */ +export function createEmptyNewsItem(): MpDraftApi.NewsItem { + return { + title: '', + thumbMediaId: '', + author: '', + digest: '', + showCoverPic: 0, + content: '', + contentSourceUrl: '', + needOpenComment: 0, + onlyFansCanComment: 0, + thumbUrl: '', + }; +} + +/** 查询草稿列表 */ +export function getDraftPage(params: PageParam) { + return requestClient.get>('/mp/draft/page', { + params, + }); +} + +/** 创建草稿 */ +export function createDraft(accountId: number, articles: MpDraftApi.Article[]) { + return requestClient.post( + '/mp/draft/create', + { articles }, + { + params: { accountId }, + }, + ); +} + +/** 更新草稿 */ +export function updateDraft( + accountId: number, + mediaId: string, + articles: MpDraftApi.Article[], +) { + return requestClient.put('/mp/draft/update', articles, { + params: { accountId, mediaId }, + }); +} + +/** 删除草稿 */ +export function deleteDraft(accountId: number, mediaId: string) { + return requestClient.delete('/mp/draft/delete', { + params: { accountId, mediaId }, + }); +} diff --git a/apps/web-antd/src/api/mp/material/index.ts b/apps/web-antd/src/api/mp/material/index.ts new file mode 100644 index 000000000..91ca6ddf0 --- /dev/null +++ b/apps/web-antd/src/api/mp/material/index.ts @@ -0,0 +1,37 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { MaterialType } from '@vben/constants'; + +import { requestClient } from '#/api/request'; + +export namespace MpMaterialApi { + /** 素材信息 */ + export interface Material { + id?: number; + accountId: number; + type: MaterialType; + mediaId: string; + url: string; + name: string; + size: number; + remark?: string; + createTime?: Date; + } +} + +/** 查询素材列表 */ +export function getMaterialPage(params: PageParam) { + return requestClient.get>( + '/mp/material/page', + { + params, + }, + ); +} + +/** 删除永久素材 */ +export function deletePermanentMaterial(id: number) { + return requestClient.delete('/mp/material/delete-permanent', { + params: { id }, + }); +} diff --git a/apps/web-antd/src/api/mp/menu/index.ts b/apps/web-antd/src/api/mp/menu/index.ts new file mode 100644 index 000000000..a99613646 --- /dev/null +++ b/apps/web-antd/src/api/mp/menu/index.ts @@ -0,0 +1,45 @@ +import { MenuType } from '@vben/constants'; + +import { requestClient } from '#/api/request'; + +export namespace MpMenuApi { + /** 菜单按钮信息 */ + export interface MenuButton { + type: MenuType; + name: string; + key?: string; + url?: string; + mediaId?: string; + appId?: string; + pagePath?: string; + subButtons?: MenuButton[]; + } + + /** 菜单信息 */ + export interface Menu { + accountId: number; + menus: MenuButton[]; + } +} + +/** 查询菜单列表 */ +export function getMenuList(accountId: number) { + return requestClient.get('/mp/menu/list', { + params: { accountId }, + }); +} + +/** 保存菜单 */ +export function saveMenu(accountId: number, menus: MpMenuApi.MenuButton[]) { + return requestClient.post('/mp/menu/save', { + accountId, + menus, + }); +} + +/** 删除菜单 */ +export function deleteMenu(accountId: number) { + return requestClient.delete('/mp/menu/delete', { + params: { accountId }, + }); +} diff --git a/apps/web-antd/src/api/mp/message/index.ts b/apps/web-antd/src/api/mp/message/index.ts new file mode 100644 index 000000000..8395e2933 --- /dev/null +++ b/apps/web-antd/src/api/mp/message/index.ts @@ -0,0 +1,60 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { MessageType } from '@vben/constants'; + +import { requestClient } from '#/api/request'; + +export namespace MpMessageApi { + /** 消息信息 */ + export interface Message { + id?: number; + accountId: number; + type: MessageType | string; + openid: string; + content: string; + mediaId?: string; + status: number; + remark?: string; + createTime?: Date; + sendFrom?: number; + userId?: number; + event?: string; + eventKey?: string; + mediaUrl?: string; + recognition?: string; + url?: string; + title?: string; + label?: string; + locationX?: number; + locationY?: number; + thumbMediaUrl?: string; + musicUrl?: string; + hqMusicUrl?: string; + description?: string; + articles?: any[]; + } + + /** 发送消息请求 */ + export interface MessageSendRequestVO { + accountId: number; + openid: string; + type: MessageType; + content: string; + mediaId?: string; + } +} + +/** 查询消息列表 */ +export function getMessagePage(params: PageParam) { + return requestClient.get>( + '/mp/message/page', + { + params, + }, + ); +} + +/** 发送消息 */ +export function sendMessage(data: MpMessageApi.MessageSendRequestVO) { + return requestClient.post('/mp/message/send', data); +} diff --git a/apps/web-antd/src/api/mp/messageTemplate/index.ts b/apps/web-antd/src/api/mp/messageTemplate/index.ts new file mode 100644 index 000000000..6b0d68294 --- /dev/null +++ b/apps/web-antd/src/api/mp/messageTemplate/index.ts @@ -0,0 +1,57 @@ +import { requestClient } from '#/api/request'; + +export namespace MpMessageTemplateApi { + /** 消息模板信息 */ + export interface MessageTemplate { + id: number; + accountId: number; + appId: string; + templateId: string; + title: string; + content: string; + example: string; + primaryIndustry: string; + deputyIndustry: string; + createTime?: Date; + } + + /** 发送消息模板请求 */ + export interface MessageTemplateSendVO { + id: number; + userId: number; + data?: Record; + url?: string; + miniProgramAppId?: string; + miniProgramPagePath?: string; + miniprogram?: string; + } +} + +/** 查询消息模板列表 */ +export function getMessageTemplateList(params: { accountId: number }) { + return requestClient.get( + '/mp/message-template/list', + { params }, + ); +} + +/** 删除消息模板 */ +export function deleteMessageTemplate(id: number) { + return requestClient.delete('/mp/message-template/delete', { + params: { id }, + }); +} + +/** 同步公众号模板 */ +export function syncMessageTemplate(accountId: number) { + return requestClient.post('/mp/message-template/sync', null, { + params: { accountId }, + }); +} + +/** 发送消息模板 */ +export function sendMessageTemplate( + data: MpMessageTemplateApi.MessageTemplateSendVO, +) { + return requestClient.post('/mp/message-template/send', data); +} diff --git a/apps/web-antd/src/api/mp/statistics/index.ts b/apps/web-antd/src/api/mp/statistics/index.ts new file mode 100644 index 000000000..7c053abd5 --- /dev/null +++ b/apps/web-antd/src/api/mp/statistics/index.ts @@ -0,0 +1,83 @@ +import { requestClient } from '#/api/request'; + +export namespace MpStatisticsApi { + /** 统计查询参数 */ + export interface StatisticsGetReqVO { + accountId: number; + date: Date[]; + } + + /** 消息发送概况数据 */ + export interface StatisticsUpstreamMessageRespVO { + refDate: string; + msgType: string; + msgUser: number; + msgCount: number; + } + + /** 用户增减数据 */ + export interface StatisticsUserSummaryRespVO { + refDate: string; + userSource: number; + newUser: number; + cancelUser: number; + cumulateUser: number; + } + + /** 用户累计数据 */ + export interface StatisticsUserCumulateRespVO { + refDate: string; + cumulateUser: number; + } + + /** 接口分析数据 */ + export interface StatisticsInterfaceSummaryRespVO { + refDate: string; + callbackCount: number; + failCount: number; + totalTimeCost: number; + maxTimeCost: number; + } +} + +/** 获取消息发送概况数据 */ +export function getUpstreamMessage(params: MpStatisticsApi.StatisticsGetReqVO) { + return requestClient.get( + '/mp/statistics/upstream-message', + { + params, + }, + ); +} + +/** 获取用户增减数据 */ +export function getUserSummary(params: MpStatisticsApi.StatisticsGetReqVO) { + return requestClient.get( + '/mp/statistics/user-summary', + { + params, + }, + ); +} + +/** 获取用户累计数据 */ +export function getUserCumulate(params: MpStatisticsApi.StatisticsGetReqVO) { + return requestClient.get( + '/mp/statistics/user-cumulate', + { + params, + }, + ); +} + +/** 获取接口分析数据 */ +export function getInterfaceSummary( + params: MpStatisticsApi.StatisticsGetReqVO, +) { + return requestClient.get( + '/mp/statistics/interface-summary', + { + params, + }, + ); +} diff --git a/apps/web-antd/src/api/mp/tag/index.ts b/apps/web-antd/src/api/mp/tag/index.ts new file mode 100644 index 000000000..df8fd3423 --- /dev/null +++ b/apps/web-antd/src/api/mp/tag/index.ts @@ -0,0 +1,57 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MpTagApi { + /** 标签信息 */ + export interface Tag { + id?: number; + accountId: number; + name: string; + count?: number; + createTime?: Date; + } +} + +/** 创建公众号标签 */ +export function createTag(data: MpTagApi.Tag) { + return requestClient.post('/mp/tag/create', data); +} + +/** 更新公众号标签 */ +export function updateTag(data: MpTagApi.Tag) { + return requestClient.put('/mp/tag/update', data); +} + +/** 删除公众号标签 */ +export function deleteTag(id: number) { + return requestClient.delete('/mp/tag/delete', { + params: { id }, + }); +} + +/** 获取公众号标签 */ +export function getTag(id: number) { + return requestClient.get('/mp/tag/get', { + params: { id }, + }); +} + +/** 获取公众号标签分页 */ +export function getTagPage(params: PageParam) { + return requestClient.get>('/mp/tag/page', { + params, + }); +} + +/** 获取公众号标签精简信息列表 */ +export function getSimpleTagList() { + return requestClient.get('/mp/tag/list-all-simple'); +} + +/** 同步公众号标签 */ +export function syncTag(accountId: number) { + return requestClient.post('/mp/tag/sync', null, { + params: { accountId }, + }); +} diff --git a/apps/web-antd/src/api/mp/user/index.ts b/apps/web-antd/src/api/mp/user/index.ts new file mode 100644 index 000000000..d79e6c6f4 --- /dev/null +++ b/apps/web-antd/src/api/mp/user/index.ts @@ -0,0 +1,50 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace MpUserApi { + /** 用户信息 */ + export interface User { + id?: number; + accountId: number; + openid: string; + nickname: string; + avatar: string; + sex: number; + country: string; + province: string; + city: string; + language: string; + subscribe: boolean; + subscribeTime?: Date; + remark?: string; + tagIds?: number[]; + createTime?: Date; + } +} + +/** 更新公众号粉丝 */ +export function updateUser(data: MpUserApi.User) { + return requestClient.put('/mp/user/update', data); +} + +/** 获取公众号粉丝 */ +export function getUser(id: number) { + return requestClient.get('/mp/user/get', { + params: { id }, + }); +} + +/** 获取公众号粉丝分页 */ +export function getUserPage(params: PageParam) { + return requestClient.get>('/mp/user/page', { + params, + }); +} + +/** 同步公众号粉丝 */ +export function syncUser(accountId: number) { + return requestClient.post('/mp/user/sync', null, { + params: { accountId }, + }); +} diff --git a/apps/web-antd/src/api/pay/app/index.ts b/apps/web-antd/src/api/pay/app/index.ts new file mode 100644 index 000000000..d4b1ab6f7 --- /dev/null +++ b/apps/web-antd/src/api/pay/app/index.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace PayAppApi { + /** 支付应用信息 */ + export interface App { + id?: number; + appKey: string; + name: string; + status: number; + remark: string; + payNotifyUrl: string; + refundNotifyUrl: string; + transferNotifyUrl: string; + merchantId: number; + merchantName: string; + createTime?: Date; + channelCodes?: string[]; + } + + /** 更新状态请求 */ + export interface AppUpdateStatusReqVO { + id: number; + status: number; + } +} + +/** 查询支付应用列表 */ +export function getAppPage(params: PageParam) { + return requestClient.get>('/pay/app/page', { + params, + }); +} + +/** 查询支付应用详情 */ +export function getApp(id: number) { + return requestClient.get(`/pay/app/get?id=${id}`); +} + +/** 新增支付应用 */ +export function createApp(data: PayAppApi.App) { + return requestClient.post('/pay/app/create', data); +} + +/** 修改支付应用 */ +export function updateApp(data: PayAppApi.App) { + return requestClient.put('/pay/app/update', data); +} + +/** 修改支付应用状态 */ +export function updateAppStatus(data: PayAppApi.AppUpdateStatusReqVO) { + return requestClient.put('/pay/app/update-status', data); +} + +/** 删除支付应用 */ +export function deleteApp(id: number) { + return requestClient.delete(`/pay/app/delete?id=${id}`); +} + +/** 获取支付应用列表 */ +export function getAppList() { + return requestClient.get('/pay/app/list'); +} diff --git a/apps/web-antd/src/api/pay/channel/index.ts b/apps/web-antd/src/api/pay/channel/index.ts new file mode 100644 index 000000000..99005024c --- /dev/null +++ b/apps/web-antd/src/api/pay/channel/index.ts @@ -0,0 +1,32 @@ +import { requestClient } from '#/api/request'; + +export namespace PayChannelApi { + /** 支付渠道信息 */ + export interface Channel { + id: number; + code: string; + config: string; + status: number; + remark: string; + feeRate: number; + appId: number; + createTime: Date; + } +} + +/** 查询支付渠道详情 */ +export function getChannel(appId: number, code: string) { + return requestClient.get('/pay/channel/get', { + params: { appId, code }, + }); +} + +/** 新增支付渠道 */ +export function createChannel(data: PayChannelApi.Channel) { + return requestClient.post('/pay/channel/create', data); +} + +/** 修改支付渠道 */ +export function updateChannel(data: PayChannelApi.Channel) { + return requestClient.put('/pay/channel/update', data); +} diff --git a/apps/web-antd/src/api/pay/demo/order/index.ts b/apps/web-antd/src/api/pay/demo/order/index.ts new file mode 100644 index 000000000..0fb178888 --- /dev/null +++ b/apps/web-antd/src/api/pay/demo/order/index.ts @@ -0,0 +1,42 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace DemoOrderApi { + /** 示例订单信息 */ + export interface Order { + id?: number; + userId?: number; + spuName?: string; + price?: number; + payStatus?: boolean; + payOrderId?: number; + payTime?: Date; + payChannelCode?: string; + payRefundId?: number; + refundPrice?: number; + refundTime?: Date; + spuId?: number; + createTime?: Date; + } +} + +/** 创建示例订单 */ +export function createDemoOrder(data: DemoOrderApi.Order) { + return requestClient.post('/pay/demo-order/create', data); +} + +/** 获得示例订单分页 */ +export function getDemoOrderPage(params: PageParam) { + return requestClient.get>( + '/pay/demo-order/page', + { + params, + }, + ); +} + +/** 退款示例订单 */ +export function refundDemoOrder(id: number) { + return requestClient.put(`/pay/demo-order/refund?id=${id}`); +} diff --git a/apps/web-antd/src/api/pay/notify/index.ts b/apps/web-antd/src/api/pay/notify/index.ts new file mode 100644 index 000000000..c4fb7b209 --- /dev/null +++ b/apps/web-antd/src/api/pay/notify/index.ts @@ -0,0 +1,40 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace PayNotifyApi { + /** 支付通知任务 */ + export interface NotifyTask { + id: number; + appId: number; + appName: string; + type: number; + dataId: number; + status: number; + merchantOrderId: string; + merchantRefundId?: string; + merchantTransferId?: string; + lastExecuteTime: Date; + nextNotifyTime: Date; + notifyTimes: number; + maxNotifyTimes: number; + createTime: Date; + updateTime: Date; + logs?: any[]; + } +} + +/** 获得支付通知明细 */ +export function getNotifyTaskDetail(id: number) { + return requestClient.get(`/pay/notify/get-detail?id=${id}`); +} + +/** 获得支付通知分页 */ +export function getNotifyTaskPage(params: PageParam) { + return requestClient.get>( + '/pay/notify/page', + { + params, + }, + ); +} diff --git a/apps/web-antd/src/api/pay/order/index.ts b/apps/web-antd/src/api/pay/order/index.ts new file mode 100644 index 000000000..0d4cdbf81 --- /dev/null +++ b/apps/web-antd/src/api/pay/order/index.ts @@ -0,0 +1,73 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace PayOrderApi { + /** 支付订单信息 */ + export interface Order { + id: number; + no: string; + price: number; + channelFeePrice: number; + refundPrice: number; + merchantId: number; + appId: number; + appName: string; + channelId: number; + channelCode: string; + merchantOrderId: string; + subject: string; + body: string; + notifyUrl: string; + notifyStatus: number; + amount: number; + channelFeeRate: number; + channelFeeAmount: number; + status: number; + userIp: string; + expireTime: Date; + successTime: Date; + notifyTime: Date; + successExtensionId: number; + refundStatus: number; + refundTimes: number; + refundAmount: number; + channelUserId: string; + channelOrderNo: string; + channelNotifyData: string; + createTime: Date; + updateTime: Date; + } +} + +/** 查询支付订单列表 */ +export function getOrderPage(params: PageParam) { + return requestClient.get>('/pay/order/page', { + params, + }); +} + +/** 查询支付订单详情 */ +export function getOrder(id: number, sync?: boolean) { + return requestClient.get('/pay/order/get', { + params: { + id, + sync, + }, + }); +} + +/** 获得支付订单的明细 */ +export function getOrderDetail(id: number) { + return requestClient.get(`/pay/order/get-detail?id=${id}`); +} + +/** 提交支付订单 */ +export function submitOrder(data: any) { + return requestClient.post('/pay/order/submit', data); +} + +/** 导出支付订单 */ +export function exportOrder(params: any) { + return requestClient.download('/pay/order/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/pay/refund/index.ts b/apps/web-antd/src/api/pay/refund/index.ts new file mode 100644 index 000000000..a63af4389 --- /dev/null +++ b/apps/web-antd/src/api/pay/refund/index.ts @@ -0,0 +1,59 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace PayRefundApi { + /** 退款订单信息 */ + export interface Refund { + id: number; + merchantId: number; + appId: number; + channelId: number; + channelCode: string; + orderId: string; + tradeNo: string; + merchantOrderId: string; + merchantRefundNo: string; + merchantRefundId: string; + notifyUrl: string; + notifyStatus: number; + status: number; + payPrice: number; + refundPrice: number; + type: number; + payAmount: number; + refundAmount: number; + reason: string; + userIp: string; + channelOrderNo: string; + channelRefundNo: string; + channelErrorCode: string; + channelErrorMsg: string; + channelExtras: string; + expireTime: Date; + successTime: Date; + notifyTime: Date; + createTime: Date; + updateTime: Date; + } +} + +/** 查询退款订单列表 */ +export function getRefundPage(params: PageParam) { + return requestClient.get>( + '/pay/refund/page', + { + params, + }, + ); +} + +/** 查询退款订单详情 */ +export function getRefund(id: number) { + return requestClient.get(`/pay/refund/get?id=${id}`); +} + +/** 导出退款订单 */ +export function exportRefund(params: any) { + return requestClient.download('/pay/refund/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/pay/transfer/index.ts b/apps/web-antd/src/api/pay/transfer/index.ts new file mode 100644 index 000000000..280ad4ddc --- /dev/null +++ b/apps/web-antd/src/api/pay/transfer/index.ts @@ -0,0 +1,52 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace PayTransferApi { + /** 转账单信息 */ + export interface Transfer { + id: number; + no: string; + appId: number; + appName: string; + channelId: number; + channelCode: string; + merchantTransferId: string; + channelTransferNo: string; + price: number; + subject: string; + userName: string; + userAccount: string; + userIp: string; + status: number; + successTime: Date; + createTime: Date; + updateTime: Date; + notifyUrl: string; + channelNotifyData: string; + } +} + +/** 查询转账单列表 */ +export function getTransferPage(params: PageParam) { + return requestClient.get>( + '/pay/transfer/page', + { + params, + }, + ); +} + +/** 查询转账单详情 */ +export function getTransfer(id: number) { + return requestClient.get( + `/pay/transfer/get?id=${id}`, + ); +} + +/** 导出转账单 */ +export function exportTransfer(params: any) { + return requestClient.download('/pay/transfer/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/pay/wallet/balance/index.ts b/apps/web-antd/src/api/pay/wallet/balance/index.ts new file mode 100644 index 000000000..052a6ec95 --- /dev/null +++ b/apps/web-antd/src/api/pay/wallet/balance/index.ts @@ -0,0 +1,51 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace PayWalletApi { + /** 钱包信息 */ + export interface Wallet { + id: number; + userId: number; + userType: number; + balance: number; + totalExpense: number; + totalRecharge: number; + freezePrice: number; + } + + /** 钱包查询参数 */ + export interface WalletUserQueryReqVO { + userId: number; + } + + /** 钱包修改余额 */ + export interface WalletUpdateBalanceReqVO { + userId: number; + balance: number; + } +} + +/** 查询用户钱包详情 */ +export function getWallet(params: PayWalletApi.WalletUserQueryReqVO) { + return requestClient.get('/pay/wallet/get', { + params, + }); +} + +/** 查询会员钱包列表 */ +export function getWalletPage(params: PageParam) { + return requestClient.get>( + '/pay/wallet/page', + { + params, + }, + ); +} + +/** 修改会员钱包余额 */ +export function updateWalletBalance( + data: PayWalletApi.WalletUpdateBalanceReqVO, +) { + return requestClient.put('/pay/wallet/update-balance', data); +} diff --git a/apps/web-antd/src/api/pay/wallet/rechargePackage/index.ts b/apps/web-antd/src/api/pay/wallet/rechargePackage/index.ts new file mode 100644 index 000000000..7f8b5621f --- /dev/null +++ b/apps/web-antd/src/api/pay/wallet/rechargePackage/index.ts @@ -0,0 +1,49 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace WalletRechargePackageApi { + /** 充值套餐信息 */ + export interface WalletRechargePackage { + id?: number; + name: string; + payPrice: number; + bonusPrice: number; + status: number; + } +} + +/** 查询充值套餐列表 */ +export function getWalletRechargePackagePage(params: PageParam) { + return requestClient.get< + PageResult + >('/pay/wallet-recharge-package/page', { + params, + }); +} + +/** 查询充值套餐详情 */ +export function getWalletRechargePackage(id: number) { + return requestClient.get( + `/pay/wallet-recharge-package/get?id=${id}`, + ); +} + +/** 新增充值套餐 */ +export function createWalletRechargePackage( + data: WalletRechargePackageApi.WalletRechargePackage, +) { + return requestClient.post('/pay/wallet-recharge-package/create', data); +} + +/** 修改充值套餐 */ +export function updateWalletRechargePackage( + data: WalletRechargePackageApi.WalletRechargePackage, +) { + return requestClient.put('/pay/wallet-recharge-package/update', data); +} + +/** 删除充值套餐 */ +export function deleteWalletRechargePackage(id: number) { + return requestClient.delete(`/pay/wallet-recharge-package/delete?id=${id}`); +} diff --git a/apps/web-antd/src/api/request.ts b/apps/web-antd/src/api/request.ts new file mode 100644 index 000000000..2568def87 --- /dev/null +++ b/apps/web-antd/src/api/request.ts @@ -0,0 +1,187 @@ +/** + * 该文件可自行根据业务逻辑进行调整 + */ +import type { RequestClientOptions } from '@vben/request'; + +import { isTenantEnable, useAppConfig } from '@vben/hooks'; +import { preferences } from '@vben/preferences'; +import { + authenticateResponseInterceptor, + defaultResponseInterceptor, + errorMessageResponseInterceptor, + RequestClient, +} from '@vben/request'; +import { useAccessStore } from '@vben/stores'; +import { createApiEncrypt } from '@vben/utils'; + +import { message } from 'ant-design-vue'; + +import { useAuthStore } from '#/store'; + +import { refreshTokenApi } from './core'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); +const tenantEnable = isTenantEnable(); +const apiEncrypt = createApiEncrypt(import.meta.env); + +function createRequestClient(baseURL: string, options?: RequestClientOptions) { + const client = new RequestClient({ + ...options, + baseURL, + }); + + /** + * 重新认证逻辑 + */ + async function doReAuthenticate() { + console.warn('Access token or refresh token is invalid or expired. '); + const accessStore = useAccessStore(); + const authStore = useAuthStore(); + accessStore.setAccessToken(null); + if ( + preferences.app.loginExpiredMode === 'modal' && + accessStore.isAccessChecked + ) { + accessStore.setLoginExpired(true); + } else { + await authStore.logout(); + } + } + + /** + * 刷新token逻辑 + */ + async function doRefreshToken() { + const accessStore = useAccessStore(); + const refreshToken = accessStore.refreshToken as string; + if (!refreshToken) { + throw new Error('Refresh token is null!'); + } + const resp = await refreshTokenApi(refreshToken); + const newToken = resp?.data?.data?.accessToken; + // add by 芋艿:这里一定要抛出 resp.data,从而触发 authenticateResponseInterceptor 中,刷新令牌失败!!! + if (!newToken) { + throw resp.data; + } + accessStore.setAccessToken(newToken); + return newToken; + } + + function formatToken(token: null | string) { + return token ? `Bearer ${token}` : null; + } + + // 请求头处理 + client.addRequestInterceptor({ + fulfilled: async (config) => { + const accessStore = useAccessStore(); + + config.headers.Authorization = formatToken(accessStore.accessToken); + config.headers['Accept-Language'] = preferences.app.locale; + // 添加租户编号 + config.headers['tenant-id'] = tenantEnable + ? accessStore.tenantId + : undefined; + // 只有登录时,才设置 visit-tenant-id 访问租户 + config.headers['visit-tenant-id'] = tenantEnable + ? accessStore.visitTenantId + : undefined; + + // 是否 API 加密 + if ((config.headers || {}).isEncrypt) { + try { + // 加密请求数据 + if (config.data) { + config.data = apiEncrypt.encryptRequest(config.data); + // 设置加密标识头 + config.headers[apiEncrypt.getEncryptHeader()] = 'true'; + } + } catch (error) { + console.error('请求数据加密失败:', error); + throw error; + } + } + return config; + }, + }); + + // API 解密响应拦截器 + client.addResponseInterceptor({ + fulfilled: (response) => { + // 检查是否需要解密响应数据 + const encryptHeader = apiEncrypt.getEncryptHeader(); + const isEncryptResponse = + response.headers[encryptHeader] === 'true' || + response.headers[encryptHeader.toLowerCase()] === 'true'; + if (isEncryptResponse && typeof response.data === 'string') { + try { + // 解密响应数据 + response.data = apiEncrypt.decryptResponse(response.data); + } catch (error) { + console.error('响应数据解密失败:', error); + throw new Error(`响应数据解密失败: ${(error as Error).message}`); + } + } + return response; + }, + }); + + // 处理返回的响应数据格式 + client.addResponseInterceptor( + defaultResponseInterceptor({ + codeField: 'code', + dataField: 'data', + successCode: 0, + }), + ); + + // token过期的处理 + client.addResponseInterceptor( + authenticateResponseInterceptor({ + client, + doReAuthenticate, + doRefreshToken, + enableRefreshToken: preferences.app.enableRefreshToken, + formatToken, + }), + ); + + // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里 + client.addResponseInterceptor( + errorMessageResponseInterceptor((msg: string, error) => { + // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg + // 当前mock接口返回的错误字段是 error 或者 message + const responseData = error?.response?.data ?? {}; + const errorMessage = + responseData?.error ?? responseData?.message ?? responseData.msg ?? ''; + // add by 芋艿:特殊:避免 401 “账号未登录”,重复提示。因为,此时会跳转到登录界面,只需提示一次!!! + if (error?.data?.code === 401) { + return; + } + // 如果没有错误信息,则会根据状态码进行提示 + message.error(errorMessage || msg); + }), + ); + + return client; +} + +export const requestClient = createRequestClient(apiURL, { + responseReturn: 'data', +}); + +export const baseRequestClient = new RequestClient({ baseURL: apiURL }); +baseRequestClient.addRequestInterceptor({ + fulfilled: (config) => { + const accessStore = useAccessStore(); + // 添加租户编号 + config.headers['tenant-id'] = tenantEnable + ? accessStore.tenantId + : undefined; + // 只有登录时,才设置 visit-tenant-id 访问租户 + config.headers['visit-tenant-id'] = tenantEnable + ? accessStore.visitTenantId + : undefined; + return config; + }, +}); diff --git a/apps/web-antd/src/api/system/dict/data/index.ts b/apps/web-antd/src/api/system/dict/data/index.ts new file mode 100644 index 000000000..28f94d504 --- /dev/null +++ b/apps/web-antd/src/api/system/dict/data/index.ts @@ -0,0 +1,68 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemDictDataApi { + /** 字典数据 */ + export type DictData = { + colorType: string; + createTime: Date; + cssClass: string; + dictType: string; + id?: number; + label: string; + remark: string; + sort?: number; + status: number; + value: string; + }; +} + +// 查询字典数据(精简)列表 +export function getSimpleDictDataList() { + return requestClient.get( + '/system/dict-data/simple-list', + ); +} + +// 查询字典数据列表 +export function getDictDataPage(params: PageParam) { + return requestClient.get>( + '/system/dict-data/page', + { params }, + ); +} + +// 查询字典数据详情 +export function getDictData(id: number) { + return requestClient.get( + `/system/dict-data/get?id=${id}`, + ); +} + +// 新增字典数据 +export function createDictData(data: SystemDictDataApi.DictData) { + return requestClient.post('/system/dict-data/create', data); +} + +// 修改字典数据 +export function updateDictData(data: SystemDictDataApi.DictData) { + return requestClient.put('/system/dict-data/update', data); +} + +// 删除字典数据 +export function deleteDictData(id: number) { + return requestClient.delete(`/system/dict-data/delete?id=${id}`); +} + +// 批量删除字典数据 +export function deleteDictDataList(ids: number[]) { + return requestClient.delete( + `/system/dict-data/delete-list?ids=${ids.join(',')}`, + ); +} + +// 导出字典类型数据 +export function exportDictData(params: any) { + return requestClient.download('/system/dict-data/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/system/dict/type/index.ts b/apps/web-antd/src/api/system/dict/type/index.ts new file mode 100644 index 000000000..7fb426337 --- /dev/null +++ b/apps/web-antd/src/api/system/dict/type/index.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemDictTypeApi { + /** 字典类型 */ + export type DictType = { + createTime: Date; + id?: number; + name: string; + remark: string; + status: number; + type: string; + }; +} + +// 查询字典(精简)列表 +export function getSimpleDictTypeList() { + return requestClient.get( + '/system/dict-type/list-all-simple', + ); +} + +// 查询字典列表 +export function getDictTypePage(params: PageParam) { + return requestClient.get>( + '/system/dict-type/page', + { params }, + ); +} + +// 查询字典详情 +export function getDictType(id: number) { + return requestClient.get( + `/system/dict-type/get?id=${id}`, + ); +} + +// 新增字典 +export function createDictType(data: SystemDictTypeApi.DictType) { + return requestClient.post('/system/dict-type/create', data); +} + +// 修改字典 +export function updateDictType(data: SystemDictTypeApi.DictType) { + return requestClient.put('/system/dict-type/update', data); +} + +// 删除字典 +export function deleteDictType(id: number) { + return requestClient.delete(`/system/dict-type/delete?id=${id}`); +} + +// 批量删除字典 +export function deleteDictTypeList(ids: number[]) { + return requestClient.delete( + `/system/dict-type/delete-list?ids=${ids.join(',')}`, + ); +} + +// 导出字典类型 +export function exportDictType(params: any) { + return requestClient.download('/system/dict-type/export-excel', { params }); +} diff --git a/apps/web-antd/src/api/system/mail/log/index.ts b/apps/web-antd/src/api/system/mail/log/index.ts new file mode 100644 index 000000000..c32b790e1 --- /dev/null +++ b/apps/web-antd/src/api/system/mail/log/index.ts @@ -0,0 +1,36 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemMailLogApi { + /** 邮件日志 */ + export interface MailLog { + id: number; + userId: number; + userType: number; + toMails: string[]; + ccMails?: string[]; + bccMails?: string[]; + accountId: number; + fromMail: string; + templateId: number; + templateCode: string; + templateNickname: string; + templateTitle: string; + templateContent: string; + templateParams: string; + sendStatus: number; + sendTime: string; + sendMessageId: string; + sendException: string; + createTime: string; + } +} + +/** 查询邮件日志列表 */ +export function getMailLogPage(params: PageParam) { + return requestClient.get>( + '/system/mail-log/page', + { params }, + ); +} diff --git a/apps/web-antd/src/api/system/mail/template/index.ts b/apps/web-antd/src/api/system/mail/template/index.ts new file mode 100644 index 000000000..9e2a5a78d --- /dev/null +++ b/apps/web-antd/src/api/system/mail/template/index.ts @@ -0,0 +1,71 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemMailTemplateApi { + /** 邮件模版信息 */ + export interface MailTemplate { + id: number; + name: string; + code: string; + accountId: number; + nickname: string; + title: string; + content: string; + params: string[]; + status: number; + remark: string; + createTime: Date; + } + + /** 邮件发送信息 */ + export interface MailSendReqVO { + toMails: string[]; + ccMails?: string[]; + bccMails?: string[]; + templateCode: string; + templateParams: Record; + } +} + +/** 查询邮件模版列表 */ +export function getMailTemplatePage(params: PageParam) { + return requestClient.get>( + '/system/mail-template/page', + { params }, + ); +} + +/** 查询邮件模版详情 */ +export function getMailTemplate(id: number) { + return requestClient.get( + `/system/mail-template/get?id=${id}`, + ); +} + +/** 新增邮件模版 */ +export function createMailTemplate(data: SystemMailTemplateApi.MailTemplate) { + return requestClient.post('/system/mail-template/create', data); +} + +/** 修改邮件模版 */ +export function updateMailTemplate(data: SystemMailTemplateApi.MailTemplate) { + return requestClient.put('/system/mail-template/update', data); +} + +/** 删除邮件模版 */ +export function deleteMailTemplate(id: number) { + return requestClient.delete(`/system/mail-template/delete?id=${id}`); +} + +/** 批量删除邮件模版 */ +export function deleteMailTemplateList(ids: number[]) { + return requestClient.delete( + `/system/mail-template/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 发送邮件 */ +export function sendMail(data: SystemMailTemplateApi.MailSendReqVO) { + return requestClient.post('/system/mail-template/send-mail', data); +} diff --git a/apps/web-antd/src/api/system/notify/template/index.ts b/apps/web-antd/src/api/system/notify/template/index.ts new file mode 100644 index 000000000..dd19f4b8f --- /dev/null +++ b/apps/web-antd/src/api/system/notify/template/index.ts @@ -0,0 +1,79 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemNotifyTemplateApi { + /** 站内信模板信息 */ + export interface NotifyTemplate { + id?: number; + name: string; + nickname: string; + code: string; + content: string; + type?: number; + params: string[]; + status: number; + remark: string; + } + + /** 发送站内信请求 */ + export interface NotifySendReqVO { + userId: number; + userType: number; + templateCode: string; + templateParams: Record; + } +} + +/** 查询站内信模板列表 */ +export function getNotifyTemplatePage(params: PageParam) { + return requestClient.get>( + '/system/notify-template/page', + { params }, + ); +} + +/** 查询站内信模板详情 */ +export function getNotifyTemplate(id: number) { + return requestClient.get( + `/system/notify-template/get?id=${id}`, + ); +} + +/** 新增站内信模板 */ +export function createNotifyTemplate( + data: SystemNotifyTemplateApi.NotifyTemplate, +) { + return requestClient.post('/system/notify-template/create', data); +} + +/** 修改站内信模板 */ +export function updateNotifyTemplate( + data: SystemNotifyTemplateApi.NotifyTemplate, +) { + return requestClient.put('/system/notify-template/update', data); +} + +/** 删除站内信模板 */ +export function deleteNotifyTemplate(id: number) { + return requestClient.delete(`/system/notify-template/delete?id=${id}`); +} + +/** 批量删除站内信模板 */ +export function deleteNotifyTemplateList(ids: number[]) { + return requestClient.delete( + `/system/notify-template/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出站内信模板 */ +export function exportNotifyTemplate(params: any) { + return requestClient.download('/system/notify-template/export-excel', { + params, + }); +} + +/** 发送站内信 */ +export function sendNotify(data: SystemNotifyTemplateApi.NotifySendReqVO) { + return requestClient.post('/system/notify-template/send-notify', data); +} diff --git a/apps/web-antd/src/api/system/oauth2/client/index.ts b/apps/web-antd/src/api/system/oauth2/client/index.ts new file mode 100644 index 000000000..5817b858b --- /dev/null +++ b/apps/web-antd/src/api/system/oauth2/client/index.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemOAuth2ClientApi { + /** OAuth2.0 客户端信息 */ + export interface OAuth2Client { + id?: number; + clientId: string; + secret: string; + name: string; + logo: string; + description: string; + status: number; + accessTokenValiditySeconds: number; + refreshTokenValiditySeconds: number; + redirectUris: string[]; + autoApprove: boolean; + authorizedGrantTypes: string[]; + scopes: string[]; + authorities: string[]; + resourceIds: string[]; + additionalInformation: string; + isAdditionalInformationJson: boolean; + createTime?: Date; + } +} + +/** 查询 OAuth2.0 客户端列表 */ +export function getOAuth2ClientPage(params: PageParam) { + return requestClient.get>( + '/system/oauth2-client/page', + { params }, + ); +} + +/** 查询 OAuth2.0 客户端详情 */ +export function getOAuth2Client(id: number) { + return requestClient.get( + `/system/oauth2-client/get?id=${id}`, + ); +} + +/** 新增 OAuth2.0 客户端 */ +export function createOAuth2Client(data: SystemOAuth2ClientApi.OAuth2Client) { + return requestClient.post('/system/oauth2-client/create', data); +} + +/** 修改 OAuth2.0 客户端 */ +export function updateOAuth2Client(data: SystemOAuth2ClientApi.OAuth2Client) { + return requestClient.put('/system/oauth2-client/update', data); +} + +/** 删除 OAuth2.0 客户端 */ +export function deleteOAuth2Client(id: number) { + return requestClient.delete(`/system/oauth2-client/delete?id=${id}`); +} + +/** 批量删除 OAuth2.0 客户端 */ +export function deleteOAuth2ClientList(ids: number[]) { + return requestClient.delete( + `/system/oauth2-client/delete-list?ids=${ids.join(',')}`, + ); +} diff --git a/apps/web-antd/src/api/system/oauth2/open/index.ts b/apps/web-antd/src/api/system/oauth2/open/index.ts new file mode 100644 index 000000000..16d9c7e62 --- /dev/null +++ b/apps/web-antd/src/api/system/oauth2/open/index.ts @@ -0,0 +1,58 @@ +import { requestClient } from '#/api/request'; + +/** OAuth2.0 授权信息响应 */ +export namespace SystemOAuth2ClientApi { + /** 授权信息 */ + export interface AuthorizeInfoRespVO { + client: { + logo: string; + name: string; + }; + scopes: { + key: string; + value: boolean; + }[]; + } +} + +/** 获得授权信息 */ +export function getAuthorize(clientId: string) { + return requestClient.get( + `/system/oauth2/authorize?clientId=${clientId}`, + ); +} + +/** 发起授权 */ +export function authorize( + responseType: string, + clientId: string, + redirectUri: string, + state: string, + autoApprove: boolean, + checkedScopes: string[], + uncheckedScopes: string[], +) { + // 构建 scopes + const scopes: Record = {}; + for (const scope of checkedScopes) { + scopes[scope] = true; + } + for (const scope of uncheckedScopes) { + scopes[scope] = false; + } + + // 发起请求 + return requestClient.post('/system/oauth2/authorize', null, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + params: { + response_type: responseType, + client_id: clientId, + redirect_uri: redirectUri, + state, + auto_approve: autoApprove, + scope: JSON.stringify(scopes), + }, + }); +} diff --git a/apps/web-antd/src/api/system/oauth2/token/index.ts b/apps/web-antd/src/api/system/oauth2/token/index.ts new file mode 100644 index 000000000..bd3697915 --- /dev/null +++ b/apps/web-antd/src/api/system/oauth2/token/index.ts @@ -0,0 +1,34 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemOAuth2TokenApi { + /** OAuth2.0 令牌信息 */ + export interface OAuth2Token { + id?: number; + accessToken: string; + refreshToken: string; + userId: number; + userType: number; + clientId: string; + createTime?: Date; + expiresTime?: Date; + } +} + +/** 查询 OAuth2.0 令牌列表 */ +export function getOAuth2TokenPage(params: PageParam) { + return requestClient.get>( + '/system/oauth2-token/page', + { + params, + }, + ); +} + +/** 删除 OAuth2.0 令牌 */ +export function deleteOAuth2Token(accessToken: string) { + return requestClient.delete( + `/system/oauth2-token/delete?accessToken=${accessToken}`, + ); +} diff --git a/apps/web-antd/src/api/system/permission/index.ts b/apps/web-antd/src/api/system/permission/index.ts new file mode 100644 index 000000000..9039d9a05 --- /dev/null +++ b/apps/web-antd/src/api/system/permission/index.ts @@ -0,0 +1,57 @@ +import { requestClient } from '#/api/request'; + +export namespace SystemPermissionApi { + /** 分配用户角色请求 */ + export interface AssignUserRoleReqVO { + userId: number; + roleIds: number[]; + } + + /** 分配角色菜单请求 */ + export interface AssignRoleMenuReqVO { + roleId: number; + menuIds: number[]; + } + + /** 分配角色数据权限请求 */ + export interface AssignRoleDataScopeReqVO { + roleId: number; + dataScope: number; + dataScopeDeptIds: number[]; + } +} + +/** 查询角色拥有的菜单权限 */ +export async function getRoleMenuList(roleId: number) { + return requestClient.get( + `/system/permission/list-role-menus?roleId=${roleId}`, + ); +} + +/** 赋予角色菜单权限 */ +export async function assignRoleMenu( + data: SystemPermissionApi.AssignRoleMenuReqVO, +) { + return requestClient.post('/system/permission/assign-role-menu', data); +} + +/** 赋予角色数据权限 */ +export async function assignRoleDataScope( + data: SystemPermissionApi.AssignRoleDataScopeReqVO, +) { + return requestClient.post('/system/permission/assign-role-data-scope', data); +} + +/** 查询用户拥有的角色数组 */ +export async function getUserRoleList(userId: number) { + return requestClient.get( + `/system/permission/list-user-roles?userId=${userId}`, + ); +} + +/** 赋予用户角色 */ +export async function assignUserRole( + data: SystemPermissionApi.AssignUserRoleReqVO, +) { + return requestClient.post('/system/permission/assign-user-role', data); +} diff --git a/apps/web-antd/src/api/system/sms/template/index.ts b/apps/web-antd/src/api/system/sms/template/index.ts new file mode 100644 index 000000000..eccfb911e --- /dev/null +++ b/apps/web-antd/src/api/system/sms/template/index.ts @@ -0,0 +1,77 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemSmsTemplateApi { + /** 短信模板信息 */ + export interface SmsTemplate { + id?: number; + type?: number; + status: number; + code: string; + name: string; + content: string; + remark: string; + apiTemplateId: string; + channelId?: number; + channelCode?: string; + params?: string[]; + createTime?: Date; + } + + /** 发送短信请求 */ + export interface SmsSendReqVO { + mobile: string; + templateCode: string; + templateParams: Record; + } +} + +/** 查询短信模板列表 */ +export function getSmsTemplatePage(params: PageParam) { + return requestClient.get>( + '/system/sms-template/page', + { params }, + ); +} + +/** 查询短信模板详情 */ +export function getSmsTemplate(id: number) { + return requestClient.get( + `/system/sms-template/get?id=${id}`, + ); +} + +/** 新增短信模板 */ +export function createSmsTemplate(data: SystemSmsTemplateApi.SmsTemplate) { + return requestClient.post('/system/sms-template/create', data); +} + +/** 修改短信模板 */ +export function updateSmsTemplate(data: SystemSmsTemplateApi.SmsTemplate) { + return requestClient.put('/system/sms-template/update', data); +} + +/** 删除短信模板 */ +export function deleteSmsTemplate(id: number) { + return requestClient.delete(`/system/sms-template/delete?id=${id}`); +} + +/** 批量删除短信模板 */ +export function deleteSmsTemplateList(ids: number[]) { + return requestClient.delete( + `/system/sms-template/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出短信模板 */ +export function exportSmsTemplate(params: any) { + return requestClient.download('/system/sms-template/export-excel', { + params, + }); +} + +/** 发送短信 */ +export function sendSms(data: SystemSmsTemplateApi.SmsSendReqVO) { + return requestClient.post('/system/sms-template/send-sms', data); +} diff --git a/apps/web-antd/src/api/system/social/client/index.ts b/apps/web-antd/src/api/system/social/client/index.ts new file mode 100644 index 000000000..181cdf86b --- /dev/null +++ b/apps/web-antd/src/api/system/social/client/index.ts @@ -0,0 +1,55 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemSocialClientApi { + /** 社交客户端信息 */ + export interface SocialClient { + id?: number; + name: string; + socialType: number; + userType: number; + clientId: string; + clientSecret: string; + agentId?: string; + status: number; + createTime?: Date; + } +} + +/** 查询社交客户端列表 */ +export function getSocialClientPage(params: PageParam) { + return requestClient.get>( + '/system/social-client/page', + { params }, + ); +} + +/** 查询社交客户端详情 */ +export function getSocialClient(id: number) { + return requestClient.get( + `/system/social-client/get?id=${id}`, + ); +} + +/** 新增社交客户端 */ +export function createSocialClient(data: SystemSocialClientApi.SocialClient) { + return requestClient.post('/system/social-client/create', data); +} + +/** 修改社交客户端 */ +export function updateSocialClient(data: SystemSocialClientApi.SocialClient) { + return requestClient.put('/system/social-client/update', data); +} + +/** 删除社交客户端 */ +export function deleteSocialClient(id: number) { + return requestClient.delete(`/system/social-client/delete?id=${id}`); +} + +/** 批量删除社交客户端 */ +export function deleteSocialClientList(ids: number[]) { + return requestClient.delete( + `/system/social-client/delete-list?ids=${ids.join(',')}`, + ); +} diff --git a/apps/web-antd/src/api/system/social/user/index.ts b/apps/web-antd/src/api/system/social/user/index.ts new file mode 100644 index 000000000..b91f15064 --- /dev/null +++ b/apps/web-antd/src/api/system/social/user/index.ts @@ -0,0 +1,66 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemSocialUserApi { + /** 社交用户信息 */ + export interface SocialUser { + id?: number; + type: number; + openid: string; + token: string; + rawTokenInfo: string; + nickname: string; + avatar: string; + rawUserInfo: string; + code: string; + state: string; + createTime?: Date; + updateTime?: Date; + } + + /** 社交绑定请求 */ + export interface SocialUserBindReqVO { + type: number; + code: string; + state: string; + } + + /** 取消社交绑定请求 */ + export interface SocialUserUnbindReqVO { + type: number; + openid: string; + } +} + +/** 查询社交用户列表 */ +export function getSocialUserPage(params: PageParam) { + return requestClient.get>( + '/system/social-user/page', + { params }, + ); +} + +/** 查询社交用户详情 */ +export function getSocialUser(id: number) { + return requestClient.get( + `/system/social-user/get?id=${id}`, + ); +} + +/** 社交绑定,使用 code 授权码 */ +export function socialBind(data: SystemSocialUserApi.SocialUserBindReqVO) { + return requestClient.post('/system/social-user/bind', data); +} + +/** 取消社交绑定 */ +export function socialUnbind(data: SystemSocialUserApi.SocialUserUnbindReqVO) { + return requestClient.delete('/system/social-user/unbind', { data }); +} + +/** 获得绑定社交用户列表 */ +export function getBindSocialUserList() { + return requestClient.get( + '/system/social-user/get-bind-list', + ); +} diff --git a/apps/web-antd/src/api/system/tenant/index.ts b/apps/web-antd/src/api/system/tenant/index.ts new file mode 100644 index 000000000..6ee52e1d6 --- /dev/null +++ b/apps/web-antd/src/api/system/tenant/index.ts @@ -0,0 +1,76 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemTenantApi { + /** 租户信息 */ + export interface Tenant { + id?: number; + name: string; + packageId: number; + contactName: string; + contactMobile: string; + accountCount: number; + expireTime: Date; + websites: string[]; + status: number; + } +} + +/** 租户列表 */ +export function getTenantPage(params: PageParam) { + return requestClient.get>( + '/system/tenant/page', + { params }, + ); +} + +/** 获取租户精简信息列表 */ +export function getSimpleTenantList() { + return requestClient.get( + '/system/tenant/simple-list', + ); +} + +/** 查询租户详情 */ +export function getTenant(id: number) { + return requestClient.get( + `/system/tenant/get?id=${id}`, + ); +} + +/** 获取租户精简信息列表 */ +export function getTenantList() { + return requestClient.get( + '/system/tenant/simple-list', + ); +} + +/** 新增租户 */ +export function createTenant(data: SystemTenantApi.Tenant) { + return requestClient.post('/system/tenant/create', data); +} + +/** 修改租户 */ +export function updateTenant(data: SystemTenantApi.Tenant) { + return requestClient.put('/system/tenant/update', data); +} + +/** 删除租户 */ +export function deleteTenant(id: number) { + return requestClient.delete(`/system/tenant/delete?id=${id}`); +} + +/** 批量删除租户 */ +export function deleteTenantList(ids: number[]) { + return requestClient.delete( + `/system/tenant/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出租户 */ +export function exportTenant(params: any) { + return requestClient.download('/system/tenant/export-excel', { + params, + }); +} diff --git a/apps/web-antd/src/api/system/user/index.ts b/apps/web-antd/src/api/system/user/index.ts new file mode 100644 index 000000000..c9f23a403 --- /dev/null +++ b/apps/web-antd/src/api/system/user/index.ts @@ -0,0 +1,88 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemUserApi { + /** 用户信息 */ + export interface User { + id?: number; + username: string; + nickname: string; + deptId: number; + postIds: string[]; + email: string; + mobile: string; + sex: number; + avatar: string; + loginIp: string; + status: number; + remark: string; + createTime?: Date; + } +} + +/** 查询用户管理列表 */ +export function getUserPage(params: PageParam) { + return requestClient.get>( + '/system/user/page', + { params }, + ); +} + +/** 查询用户详情 */ +export function getUser(id: number) { + return requestClient.get(`/system/user/get?id=${id}`); +} + +/** 新增用户 */ +export function createUser(data: SystemUserApi.User) { + return requestClient.post('/system/user/create', data); +} + +/** 修改用户 */ +export function updateUser(data: SystemUserApi.User) { + return requestClient.put('/system/user/update', data); +} + +/** 删除用户 */ +export function deleteUser(id: number) { + return requestClient.delete(`/system/user/delete?id=${id}`); +} + +/** 批量删除用户 */ +export function deleteUserList(ids: number[]) { + return requestClient.delete(`/system/user/delete-list?ids=${ids.join(',')}`); +} + +/** 导出用户 */ +export function exportUser(params: any) { + return requestClient.download('/system/user/export-excel', { params }); +} + +/** 下载用户导入模板 */ +export function importUserTemplate() { + return requestClient.download('/system/user/get-import-template'); +} + +/** 导入用户 */ +export function importUser(file: File, updateSupport: boolean) { + return requestClient.upload('/system/user/import', { + file, + updateSupport, + }); +} + +/** 用户密码重置 */ +export function resetUserPassword(id: number, password: string) { + return requestClient.put('/system/user/update-password', { id, password }); +} + +/** 用户状态修改 */ +export function updateUserStatus(id: number, status: number) { + return requestClient.put('/system/user/update-status', { id, status }); +} + +/** 获取用户精简信息列表 */ +export function getSimpleUserList() { + return requestClient.get('/system/user/simple-list'); +} diff --git a/apps/web-antd/src/api/system/user/profile/index.ts b/apps/web-antd/src/api/system/user/profile/index.ts new file mode 100644 index 000000000..97898e71a --- /dev/null +++ b/apps/web-antd/src/api/system/user/profile/index.ts @@ -0,0 +1,56 @@ +import { requestClient } from '#/api/request'; + +export namespace SystemUserProfileApi { + /** 用户个人中心信息 */ + export interface UserProfileRespVO { + id: number; + username: string; + nickname: string; + email?: string; + mobile?: string; + sex?: number; + avatar?: string; + loginIp: string; + loginDate: string; + createTime: string; + roles: any[]; + dept: any; + posts: any[]; + } + + /** 更新密码请求 */ + export interface UpdatePasswordReqVO { + oldPassword: string; + newPassword: string; + } + + /** 更新个人信息请求 */ + export interface UpdateProfileReqVO { + nickname?: string; + email?: string; + mobile?: string; + sex?: number; + avatar?: string; + } +} + +/** 获取登录用户信息 */ +export function getUserProfile() { + return requestClient.get( + '/system/user/profile/get', + ); +} + +/** 修改用户个人信息 */ +export function updateUserProfile( + data: SystemUserProfileApi.UpdateProfileReqVO, +) { + return requestClient.put('/system/user/profile/update', data); +} + +/** 修改用户个人密码 */ +export function updateUserPassword( + data: SystemUserProfileApi.UpdatePasswordReqVO, +) { + return requestClient.put('/system/user/profile/update-password', data); +} diff --git a/apps/web-antd/src/assets/imgs/diy/app-nav-bar-mp.png b/apps/web-antd/src/assets/imgs/diy/app-nav-bar-mp.png new file mode 100644 index 000000000..c982804c7 Binary files /dev/null and b/apps/web-antd/src/assets/imgs/diy/app-nav-bar-mp.png differ diff --git a/apps/web-antd/src/assets/imgs/diy/statusBar.png b/apps/web-antd/src/assets/imgs/diy/statusBar.png new file mode 100644 index 000000000..b85562e42 Binary files /dev/null and b/apps/web-antd/src/assets/imgs/diy/statusBar.png differ diff --git a/apps/web-antd/src/assets/imgs/wechat.png b/apps/web-antd/src/assets/imgs/wechat.png new file mode 100644 index 000000000..6afc5e41c Binary files /dev/null and b/apps/web-antd/src/assets/imgs/wechat.png differ diff --git a/apps/web-antd/src/bootstrap.ts b/apps/web-antd/src/bootstrap.ts new file mode 100644 index 000000000..be5a5eda7 --- /dev/null +++ b/apps/web-antd/src/bootstrap.ts @@ -0,0 +1,82 @@ +import { createApp, watchEffect } from 'vue'; +import VueDOMPurifyHTML from 'vue-dompurify-html'; + +import { registerAccessDirective } from '@vben/access'; +import { registerLoadingDirective } from '@vben/common-ui/es/loading'; +import { preferences } from '@vben/preferences'; +import { initStores } from '@vben/stores'; +import '@vben/styles'; +import '@vben/styles/antd'; + +import { useTitle } from '@vueuse/core'; + +import { $t, setupI18n } from '#/locales'; +import { setupFormCreate } from '#/plugins/form-create'; + +import { initComponentAdapter } from './adapter/component'; +import { initSetupVbenForm } from './adapter/form'; +import App from './app.vue'; +import { router } from './router'; + +async function bootstrap(namespace: string) { + // 初始化组件适配器 + await initComponentAdapter(); + + // 初始化表单组件 + await initSetupVbenForm(); + + // // 设置弹窗的默认配置 + // setDefaultModalProps({ + // fullscreenButton: false, + // }); + // // 设置抽屉的默认配置 + // setDefaultDrawerProps({ + // zIndex: 1020, + // }); + + const app = createApp(App); + app.use(VueDOMPurifyHTML); + + // 注册v-loading指令 + registerLoadingDirective(app, { + loading: 'loading', // 在这里可以自定义指令名称,也可以明确提供false表示不注册这个指令 + spinning: 'spinning', + }); + + // 国际化 i18n 配置 + await setupI18n(app); + + // 配置 pinia-store + await initStores(app, { namespace }); + + // 安装权限指令 + registerAccessDirective(app); + + // 初始化 tippy + const { initTippy } = await import('@vben/common-ui/es/tippy'); + initTippy(app); + + // 配置路由及路由守卫 + app.use(router); + + // formCreate + setupFormCreate(app); + + // 配置Motion插件 + const { MotionPlugin } = await import('@vben/plugins/motion'); + app.use(MotionPlugin); + + // 动态更新标题 + watchEffect(() => { + if (preferences.app.dynamicTitle) { + const routeTitle = router.currentRoute.value.meta?.title; + const pageTitle = + (routeTitle ? `${$t(routeTitle)} - ` : '') + preferences.app.name; + useTitle(pageTitle); + } + }); + + app.mount('#app'); +} + +export { bootstrap }; diff --git a/apps/web-antd/src/components/cron-tab/cron-tab.vue b/apps/web-antd/src/components/cron-tab/cron-tab.vue new file mode 100644 index 000000000..242ecb554 --- /dev/null +++ b/apps/web-antd/src/components/cron-tab/cron-tab.vue @@ -0,0 +1,961 @@ + + + + + diff --git a/apps/web-antd/src/components/cron-tab/types.ts b/apps/web-antd/src/components/cron-tab/types.ts new file mode 100644 index 000000000..2adf942b7 --- /dev/null +++ b/apps/web-antd/src/components/cron-tab/types.ts @@ -0,0 +1,266 @@ +export interface ShortcutsType { + text: string; + value: string; +} + +export interface CronRange { + start: number | string | undefined; + end: number | string | undefined; +} + +export interface CronLoop { + start: number | string | undefined; + end: number | string | undefined; +} + +export interface CronItem { + type: string; + range: CronRange; + loop: CronLoop; + appoint: string[]; + last?: string; +} + +export interface CronValue { + second: CronItem; + minute: CronItem; + hour: CronItem; + day: CronItem; + month: CronItem; + week: CronItem & { last: string }; + year: CronItem; +} + +export interface WeekOption { + value: string; + label: string; +} + +export interface CronData { + second: string[]; + minute: string[]; + hour: string[]; + day: string[]; + month: string[]; + week: WeekOption[]; + year: number[]; +} + +const getYear = (): number[] => { + const v: number[] = []; + const y = new Date().getFullYear(); + for (let i = 0; i < 11; i++) { + v.push(y + i); + } + return v; +}; + +export const CronValueDefault: CronValue = { + second: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + minute: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + hour: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + day: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 1, + end: 1, + }, + appoint: [], + }, + month: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 1, + end: 1, + }, + appoint: [], + }, + week: { + type: '5', + range: { + start: '2', + end: '3', + }, + loop: { + start: 0, + end: '2', + }, + last: '2', + appoint: [], + }, + year: { + type: '-1', + range: { + start: getYear()[0], + end: getYear()[1], + }, + loop: { + start: getYear()[0], + end: 1, + }, + appoint: [], + }, +}; + +export const CronDataDefault: CronData = { + second: [ + '0', + '5', + '15', + '20', + '25', + '30', + '35', + '40', + '45', + '50', + '55', + '59', + ], + minute: [ + '0', + '5', + '15', + '20', + '25', + '30', + '35', + '40', + '45', + '50', + '55', + '59', + ], + hour: [ + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '20', + '21', + '22', + '23', + ], + day: [ + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '20', + '21', + '22', + '23', + '24', + '25', + '26', + '27', + '28', + '29', + '30', + '31', + ], + month: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], + week: [ + { + value: '1', + label: '周日', + }, + { + value: '2', + label: '周一', + }, + { + value: '3', + label: '周二', + }, + { + value: '4', + label: '周三', + }, + { + value: '5', + label: '周四', + }, + { + value: '6', + label: '周五', + }, + { + value: '7', + label: '周六', + }, + ], + year: getYear(), +}; diff --git a/apps/web-antd/src/components/cropper/cropper-avatar.vue b/apps/web-antd/src/components/cropper/cropper-avatar.vue new file mode 100644 index 000000000..72c71d031 --- /dev/null +++ b/apps/web-antd/src/components/cropper/cropper-avatar.vue @@ -0,0 +1,123 @@ + + + diff --git a/apps/web-antd/src/components/cropper/cropper-modal.vue b/apps/web-antd/src/components/cropper/cropper-modal.vue new file mode 100644 index 000000000..90c9dbc75 --- /dev/null +++ b/apps/web-antd/src/components/cropper/cropper-modal.vue @@ -0,0 +1,301 @@ + + + diff --git a/apps/web-antd/src/components/description/description.vue b/apps/web-antd/src/components/description/description.vue new file mode 100644 index 000000000..d591e5722 --- /dev/null +++ b/apps/web-antd/src/components/description/description.vue @@ -0,0 +1,195 @@ + diff --git a/apps/web-antd/src/components/description/typing.ts b/apps/web-antd/src/components/description/typing.ts new file mode 100644 index 000000000..97cd8cab9 --- /dev/null +++ b/apps/web-antd/src/components/description/typing.ts @@ -0,0 +1,43 @@ +import type { DescriptionsProps } from 'ant-design-vue/es/descriptions'; +import type { JSX } from 'vue/jsx-runtime'; + +import type { CSSProperties, VNode } from 'vue'; + +import type { Recordable } from '@vben/types'; + +export interface DescriptionItemSchema { + labelMinWidth?: number; + contentMinWidth?: number; + labelStyle?: CSSProperties; // 自定义标签样式 + field: string; // 对应 data 中的字段名 + label: JSX.Element | string | VNode; // 内容的描述 + span?: number; // 包含列的数量 + show?: (...arg: any) => boolean; // 是否显示 + slot?: string; // 插槽名称 + render?: ( + val: any, + data?: Recordable, + ) => Element | JSX.Element | number | string | undefined | VNode; // 自定义需要展示的内容 +} + +export interface DescriptionProps extends DescriptionsProps { + useCard?: boolean; // 是否包含卡片组件 + schema: DescriptionItemSchema[]; // 描述项配置 + data: Recordable; // 数据 + title?: string; // 标题 + bordered?: boolean; // 是否包含边框 + column?: + | number + | { + lg: number; + md: number; + sm: number; + xl: number; + xs: number; + xxl: number; + }; // 列数 +} + +export interface DescInstance { + setDescProps(descProps: Partial): void; +} diff --git a/apps/web-antd/src/components/description/use-description.ts b/apps/web-antd/src/components/description/use-description.ts new file mode 100644 index 000000000..fd24920f0 --- /dev/null +++ b/apps/web-antd/src/components/description/use-description.ts @@ -0,0 +1,31 @@ +import type { Component } from 'vue'; + +import type { DescInstance, DescriptionProps } from './typing'; + +import { h, reactive } from 'vue'; + +import Description from './description.vue'; + +export function useDescription(options?: Partial) { + const propsState = reactive>(options || {}); + + const api: DescInstance = { + setDescProps: (descProps: Partial): void => { + Object.assign(propsState, descProps); + }, + }; + + // 创建一个包装组件,将 propsState 合并到 props 中 + const DescriptionWrapper: Component = { + name: 'UseDescription', + inheritAttrs: false, + setup(_props, { attrs, slots }) { + return () => { + // @ts-ignore - 避免类型实例化过深 + return h(Description, { ...propsState, ...attrs }, slots); + }; + }, + }; + + return [DescriptionWrapper, api] as const; +} diff --git a/apps/web-antd/src/components/dict-tag/dict-tag.vue b/apps/web-antd/src/components/dict-tag/dict-tag.vue new file mode 100644 index 000000000..14cc34d86 --- /dev/null +++ b/apps/web-antd/src/components/dict-tag/dict-tag.vue @@ -0,0 +1,71 @@ + + + diff --git a/apps/web-antd/src/components/form-create/components/dict-select.vue b/apps/web-antd/src/components/form-create/components/dict-select.vue new file mode 100644 index 000000000..03a527458 --- /dev/null +++ b/apps/web-antd/src/components/form-create/components/dict-select.vue @@ -0,0 +1,74 @@ + + + + diff --git a/apps/web-antd/src/components/form-create/components/use-api-select.tsx b/apps/web-antd/src/components/form-create/components/use-api-select.tsx new file mode 100644 index 000000000..c63e2cd1e --- /dev/null +++ b/apps/web-antd/src/components/form-create/components/use-api-select.tsx @@ -0,0 +1,336 @@ +import type { ApiSelectProps } from '#/components/form-create/typing'; + +import { defineComponent, onMounted, ref, useAttrs } from 'vue'; + +import { isEmpty } from '@vben/utils'; + +import { + Checkbox, + CheckboxGroup, + Radio, + RadioGroup, + Select, + SelectOption, +} from 'ant-design-vue'; + +import { requestClient } from '#/api/request'; + +export function useApiSelect(option: ApiSelectProps) { + return defineComponent({ + name: option.name, + props: { + // 选项标签 + labelField: { + type: String, + default: () => option.labelField ?? 'label', + }, + // 选项的值 + valueField: { + type: String, + default: () => option.valueField ?? 'value', + }, + // api 接口 + url: { + type: String, + default: () => option.url ?? '', + }, + // 请求类型 + method: { + type: String, + default: 'GET', + }, + // 选项解析函数 + parseFunc: { + type: String, + default: '', + }, + // 请求参数 + data: { + type: String, + default: '', + }, + // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio + selectType: { + type: String, + default: 'select', + }, + // 是否多选 + multiple: { + type: Boolean, + default: false, + }, + // 是否远程搜索 + remote: { + type: Boolean, + default: false, + }, + // 远程搜索时携带的参数 + remoteField: { + type: String, + default: 'label', + }, + // 返回值类型(用于部门选择器等):id 返回 ID,name 返回名称 + returnType: { + type: String, + default: 'id', + }, + }, + setup(props) { + const attrs = useAttrs(); + const options = ref([]); // 下拉数据 + const loading = ref(false); // 是否正在从远程获取数据 + const queryParam = ref(); // 当前输入的值 + const getOptions = async () => { + options.value = []; + // 接口选择器 + if (isEmpty(props.url)) { + return; + } + + switch (props.method) { + case 'GET': { + let url: string = props.url; + if (props.remote && queryParam.value !== undefined) { + url = url.includes('?') + ? `${url}&${props.remoteField}=${queryParam.value}` + : `${url}?${props.remoteField}=${queryParam.value}`; + } + parseOptions(await requestClient.get(url)); + break; + } + case 'POST': { + const data: any = JSON.parse(props.data); + if (props.remote) { + data[props.remoteField] = queryParam.value; + } + parseOptions(await requestClient.post(props.url, data)); + break; + } + } + }; + + function parseOptions(data: any) { + // 情况一:如果有自定义解析函数优先使用自定义解析 + if (!isEmpty(props.parseFunc)) { + options.value = parseFunc()?.(data); + return; + } + // 情况二:返回的直接是一个列表 + if (Array.isArray(data)) { + parseOptions0(data); + return; + } + // 情况二:返回的是分页数据,尝试读取 list + data = data.list; + if (!!data && Array.isArray(data)) { + parseOptions0(data); + return; + } + // 情况三:不是 yudao-vue-pro 标准返回 + console.warn( + `接口[${props.url}] 返回结果不是 yudao-vue-pro 标准返回建议采用自定义解析函数处理`, + ); + } + + function parseOptions0(data: any[]) { + if (Array.isArray(data)) { + options.value = data.map((item: any) => { + const label = parseExpression(item, props.labelField); + let value = parseExpression(item, props.valueField); + + // 根据 returnType 决定返回值 + // 如果设置了 returnType 为 'name',则返回 label 作为 value + if (props.returnType === 'name') { + value = label; + } + + return { + label, + value, + }; + }); + return; + } + console.warn(`接口[${props.url}] 返回结果不是一个数组`); + } + + function parseFunc() { + let parse: any = null; + if (props.parseFunc) { + // 解析字符串函数 + // eslint-disable-next-line no-new-func + parse = new Function(`return ${props.parseFunc}`)(); + } + return parse; + } + + function parseExpression(data: any, template: string) { + // 检测是否使用了表达式 + if (!template.includes('${')) { + return data[template]; + } + // 正则表达式匹配模板字符串中的 ${...} + const pattern = /\$\{([^}]*)\}/g; + // 使用replace函数配合正则表达式和回调函数来进行替换 + return template.replaceAll(pattern, (_, expr) => { + // expr 是匹配到的 ${} 内的表达式(这里是属性名),从 data 中获取对应的值 + const result = data[expr.trim()]; // 去除前后空白,以防用户输入带空格的属性名 + if (!result) { + console.warn( + `接口选择器选项模版[${template}][${expr.trim()}] 解析值失败结果为[${result}], 请检查属性名称是否存在于接口返回值中,存在则忽略此条!!!`, + ); + } + return result; + }); + } + + const remoteMethod = async (query: any) => { + if (!query) { + return; + } + loading.value = true; + try { + queryParam.value = query; + await getOptions(); + } finally { + loading.value = false; + } + }; + + onMounted(async () => { + await getOptions(); + }); + + const buildSelect = () => { + const { + modelValue, + 'onUpdate:modelValue': onUpdateModelValue, + ...restAttrs + } = attrs; + + if (props.multiple) { + // fix:多写此步是为了解决 multiple 属性问题 + return ( + + ); + } + return ( + + ); + }; + const buildCheckbox = () => { + const { + modelValue, + 'onUpdate:modelValue': onUpdateModelValue, + ...restAttrs + } = attrs; + if (isEmpty(options.value)) { + options.value = [ + { label: '选项1', value: '选项1' }, + { label: '选项2', value: '选项2' }, + ]; + } + return ( + + {options.value.map( + (item: { label: any; value: any }, index: any) => ( + + {item.label} + + ), + )} + + ); + }; + const buildRadio = () => { + const { + modelValue, + 'onUpdate:modelValue': onUpdateModelValue, + ...restAttrs + } = attrs; + if (isEmpty(options.value)) { + options.value = [ + { label: '选项1', value: '选项1' }, + { label: '选项2', value: '选项2' }, + ]; + } + return ( + + {options.value.map( + (item: { label: any; value: any }, index: any) => ( + + {item.label} + + ), + )} + + ); + }; + return () => ( + <> + {(() => { + switch (props.selectType) { + case 'checkbox': { + return buildCheckbox(); + } + case 'radio': { + return buildRadio(); + } + case 'select': { + return buildSelect(); + } + default: { + return buildSelect(); + } + } + })()} + + ); + }, + }); +} diff --git a/apps/web-antd/src/components/form-create/components/use-images-upload.tsx b/apps/web-antd/src/components/form-create/components/use-images-upload.tsx new file mode 100644 index 000000000..a9d0572b5 --- /dev/null +++ b/apps/web-antd/src/components/form-create/components/use-images-upload.tsx @@ -0,0 +1,25 @@ +import { defineComponent } from 'vue'; + +import ImageUpload from '#/components/upload/image-upload.vue'; + +export function useImagesUpload() { + return defineComponent({ + name: 'ImagesUpload', + props: { + multiple: { + type: Boolean, + default: true, + }, + maxNumber: { + type: Number, + default: 5, + }, + }, + setup() { + // TODO: @puhui999:@dhb52 其实还是靠 props 默认参数起作用,没能从 formCreate 传递 + return (props: { maxNumber?: number; multiple?: boolean }) => ( + + ); + }, + }); +} diff --git a/apps/web-antd/src/components/form-create/helpers.ts b/apps/web-antd/src/components/form-create/helpers.ts new file mode 100644 index 000000000..f43b1ce6d --- /dev/null +++ b/apps/web-antd/src/components/form-create/helpers.ts @@ -0,0 +1,253 @@ +import type { Rule } from '@form-create/ant-design-vue'; + +import type { Ref } from 'vue'; + +import type { Menu } from '#/components/form-create/typing'; + +import { isRef, nextTick, onMounted } from 'vue'; + +import formCreate from '@form-create/ant-design-vue'; + +import { apiSelectRule } from '#/components/form-create/rules/data'; + +import { + useDictSelectRule, + useEditorRule, + useSelectRule, + useUploadFileRule, + useUploadImageRule, + useUploadImagesRule, +} from './rules'; + +/** 编码表单 Conf */ +export function encodeConf(designerRef: any) { + // 关联案例:https://gitee.com/yudaocode/yudao-ui-admin-vue3/pulls/834/ + return formCreate.toJson(designerRef.value.getOption()); +} + +/** 解码表单 Conf */ +export function decodeConf(conf: string) { + return formCreate.parseJson(conf); +} + +/** 编码表单 Fields */ +export function encodeFields(designerRef: any) { + const rule = designerRef.value.getRule(); + const fields: string[] = []; + rule.forEach((item: any) => { + fields.push(formCreate.toJson(item)); + }); + return fields; +} + +/** 解码表单 Fields */ +export function decodeFields(fields: string[]) { + const rule: Rule[] = []; + fields.forEach((item) => { + rule.push(formCreate.parseJson(item)); + }); + return rule; +} + +/** 设置表单的 Conf 和 Fields,适用 FcDesigner 场景 */ +export function setConfAndFields( + designerRef: any, + conf: string, + fields: string | string[], +) { + designerRef.value.setOption(decodeConf(conf)); + // 处理 fields 参数类型,确保传入 decodeFields 的是 string[] 类型 + const fieldsArray = Array.isArray(fields) ? fields : [fields]; + designerRef.value.setRule(decodeFields(fieldsArray)); +} + +/** 设置表单的 Conf 和 Fields,适用 form-create 场景 */ +export function setConfAndFields2( + detailPreview: any, + conf: string, + fields: string[], + value?: any, +) { + if (isRef(detailPreview)) { + detailPreview = detailPreview.value; + } + detailPreview.option = decodeConf(conf); + detailPreview.rule = decodeFields(fields); + if (value) { + detailPreview.value = value; + } +} + +export function makeRequiredRule() { + return { + type: 'Required', + field: 'formCreate$required', + title: '是否必填', + }; +} + +export function localeProps( + t: (msg: string) => any, + prefix: string, + rules: any[], +) { + return rules.map((rule: { field: string; title: any }) => { + if (rule.field === 'formCreate$required') { + rule.title = t('props.required') || rule.title; + } else if (rule.field && rule.field !== '_optionType') { + rule.title = t(`components.${prefix}.${rule.field}`) || rule.title; + } + return rule; + }); +} + +/** + * 解析表单组件的 field, title 等字段(递归,如果组件包含子组件) + * + * @param rule 组件的生成规则 https://www.form-create.com/v3/guide/rule + * @param fields 解析后表单组件字段 + * @param parentTitle 如果是子表单,子表单的标题,默认为空 + */ +export function parseFormFields( + rule: Record, + fields: Array> = [], + parentTitle: string = '', +) { + const { type, field, $required, title: tempTitle, children } = rule; + if (field && tempTitle) { + let title = tempTitle; + if (parentTitle) { + title = `${parentTitle}.${tempTitle}`; + } + let required = false; + if ($required) { + required = true; + } + fields.push({ + field, + title, + type, + required, + }); + // TODO 子表单 需要处理子表单字段 + // if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) { + // // 解析子表单的字段 + // rule.props.rule.forEach((item) => { + // parseFields(item, fieldsPermission, title) + // }) + // } + } + if (children && Array.isArray(children)) { + children.forEach((rule) => { + parseFormFields(rule, fields); + }); + } +} + +/** + * 表单设计器增强 hook + * 新增 + * - 文件上传 + * - 单图上传 + * - 多图上传 + * - 字典选择器 + * - 用户选择器 + * - 部门选择器 + * - 富文本 + */ +export async function useFormCreateDesigner(designer: Ref) { + const editorRule = useEditorRule(); + const uploadFileRule = useUploadFileRule(); + const uploadImageRule = useUploadImageRule(); + const uploadImagesRule = useUploadImagesRule(); + + /** 构建表单组件 */ + function buildFormComponents() { + // 移除自带的上传组件规则,使用 uploadFileRule、uploadImgRule、uploadImgsRule 替代 + designer.value?.removeMenuItem('upload'); + // 移除自带的富文本组件规则,使用 editorRule 替代 + designer.value?.removeMenuItem('fc-editor'); + const components = [ + editorRule, + uploadFileRule, + uploadImageRule, + uploadImagesRule, + ]; + components.forEach((component) => { + // 插入组件规则 + designer.value?.addComponent(component); + // 插入拖拽按钮到 `main` 分类下 + designer.value?.appendMenuItem('main', { + icon: component.icon, + name: component.name, + label: component.label, + }); + }); + } + + const userSelectRule = useSelectRule({ + name: 'UserSelect', + label: '用户选择器', + icon: 'icon-eye', + }); + const deptSelectRule = useSelectRule({ + name: 'DeptSelect', + label: '部门选择器', + icon: 'icon-tree', + props: [ + { + type: 'select', + field: 'returnType', + title: '返回值类型', + value: 'id', + options: [ + { label: '部门编号', value: 'id' }, + { label: '部门名称', value: 'name' }, + ], + }, + ], + }); + const dictSelectRule = useDictSelectRule(); + const apiSelectRule0 = useSelectRule({ + name: 'ApiSelect', + label: '接口选择器', + icon: 'icon-json', + props: [...apiSelectRule], + event: ['click', 'change', 'visibleChange', 'clear', 'blur', 'focus'], + }); + + /** 构建系统字段菜单 */ + function buildSystemMenu() { + // 移除自带的下拉选择器组件,使用 currencySelectRule 替代 + // designer.value?.removeMenuItem('select') + // designer.value?.removeMenuItem('radio') + // designer.value?.removeMenuItem('checkbox') + const components = [ + userSelectRule, + deptSelectRule, + dictSelectRule, + apiSelectRule0, + ]; + const menu: Menu = { + name: 'system', + title: '系统字段', + list: components.map((component) => { + // 插入组件规则 + designer.value?.addComponent(component); + // 插入拖拽按钮到 `system` 分类下 + return { + icon: component.icon, + name: component.name, + label: component.label, + }; + }), + }; + designer.value?.addMenu(menu); + } + + onMounted(async () => { + await nextTick(); + buildFormComponents(); + buildSystemMenu(); + }); +} diff --git a/apps/web-antd/src/components/form-create/index.ts b/apps/web-antd/src/components/form-create/index.ts new file mode 100644 index 000000000..2c512158d --- /dev/null +++ b/apps/web-antd/src/components/form-create/index.ts @@ -0,0 +1,3 @@ +export { useApiSelect } from './components/use-api-select'; + +export * from './helpers'; diff --git a/apps/web-antd/src/components/form-create/rules/use-dict-select.ts b/apps/web-antd/src/components/form-create/rules/use-dict-select.ts new file mode 100644 index 000000000..0f041c87b --- /dev/null +++ b/apps/web-antd/src/components/form-create/rules/use-dict-select.ts @@ -0,0 +1,70 @@ +import type { SystemDictTypeApi } from '#/api/system/dict/type'; + +import { onMounted, ref } from 'vue'; + +import { buildUUID, cloneDeep } from '@vben/utils'; + +import { getSimpleDictTypeList } from '#/api/system/dict/type'; +import { + localeProps, + makeRequiredRule, +} from '#/components/form-create/helpers'; +import { selectRule } from '#/components/form-create/rules/data'; + +/** 字典选择器规则,如果规则使用到动态数据则需要单独配置不能使用 useSelectRule */ +export function useDictSelectRule() { + const label = '字典选择器'; + const name = 'DictSelect'; + const rules = cloneDeep(selectRule); + const dictOptions = ref<{ label: string; value: string }[]>([]); // 字典类型下拉数据 + onMounted(async () => { + const data = await getSimpleDictTypeList(); + if (!data || data.length === 0) { + return; + } + dictOptions.value = + data?.map((item: SystemDictTypeApi.DictType) => ({ + label: item.name, + value: item.type, + })) ?? []; + }); + return { + icon: 'icon-descriptions', + label, + name, + rule() { + return { + type: name, + field: buildUUID(), + title: label, + info: '', + $required: false, + modelField: 'value', + }; + }, + props(_: any, { t }: any) { + return localeProps(t, `${name}.props`, [ + makeRequiredRule(), + { + type: 'select', + field: 'dictType', + title: '字典类型', + value: '', + options: dictOptions.value, + }, + { + type: 'select', + field: 'valueType', + title: '字典值类型', + value: 'str', + options: [ + { label: '数字', value: 'int' }, + { label: '字符串', value: 'str' }, + { label: '布尔值', value: 'bool' }, + ], + }, + ...rules, + ]); + }, + }; +} diff --git a/apps/web-antd/src/components/form-create/rules/use-editor-rule.ts b/apps/web-antd/src/components/form-create/rules/use-editor-rule.ts new file mode 100644 index 000000000..dcde682cb --- /dev/null +++ b/apps/web-antd/src/components/form-create/rules/use-editor-rule.ts @@ -0,0 +1,36 @@ +import { buildUUID } from '@vben/utils'; + +import { + localeProps, + makeRequiredRule, +} from '#/components/form-create/helpers'; + +export function useEditorRule() { + const label = '富文本'; + const name = 'Tinymce'; + return { + icon: 'icon-editor', + label, + name, + rule() { + return { + type: name, + field: buildUUID(), + title: label, + info: '', + $required: false, + }; + }, + props(_: any, { t }: any) { + return localeProps(t, `${name}.props`, [ + makeRequiredRule(), + { + type: 'input', + field: 'height', + title: '高度', + }, + { type: 'switch', field: 'readonly', title: '是否只读' }, + ]); + }, + }; +} diff --git a/apps/web-antd/src/components/form-create/rules/use-select-rule.ts b/apps/web-antd/src/components/form-create/rules/use-select-rule.ts new file mode 100644 index 000000000..3efd8c9d5 --- /dev/null +++ b/apps/web-antd/src/components/form-create/rules/use-select-rule.ts @@ -0,0 +1,45 @@ +import type { SelectRuleOption } from '#/components/form-create/typing'; + +import { buildUUID, cloneDeep } from '@vben/utils'; + +import { + localeProps, + makeRequiredRule, +} from '#/components/form-create/helpers'; +import { selectRule } from '#/components/form-create/rules/data'; + +/** + * 通用选择器规则 hook + * + * @param option 规则配置 + */ +export function useSelectRule(option: SelectRuleOption) { + const label = option.label; + const name = option.name; + const rules = cloneDeep(selectRule); + return { + icon: option.icon, + label, + name, + event: option.event, + rule() { + return { + type: name, + field: buildUUID(), + title: label, + info: '', + $required: false, + }; + }, + props(_: any, { t }: any) { + if (!option.props) { + option.props = []; + } + return localeProps(t, `${name}.props`, [ + makeRequiredRule(), + ...option.props, + ...rules, + ]); + }, + }; +} diff --git a/apps/web-antd/src/components/form-create/rules/use-upload-file-rule.ts b/apps/web-antd/src/components/form-create/rules/use-upload-file-rule.ts new file mode 100644 index 000000000..91c21e513 --- /dev/null +++ b/apps/web-antd/src/components/form-create/rules/use-upload-file-rule.ts @@ -0,0 +1,84 @@ +import { buildUUID } from '@vben/utils'; + +import { + localeProps, + makeRequiredRule, +} from '#/components/form-create/helpers'; + +export function useUploadFileRule() { + const label = '文件上传'; + const name = 'FileUpload'; + return { + icon: 'icon-upload', + label, + name, + rule() { + return { + type: name, + field: buildUUID(), + title: label, + info: '', + $required: false, + }; + }, + props(_: any, { t }: any) { + return localeProps(t, `${name}.props`, [ + makeRequiredRule(), + { + type: 'select', + field: 'fileType', + title: '文件类型', + value: ['doc', 'xls', 'ppt', 'txt', 'pdf'], + options: [ + { label: 'doc', value: 'doc' }, + { label: 'xls', value: 'xls' }, + { label: 'ppt', value: 'ppt' }, + { label: 'txt', value: 'txt' }, + { label: 'pdf', value: 'pdf' }, + ], + props: { + mode: 'multiple', + }, + }, + { + type: 'switch', + field: 'autoUpload', + title: '是否在选取文件后立即进行上传', + value: true, + }, + { + type: 'switch', + field: 'drag', + title: '拖拽上传', + value: false, + }, + { + type: 'switch', + field: 'isShowTip', + title: '是否显示提示', + value: true, + }, + { + type: 'inputNumber', + field: 'fileSize', + title: '大小限制(MB)', + value: 5, + props: { min: 0 }, + }, + { + type: 'inputNumber', + field: 'limit', + title: '数量限制', + value: 5, + props: { min: 0 }, + }, + { + type: 'switch', + field: 'disabled', + title: '是否禁用', + value: false, + }, + ]); + }, + }; +} diff --git a/apps/web-antd/src/components/form-create/rules/use-upload-image-rule.ts b/apps/web-antd/src/components/form-create/rules/use-upload-image-rule.ts new file mode 100644 index 000000000..58dd71dc7 --- /dev/null +++ b/apps/web-antd/src/components/form-create/rules/use-upload-image-rule.ts @@ -0,0 +1,93 @@ +import { buildUUID } from '@vben/utils'; + +import { + localeProps, + makeRequiredRule, +} from '#/components/form-create/helpers'; + +export function useUploadImageRule() { + const label = '单图上传'; + const name = 'ImageUpload'; + return { + icon: 'icon-image', + label, + name, + rule() { + return { + type: name, + field: buildUUID(), + title: label, + info: '', + $required: false, + }; + }, + props(_: any, { t }: any) { + return localeProps(t, `${name}.props`, [ + makeRequiredRule(), + { + type: 'switch', + field: 'drag', + title: '拖拽上传', + value: false, + }, + { + type: 'select', + field: 'fileType', + title: '图片类型限制', + value: ['image/jpeg', 'image/png', 'image/gif'], + options: [ + { label: 'image/apng', value: 'image/apng' }, + { label: 'image/bmp', value: 'image/bmp' }, + { label: 'image/gif', value: 'image/gif' }, + { label: 'image/jpeg', value: 'image/jpeg' }, + { label: 'image/pjpeg', value: 'image/pjpeg' }, + { label: 'image/svg+xml', value: 'image/svg+xml' }, + { label: 'image/tiff', value: 'image/tiff' }, + { label: 'image/webp', value: 'image/webp' }, + { label: 'image/x-icon', value: 'image/x-icon' }, + ], + props: { + mode: 'multiple', + }, + }, + { + type: 'inputNumber', + field: 'fileSize', + title: '大小限制(MB)', + value: 5, + props: { min: 0 }, + }, + { + type: 'input', + field: 'height', + title: '组件高度', + value: '150px', + }, + { + type: 'input', + field: 'width', + title: '组件宽度', + value: '150px', + }, + { + type: 'input', + field: 'borderradius', + title: '组件边框圆角', + value: '8px', + }, + { + type: 'switch', + field: 'disabled', + title: '是否显示删除按钮', + value: true, + }, + { + type: 'switch', + field: 'showBtnText', + title: '是否显示按钮文字', + value: true, + }, + ]); + }, + }; +} diff --git a/apps/web-antd/src/components/form-create/rules/use-upload-images-rule.ts b/apps/web-antd/src/components/form-create/rules/use-upload-images-rule.ts new file mode 100644 index 000000000..b1559ac3f --- /dev/null +++ b/apps/web-antd/src/components/form-create/rules/use-upload-images-rule.ts @@ -0,0 +1,89 @@ +import { buildUUID } from '@vben/utils'; + +import { + localeProps, + makeRequiredRule, +} from '#/components/form-create/helpers'; + +export function useUploadImagesRule() { + const label = '多图上传'; + const name = 'ImagesUpload'; + return { + icon: 'icon-image', + label, + name, + rule() { + return { + type: name, + field: buildUUID(), + title: label, + info: '', + $required: false, + }; + }, + props(_: any, { t }: any) { + return localeProps(t, `${name}.props`, [ + makeRequiredRule(), + { + type: 'switch', + field: 'drag', + title: '拖拽上传', + value: false, + }, + { + type: 'select', + field: 'fileType', + title: '图片类型限制', + value: ['image/jpeg', 'image/png', 'image/gif'], + options: [ + { label: 'image/apng', value: 'image/apng' }, + { label: 'image/bmp', value: 'image/bmp' }, + { label: 'image/gif', value: 'image/gif' }, + { label: 'image/jpeg', value: 'image/jpeg' }, + { label: 'image/pjpeg', value: 'image/pjpeg' }, + { label: 'image/svg+xml', value: 'image/svg+xml' }, + { label: 'image/tiff', value: 'image/tiff' }, + { label: 'image/webp', value: 'image/webp' }, + { label: 'image/x-icon', value: 'image/x-icon' }, + ], + props: { + mode: 'multiple', + maxNumber: 5, + }, + }, + { + type: 'inputNumber', + field: 'fileSize', + title: '大小限制(MB)', + value: 5, + props: { min: 0 }, + }, + { + type: 'inputNumber', + field: 'limit', + title: '数量限制', + value: 5, + props: { min: 0 }, + }, + { + type: 'input', + field: 'height', + title: '组件高度', + value: '150px', + }, + { + type: 'input', + field: 'width', + title: '组件宽度', + value: '150px', + }, + { + type: 'input', + field: 'borderradius', + title: '组件边框圆角', + value: '8px', + }, + ]); + }, + }; +} diff --git a/apps/web-antd/src/components/form-create/typing.ts b/apps/web-antd/src/components/form-create/typing.ts new file mode 100644 index 000000000..35c1a39fc --- /dev/null +++ b/apps/web-antd/src/components/form-create/typing.ts @@ -0,0 +1,39 @@ +/** 数据字典 Select 选择器组件 Props 类型 */ +export interface DictSelectProps { + dictType: string; // 字典类型 + valueType?: 'bool' | 'int' | 'str'; // 字典值类型 + selectType?: 'checkbox' | 'radio' | 'select'; // 选择器类型,下拉框 select、多选框 checkbox、单选框 radio + formCreateInject?: any; +} + +/** 左侧拖拽按钮 */ +export interface MenuItem { + label: string; + name: string; + icon: string; +} + +/** 左侧拖拽按钮分类 */ +export interface Menu { + title: string; + name: string; + list: MenuItem[]; +} + +/** 通用 API 下拉组件 Props 类型 */ +export interface ApiSelectProps { + name: string; // 组件名称 + labelField?: string; // 选项标签 + valueField?: string; // 选项的值 + url?: string; // url 接口 + isDict?: boolean; // 是否字典选择器 +} + +/** 选择组件规则配置类型 */ +export interface SelectRuleOption { + label: string; // label 名称 + name: string; // 组件名称 + icon: string; // 组件图标 + props?: any[]; // 组件规则 + event?: any[]; // 事件配置 +} diff --git a/apps/web-antd/src/components/operate-log/index.ts b/apps/web-antd/src/components/operate-log/index.ts new file mode 100644 index 000000000..cf38b5e70 --- /dev/null +++ b/apps/web-antd/src/components/operate-log/index.ts @@ -0,0 +1,3 @@ +export { default as OperateLog } from './operate-log.vue'; + +export type { OperateLogProps } from './typing'; diff --git a/apps/web-antd/src/components/operate-log/operate-log.vue b/apps/web-antd/src/components/operate-log/operate-log.vue new file mode 100644 index 000000000..d1ae60112 --- /dev/null +++ b/apps/web-antd/src/components/operate-log/operate-log.vue @@ -0,0 +1,51 @@ + + diff --git a/apps/web-antd/src/components/select-modal/user-select-modal.vue b/apps/web-antd/src/components/select-modal/user-select-modal.vue new file mode 100644 index 000000000..77ef98155 --- /dev/null +++ b/apps/web-antd/src/components/select-modal/user-select-modal.vue @@ -0,0 +1,546 @@ + + + + + diff --git a/apps/web-antd/src/components/shortcut-date-range-picker/index.ts b/apps/web-antd/src/components/shortcut-date-range-picker/index.ts new file mode 100644 index 000000000..a3ee51539 --- /dev/null +++ b/apps/web-antd/src/components/shortcut-date-range-picker/index.ts @@ -0,0 +1 @@ +export { default as ShortcutDateRangePicker } from './shortcut-date-range-picker.vue'; diff --git a/apps/web-antd/src/components/shortcut-date-range-picker/shortcut-date-range-picker.vue b/apps/web-antd/src/components/shortcut-date-range-picker/shortcut-date-range-picker.vue new file mode 100644 index 000000000..5a3ce0526 --- /dev/null +++ b/apps/web-antd/src/components/shortcut-date-range-picker/shortcut-date-range-picker.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-antd/src/components/table-action/icons.ts b/apps/web-antd/src/components/table-action/icons.ts new file mode 100644 index 000000000..578265f4c --- /dev/null +++ b/apps/web-antd/src/components/table-action/icons.ts @@ -0,0 +1,16 @@ +export const ACTION_ICON = { + DOWNLOAD: 'lucide:download', + UPLOAD: 'lucide:upload', + ADD: 'lucide:plus', + EDIT: 'lucide:edit', + DELETE: 'lucide:trash-2', + REFRESH: 'lucide:refresh-cw', + SEARCH: 'lucide:search', + FILTER: 'lucide:filter', + MORE: 'lucide:ellipsis-vertical', + VIEW: 'lucide:eye', + COPY: 'lucide:copy', + CLOSE: 'lucide:x', + BOOK: 'lucide:book', + AUDIT: 'lucide:file-check', +}; diff --git a/apps/web-antd/src/components/table-action/table-action.vue b/apps/web-antd/src/components/table-action/table-action.vue new file mode 100644 index 000000000..679f6fcaa --- /dev/null +++ b/apps/web-antd/src/components/table-action/table-action.vue @@ -0,0 +1,278 @@ + + + + + + diff --git a/apps/web-antd/src/components/tinymce/editor.vue b/apps/web-antd/src/components/tinymce/editor.vue new file mode 100644 index 000000000..722073cf7 --- /dev/null +++ b/apps/web-antd/src/components/tinymce/editor.vue @@ -0,0 +1,344 @@ + + + + + + diff --git a/apps/web-antd/src/components/tinymce/img-upload.vue b/apps/web-antd/src/components/tinymce/img-upload.vue new file mode 100644 index 000000000..e5d47bbc1 --- /dev/null +++ b/apps/web-antd/src/components/tinymce/img-upload.vue @@ -0,0 +1,84 @@ + + + + diff --git a/apps/web-antd/src/components/upload/file-upload.vue b/apps/web-antd/src/components/upload/file-upload.vue new file mode 100644 index 000000000..b2a800785 --- /dev/null +++ b/apps/web-antd/src/components/upload/file-upload.vue @@ -0,0 +1,344 @@ + + + + + diff --git a/apps/web-antd/src/components/upload/image-upload.vue b/apps/web-antd/src/components/upload/image-upload.vue new file mode 100644 index 000000000..fe962f538 --- /dev/null +++ b/apps/web-antd/src/components/upload/image-upload.vue @@ -0,0 +1,335 @@ + + + + + diff --git a/apps/web-antd/src/components/upload/input-upload.vue b/apps/web-antd/src/components/upload/input-upload.vue new file mode 100644 index 000000000..66495467a --- /dev/null +++ b/apps/web-antd/src/components/upload/input-upload.vue @@ -0,0 +1,74 @@ + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/conversation/list.vue b/apps/web-antd/src/views/ai/chat/index/modules/conversation/list.vue new file mode 100644 index 000000000..f150e62f5 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/conversation/list.vue @@ -0,0 +1,435 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/conversation/update-form.vue b/apps/web-antd/src/views/ai/chat/index/modules/conversation/update-form.vue new file mode 100644 index 000000000..fda49d734 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/conversation/update-form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/file-upload.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/file-upload.vue new file mode 100644 index 000000000..3d5e27fb0 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/file-upload.vue @@ -0,0 +1,304 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/files.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/files.vue new file mode 100644 index 000000000..ed163a026 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/files.vue @@ -0,0 +1,53 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/knowledge.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/knowledge.vue new file mode 100644 index 000000000..960188c04 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/knowledge.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/list-empty.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/list-empty.vue new file mode 100644 index 000000000..f97a054b6 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/list-empty.vue @@ -0,0 +1,37 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/list.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/list.vue new file mode 100644 index 000000000..a841ee29b --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/list.vue @@ -0,0 +1,243 @@ + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/loading.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/loading.vue new file mode 100644 index 000000000..5928a19a1 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/loading.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/new-conversation.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/new-conversation.vue new file mode 100644 index 000000000..41c08ce64 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/new-conversation.vue @@ -0,0 +1,23 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/reasoning.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/reasoning.vue new file mode 100644 index 000000000..de3175daa --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/reasoning.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/message/web-search.vue b/apps/web-antd/src/views/ai/chat/index/modules/message/web-search.vue new file mode 100644 index 000000000..d48a94c24 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/message/web-search.vue @@ -0,0 +1,173 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/role/category-list.vue b/apps/web-antd/src/views/ai/chat/index/modules/role/category-list.vue new file mode 100644 index 000000000..91f6d6409 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/role/category-list.vue @@ -0,0 +1,43 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/role/list.vue b/apps/web-antd/src/views/ai/chat/index/modules/role/list.vue new file mode 100644 index 000000000..544dfb082 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/role/list.vue @@ -0,0 +1,126 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/index/modules/role/repository.vue b/apps/web-antd/src/views/ai/chat/index/modules/role/repository.vue new file mode 100644 index 000000000..e9236a6a5 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/index/modules/role/repository.vue @@ -0,0 +1,249 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/manager/data.ts b/apps/web-antd/src/views/ai/chat/manager/data.ts new file mode 100644 index 000000000..f8dcb1fa7 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/manager/data.ts @@ -0,0 +1,223 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { DICT_TYPE } from '@vben/constants'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 列表的搜索表单 */ +export function useGridFormSchemaConversation(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + allowClear: true, + }, + }, + { + fieldName: 'title', + label: '聊天标题', + component: 'Input', + componentProps: { + placeholder: '请输入聊天标题', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumnsConversation(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '对话编号', + fixed: 'left', + minWidth: 180, + }, + { + field: 'title', + title: '对话标题', + minWidth: 180, + fixed: 'left', + }, + { + title: '用户', + minWidth: 180, + field: 'userId', + formatter: ({ cellValue }) => { + if (cellValue === 0) { + return '系统'; + } + return userList.find((user) => user.id === cellValue)?.nickname || '-'; + }, + }, + { + field: 'roleName', + title: '角色', + minWidth: 180, + }, + { + field: 'model', + title: '模型标识', + minWidth: 180, + }, + { + field: 'messageCount', + title: '消息数', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'temperature', + title: '温度参数', + minWidth: 80, + }, + { + title: '回复数 Token 数', + field: 'maxTokens', + minWidth: 120, + }, + { + title: '上下文数量', + field: 'maxContexts', + minWidth: 120, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchemaMessage(): VbenFormSchema[] { + return [ + { + fieldName: 'conversationId', + label: '对话编号', + component: 'Input', + componentProps: { + placeholder: '请输入对话编号', + allowClear: true, + }, + }, + { + fieldName: 'userId', + label: '用户编号', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择用户编号', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumnsMessage(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '消息编号', + fixed: 'left', + minWidth: 180, + }, + { + field: 'conversationId', + title: '对话编号', + minWidth: 180, + fixed: 'left', + }, + { + title: '用户', + minWidth: 180, + field: 'userId', + formatter: ({ cellValue }) => + userList.find((user) => user.id === cellValue)?.nickname || '-', + }, + { + field: 'roleName', + title: '角色', + minWidth: 180, + }, + { + field: 'type', + title: '消息类型', + minWidth: 100, + }, + { + field: 'model', + title: '模型标识', + minWidth: 180, + }, + { + field: 'content', + title: '消息内容', + minWidth: 300, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'replyId', + title: '回复消息编号', + minWidth: 180, + }, + { + title: '携带上下文', + field: 'useContext', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + minWidth: 100, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-antd/src/views/ai/chat/manager/index.vue b/apps/web-antd/src/views/ai/chat/manager/index.vue new file mode 100644 index 000000000..69c4e844b --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/manager/index.vue @@ -0,0 +1,29 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/manager/modules/conversation-list.vue b/apps/web-antd/src/views/ai/chat/manager/modules/conversation-list.vue new file mode 100644 index 000000000..6bfe70216 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/manager/modules/conversation-list.vue @@ -0,0 +1,94 @@ + + + diff --git a/apps/web-antd/src/views/ai/chat/manager/modules/message-list.vue b/apps/web-antd/src/views/ai/chat/manager/modules/message-list.vue new file mode 100644 index 000000000..bf6bd5747 --- /dev/null +++ b/apps/web-antd/src/views/ai/chat/manager/modules/message-list.vue @@ -0,0 +1,91 @@ + + + diff --git a/apps/web-antd/src/views/ai/image/index/index.vue b/apps/web-antd/src/views/ai/image/index/index.vue new file mode 100644 index 000000000..16fdb5ba6 --- /dev/null +++ b/apps/web-antd/src/views/ai/image/index/index.vue @@ -0,0 +1,131 @@ + + + diff --git a/apps/web-antd/src/views/ai/image/index/modules/card.vue b/apps/web-antd/src/views/ai/image/index/modules/card.vue new file mode 100644 index 000000000..57b12e655 --- /dev/null +++ b/apps/web-antd/src/views/ai/image/index/modules/card.vue @@ -0,0 +1,133 @@ + + diff --git a/apps/web-antd/src/views/ai/image/index/modules/common/index.vue b/apps/web-antd/src/views/ai/image/index/modules/common/index.vue new file mode 100644 index 000000000..bd1098775 --- /dev/null +++ b/apps/web-antd/src/views/ai/image/index/modules/common/index.vue @@ -0,0 +1,218 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/conversation/list.vue b/apps/web-ele/src/views/ai/chat/index/modules/conversation/list.vue new file mode 100644 index 000000000..028055456 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/conversation/list.vue @@ -0,0 +1,442 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/conversation/update-form.vue b/apps/web-ele/src/views/ai/chat/index/modules/conversation/update-form.vue new file mode 100644 index 000000000..6a7ae84e4 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/conversation/update-form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/file-upload.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/file-upload.vue new file mode 100644 index 000000000..eff1b57c2 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/file-upload.vue @@ -0,0 +1,304 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/files.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/files.vue new file mode 100644 index 000000000..ed163a026 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/files.vue @@ -0,0 +1,53 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/knowledge.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/knowledge.vue new file mode 100644 index 000000000..6feff4184 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/knowledge.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/list-empty.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/list-empty.vue new file mode 100644 index 000000000..f97a054b6 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/list-empty.vue @@ -0,0 +1,37 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/list.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/list.vue new file mode 100644 index 000000000..36630d152 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/list.vue @@ -0,0 +1,243 @@ + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/loading.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/loading.vue new file mode 100644 index 000000000..a5e076532 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/loading.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/new-conversation.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/new-conversation.vue new file mode 100644 index 000000000..339af1704 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/new-conversation.vue @@ -0,0 +1,25 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/reasoning.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/reasoning.vue new file mode 100644 index 000000000..de3175daa --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/reasoning.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/message/web-search.vue b/apps/web-ele/src/views/ai/chat/index/modules/message/web-search.vue new file mode 100644 index 000000000..d48a94c24 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/message/web-search.vue @@ -0,0 +1,173 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/role/category-list.vue b/apps/web-ele/src/views/ai/chat/index/modules/role/category-list.vue new file mode 100644 index 000000000..96f32a1b7 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/role/category-list.vue @@ -0,0 +1,43 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/role/list.vue b/apps/web-ele/src/views/ai/chat/index/modules/role/list.vue new file mode 100644 index 000000000..a3f3e4035 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/role/list.vue @@ -0,0 +1,123 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/index/modules/role/repository.vue b/apps/web-ele/src/views/ai/chat/index/modules/role/repository.vue new file mode 100644 index 000000000..20bc9e416 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/index/modules/role/repository.vue @@ -0,0 +1,263 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/manager/data.ts b/apps/web-ele/src/views/ai/chat/manager/data.ts new file mode 100644 index 000000000..266d44ce6 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/manager/data.ts @@ -0,0 +1,223 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { DICT_TYPE } from '@vben/constants'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 列表的搜索表单 */ +export function useGridFormSchemaConversation(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + clearable: true, + }, + }, + { + fieldName: 'title', + label: '聊天标题', + component: 'Input', + componentProps: { + placeholder: '请输入聊天标题', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumnsConversation(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '对话编号', + fixed: 'left', + minWidth: 180, + }, + { + field: 'title', + title: '对话标题', + minWidth: 180, + fixed: 'left', + }, + { + title: '用户', + minWidth: 180, + field: 'userId', + formatter: ({ cellValue }) => { + if (cellValue === 0) { + return '系统'; + } + return userList.find((user) => user.id === cellValue)?.nickname || '-'; + }, + }, + { + field: 'roleName', + title: '角色', + minWidth: 180, + }, + { + field: 'model', + title: '模型标识', + minWidth: 180, + }, + { + field: 'messageCount', + title: '消息数', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'temperature', + title: '温度参数', + minWidth: 80, + }, + { + title: '回复数 Token 数', + field: 'maxTokens', + minWidth: 120, + }, + { + title: '上下文数量', + field: 'maxContexts', + minWidth: 120, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchemaMessage(): VbenFormSchema[] { + return [ + { + fieldName: 'conversationId', + label: '对话编号', + component: 'Input', + componentProps: { + placeholder: '请输入对话编号', + clearable: true, + }, + }, + { + fieldName: 'userId', + label: '用户编号', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择用户编号', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumnsMessage(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '消息编号', + fixed: 'left', + minWidth: 180, + }, + { + field: 'conversationId', + title: '对话编号', + minWidth: 180, + fixed: 'left', + }, + { + title: '用户', + minWidth: 180, + field: 'userId', + formatter: ({ cellValue }) => + userList.find((user) => user.id === cellValue)?.nickname || '-', + }, + { + field: 'roleName', + title: '角色', + minWidth: 180, + }, + { + field: 'type', + title: '消息类型', + minWidth: 100, + }, + { + field: 'model', + title: '模型标识', + minWidth: 180, + }, + { + field: 'content', + title: '消息内容', + minWidth: 300, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'replyId', + title: '回复消息编号', + minWidth: 180, + }, + { + title: '携带上下文', + field: 'useContext', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + minWidth: 100, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/chat/manager/index.vue b/apps/web-ele/src/views/ai/chat/manager/index.vue new file mode 100644 index 000000000..e87c3d30f --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/manager/index.vue @@ -0,0 +1,29 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/manager/modules/conversation-list.vue b/apps/web-ele/src/views/ai/chat/manager/modules/conversation-list.vue new file mode 100644 index 000000000..b6ea3dcc9 --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/manager/modules/conversation-list.vue @@ -0,0 +1,93 @@ + + + diff --git a/apps/web-ele/src/views/ai/chat/manager/modules/message-list.vue b/apps/web-ele/src/views/ai/chat/manager/modules/message-list.vue new file mode 100644 index 000000000..f5682141b --- /dev/null +++ b/apps/web-ele/src/views/ai/chat/manager/modules/message-list.vue @@ -0,0 +1,90 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/index/index.vue b/apps/web-ele/src/views/ai/image/index/index.vue new file mode 100644 index 000000000..1a2f84272 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/index/index.vue @@ -0,0 +1,128 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/index/modules/card.vue b/apps/web-ele/src/views/ai/image/index/modules/card.vue new file mode 100644 index 000000000..91018c742 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/index/modules/card.vue @@ -0,0 +1,136 @@ + + diff --git a/apps/web-ele/src/views/ai/image/index/modules/common/index.vue b/apps/web-ele/src/views/ai/image/index/modules/common/index.vue new file mode 100644 index 000000000..c0197e423 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/index/modules/common/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/index/modules/dall3/index.vue b/apps/web-ele/src/views/ai/image/index/modules/dall3/index.vue new file mode 100644 index 000000000..299b93fd4 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/index/modules/dall3/index.vue @@ -0,0 +1,260 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/index/modules/detail.vue b/apps/web-ele/src/views/ai/image/index/modules/detail.vue new file mode 100644 index 000000000..7959a44c1 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/index/modules/detail.vue @@ -0,0 +1,206 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/index/modules/list.vue b/apps/web-ele/src/views/ai/image/index/modules/list.vue new file mode 100644 index 000000000..903654965 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/index/modules/list.vue @@ -0,0 +1,222 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/index/modules/midjourney/index.vue b/apps/web-ele/src/views/ai/image/index/modules/midjourney/index.vue new file mode 100644 index 000000000..5c847baa0 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/index/modules/midjourney/index.vue @@ -0,0 +1,257 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/index/modules/stable-diffusion/index.vue b/apps/web-ele/src/views/ai/image/index/modules/stable-diffusion/index.vue new file mode 100644 index 000000000..5d7914f33 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/index/modules/stable-diffusion/index.vue @@ -0,0 +1,309 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/manager/data.ts b/apps/web-ele/src/views/ai/image/manager/data.ts new file mode 100644 index 000000000..68cc3c4ee --- /dev/null +++ b/apps/web-ele/src/views/ai/image/manager/data.ts @@ -0,0 +1,180 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +let userList: SystemUserApi.User[] = []; +async function getUserData() { + userList = await getSimpleUserList(); +} + +getUserData(); + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择用户编号', + clearable: true, + }, + }, + { + fieldName: 'platform', + label: '平台', + component: 'Select', + componentProps: { + placeholder: '请选择平台', + clearable: true, + options: getDictOptions(DICT_TYPE.AI_PLATFORM, 'string'), + }, + }, + { + fieldName: 'status', + label: '绘画状态', + component: 'Select', + componentProps: { + placeholder: '请选择绘画状态', + clearable: true, + options: getDictOptions(DICT_TYPE.AI_IMAGE_STATUS, 'number'), + }, + }, + { + fieldName: 'publicStatus', + label: '是否发布', + component: 'Select', + componentProps: { + placeholder: '请选择是否发布', + clearable: true, + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onPublicStatusChange?: ( + newStatus: boolean, + row: any, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 180, + fixed: 'left', + }, + { + field: 'picUrl', + title: '图片', + minWidth: 110, + fixed: 'left', + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'userId', + title: '用户', + minWidth: 180, + formatter: ({ cellValue }) => + userList.find((user) => user.id === cellValue)?.nickname || '-', + }, + { + field: 'platform', + title: '平台', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_PLATFORM }, + }, + }, + { + field: 'model', + title: '模型', + minWidth: 180, + }, + { + field: 'status', + title: '绘画状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_IMAGE_STATUS }, + }, + }, + { + minWidth: 100, + title: '是否发布', + field: 'publicStatus', + align: 'center', + cellRender: { + attrs: { beforeChange: onPublicStatusChange }, + name: 'CellSwitch', + props: { + activeValue: true, + inactiveValue: false, + }, + }, + }, + { + field: 'prompt', + title: '提示词', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'width', + title: '宽度', + minWidth: 180, + }, + { + field: 'height', + title: '高度', + minWidth: 180, + }, + { + field: 'errorMessage', + title: '错误信息', + minWidth: 180, + }, + { + field: 'taskId', + title: '任务编号', + minWidth: 180, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/image/manager/index.vue b/apps/web-ele/src/views/ai/image/manager/index.vue new file mode 100644 index 000000000..72830e7a9 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/manager/index.vue @@ -0,0 +1,116 @@ + + + diff --git a/apps/web-ele/src/views/ai/image/square/index.vue b/apps/web-ele/src/views/ai/image/square/index.vue new file mode 100644 index 000000000..636aaded5 --- /dev/null +++ b/apps/web-ele/src/views/ai/image/square/index.vue @@ -0,0 +1,90 @@ + + diff --git a/apps/web-ele/src/views/ai/knowledge/document/data.ts b/apps/web-ele/src/views/ai/knowledge/document/data.ts new file mode 100644 index 000000000..668fb09c7 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/document/data.ts @@ -0,0 +1,180 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { AiKnowledgeDocumentApi } from '#/api/ai/knowledge/document'; + +import { AiModelTypeEnum, CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getModelSimpleList } from '#/api/ai/model/model'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '知识库名称', + rules: 'required', + }, + { + fieldName: 'description', + label: '知识库描述', + component: 'Textarea', + componentProps: { + rows: 3, + placeholder: '请输入知识库描述', + }, + }, + { + component: 'ApiSelect', + fieldName: 'embeddingModelId', + label: '向量模型', + componentProps: { + api: () => getModelSimpleList(AiModelTypeEnum.EMBEDDING), + labelField: 'name', + valueField: 'id', + allowClear: true, + placeholder: '请选择向量模型', + }, + rules: 'required', + }, + { + fieldName: 'topK', + label: '检索 topK', + component: 'InputNumber', + componentProps: { + placeholder: '请输入检索 topK', + min: 0, + max: 10, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'similarityThreshold', + label: '检索相似度阈值', + component: 'InputNumber', + componentProps: { + placeholder: '请输入检索相似度阈值', + min: 0, + max: 1, + step: 0.01, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '是否启用', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '文件名称', + component: 'Input', + componentProps: { + placeholder: '请输入文件名称', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '是否启用', + component: 'Select', + componentProps: { + placeholder: '请选择是否启用', + allowClear: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onStatusChange?: ( + newStatus: number, + row: AiKnowledgeDocumentApi.KnowledgeDocument, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '文档编号', + minWidth: 100, + }, + { + field: 'name', + title: '文件名称', + minWidth: 200, + }, + { + field: 'contentLength', + title: '字符数', + minWidth: 100, + }, + { + field: 'tokens', + title: 'Token 数', + minWidth: 100, + }, + { + field: 'segmentMaxTokens', + title: '分片最大 Token 数', + minWidth: 150, + }, + { + field: 'retrievalCount', + title: '召回次数', + minWidth: 100, + }, + { + field: 'status', + title: '是否启用', + minWidth: 100, + align: 'center', + cellRender: { + attrs: { beforeChange: onStatusChange }, + name: 'CellSwitch', + props: { + activeValue: CommonStatusEnum.ENABLE, + inactiveValue: CommonStatusEnum.DISABLE, + }, + }, + }, + { + field: 'createTime', + title: '上传时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + minWidth: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/knowledge/document/form/index.vue b/apps/web-ele/src/views/ai/knowledge/document/form/index.vue new file mode 100644 index 000000000..1ef242912 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/document/form/index.vue @@ -0,0 +1,200 @@ + + + diff --git a/apps/web-ele/src/views/ai/knowledge/document/form/modules/process-step.vue b/apps/web-ele/src/views/ai/knowledge/document/form/modules/process-step.vue new file mode 100644 index 000000000..e790c7344 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/document/form/modules/process-step.vue @@ -0,0 +1,158 @@ + + + diff --git a/apps/web-ele/src/views/ai/knowledge/document/form/modules/split-step.vue b/apps/web-ele/src/views/ai/knowledge/document/form/modules/split-step.vue new file mode 100644 index 000000000..e2c97818b --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/document/form/modules/split-step.vue @@ -0,0 +1,286 @@ + + + diff --git a/apps/web-ele/src/views/ai/knowledge/document/form/modules/upload-step.vue b/apps/web-ele/src/views/ai/knowledge/document/form/modules/upload-step.vue new file mode 100644 index 000000000..456aafd58 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/document/form/modules/upload-step.vue @@ -0,0 +1,270 @@ + + + diff --git a/apps/web-ele/src/views/ai/knowledge/document/index.vue b/apps/web-ele/src/views/ai/knowledge/document/index.vue new file mode 100644 index 000000000..df0fc7142 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/document/index.vue @@ -0,0 +1,191 @@ + + + diff --git a/apps/web-ele/src/views/ai/knowledge/knowledge/data.ts b/apps/web-ele/src/views/ai/knowledge/knowledge/data.ts new file mode 100644 index 000000000..1e69f2c85 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/knowledge/data.ts @@ -0,0 +1,172 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { AiModelTypeEnum, CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getModelSimpleList } from '#/api/ai/model/model'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '知识库名称', + componentProps: { + placeholder: '请输入知识库名称', + }, + rules: 'required', + }, + { + fieldName: 'description', + label: '知识库描述', + component: 'Textarea', + componentProps: { + rows: 3, + placeholder: '请输入知识库描述', + }, + }, + { + component: 'ApiSelect', + fieldName: 'embeddingModelId', + label: '向量模型', + componentProps: { + api: () => getModelSimpleList(AiModelTypeEnum.EMBEDDING), + labelField: 'name', + valueField: 'id', + allowClear: true, + placeholder: '请选择向量模型', + }, + rules: 'required', + }, + { + fieldName: 'topK', + label: '检索 topK', + component: 'InputNumber', + componentProps: { + placeholder: '请输入检索 topK', + min: 0, + max: 10, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'similarityThreshold', + label: '检索相似度阈值', + component: 'InputNumber', + componentProps: { + placeholder: '请输入检索相似度阈值', + min: 0, + max: 1, + step: 0.01, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '是否启用', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '知识库名称', + component: 'Input', + componentProps: { + placeholder: '请输入知识库名称', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '是否启用', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择是否启用', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '知识库名称', + minWidth: 150, + }, + { + field: 'description', + title: '知识库描述', + minWidth: 200, + }, + { + field: 'embeddingModel', + title: '向量化模型', + minWidth: 150, + }, + { + field: 'status', + title: '是否启用', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/knowledge/knowledge/index.vue b/apps/web-ele/src/views/ai/knowledge/knowledge/index.vue new file mode 100644 index 000000000..55cfbccd0 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/knowledge/index.vue @@ -0,0 +1,166 @@ + + + diff --git a/apps/web-ele/src/views/ai/knowledge/knowledge/modules/form.vue b/apps/web-ele/src/views/ai/knowledge/knowledge/modules/form.vue new file mode 100644 index 000000000..5daf71447 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/knowledge/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/ai/knowledge/knowledge/retrieval/index.vue b/apps/web-ele/src/views/ai/knowledge/knowledge/retrieval/index.vue new file mode 100644 index 000000000..1af68dfd4 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/knowledge/retrieval/index.vue @@ -0,0 +1,214 @@ + + diff --git a/apps/web-ele/src/views/ai/knowledge/segment/data.ts b/apps/web-ele/src/views/ai/knowledge/segment/data.ts new file mode 100644 index 000000000..33cb64b01 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/segment/data.ts @@ -0,0 +1,130 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { AiKnowledgeSegmentApi } from '#/api/ai/knowledge/segment'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'documentId', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'content', + label: '切片内容', + component: 'Textarea', + componentProps: { + placeholder: '请输入切片内容', + rows: 6, + showCount: true, + }, + rules: 'required', + }, + ]; +} +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'documentId', + label: '文档编号', + component: 'Input', + componentProps: { + placeholder: '请输入文档编号', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '是否启用', + component: 'Select', + componentProps: { + placeholder: '请选择是否启用', + allowClear: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onStatusChange?: ( + newStatus: number, + row: AiKnowledgeSegmentApi.KnowledgeSegment, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '分段编号', + minWidth: 100, + }, + { + type: 'expand', + width: 40, + slots: { content: 'expand_content' }, + }, + { + field: 'content', + title: '切片内容', + minWidth: 250, + }, + { + field: 'contentLength', + title: '字符数', + minWidth: 100, + }, + { + field: 'tokens', + title: 'token 数量', + minWidth: 120, + }, + { + field: 'retrievalCount', + title: '召回次数', + minWidth: 100, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + align: 'center', + cellRender: { + attrs: { beforeChange: onStatusChange }, + name: 'CellSwitch', + props: { + activeValue: CommonStatusEnum.ENABLE, + inactiveValue: CommonStatusEnum.DISABLE, + }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/knowledge/segment/index.vue b/apps/web-ele/src/views/ai/knowledge/segment/index.vue new file mode 100644 index 000000000..980a904e4 --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/segment/index.vue @@ -0,0 +1,171 @@ + + + diff --git a/apps/web-ele/src/views/ai/knowledge/segment/modules/form.vue b/apps/web-ele/src/views/ai/knowledge/segment/modules/form.vue new file mode 100644 index 000000000..1b4b1354e --- /dev/null +++ b/apps/web-ele/src/views/ai/knowledge/segment/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/ai/mindmap/index/index.vue b/apps/web-ele/src/views/ai/mindmap/index/index.vue new file mode 100644 index 000000000..0bdb46eca --- /dev/null +++ b/apps/web-ele/src/views/ai/mindmap/index/index.vue @@ -0,0 +1,99 @@ + + + diff --git a/apps/web-ele/src/views/ai/mindmap/index/modules/left.vue b/apps/web-ele/src/views/ai/mindmap/index/modules/left.vue new file mode 100644 index 000000000..f2bd48c3d --- /dev/null +++ b/apps/web-ele/src/views/ai/mindmap/index/modules/left.vue @@ -0,0 +1,75 @@ + + diff --git a/apps/web-ele/src/views/ai/mindmap/index/modules/right.vue b/apps/web-ele/src/views/ai/mindmap/index/modules/right.vue new file mode 100644 index 000000000..5adc66aaf --- /dev/null +++ b/apps/web-ele/src/views/ai/mindmap/index/modules/right.vue @@ -0,0 +1,215 @@ + + + + + diff --git a/apps/web-ele/src/views/ai/mindmap/manager/data.ts b/apps/web-ele/src/views/ai/mindmap/manager/data.ts new file mode 100644 index 000000000..967981d40 --- /dev/null +++ b/apps/web-ele/src/views/ai/mindmap/manager/data.ts @@ -0,0 +1,97 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择用户', + allowClear: true, + }, + }, + { + fieldName: 'prompt', + label: '提示词', + component: 'Input', + componentProps: { + placeholder: '请输入提示词', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 180, + fixed: 'left', + }, + { + field: 'userId', + title: '用户', + minWidth: 180, + formatter: ({ cellValue }) => + userList.find((user) => user.id === cellValue)?.nickname || '-', + }, + { + field: 'prompt', + title: '提示词', + minWidth: 180, + }, + { + field: 'generatedContent', + title: '思维导图', + minWidth: 300, + }, + { + field: 'model', + title: '模型', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'errorMessage', + title: '错误信息', + minWidth: 180, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/mindmap/manager/index.vue b/apps/web-ele/src/views/ai/mindmap/manager/index.vue new file mode 100644 index 000000000..9684dfc51 --- /dev/null +++ b/apps/web-ele/src/views/ai/mindmap/manager/index.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-ele/src/views/ai/model/apiKey/data.ts b/apps/web-ele/src/views/ai/model/apiKey/data.ts new file mode 100644 index 000000000..1c307ed55 --- /dev/null +++ b/apps/web-ele/src/views/ai/model/apiKey/data.ts @@ -0,0 +1,147 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'platform', + label: '所属平台', + component: 'Select', + componentProps: { + placeholder: '请选择所属平台', + options: getDictOptions(DICT_TYPE.AI_PLATFORM, 'string'), + allowClear: true, + }, + rules: 'required', + }, + { + component: 'Input', + fieldName: 'name', + label: '名称', + rules: 'required', + componentProps: { + placeholder: '请输入名称', + }, + }, + { + component: 'Input', + fieldName: 'apiKey', + label: '密钥', + rules: 'required', + componentProps: { + placeholder: '请输入密钥', + }, + }, + { + component: 'Input', + fieldName: 'url', + label: '自定义 API URL', + componentProps: { + placeholder: '请输入自定义 API URL', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名称', + component: 'Input', + componentProps: { + placeholder: '请输入名称', + allowClear: true, + }, + }, + { + fieldName: 'platform', + label: '平台', + component: 'Select', + componentProps: { + allowClear: true, + placeholder: '请选择平台', + options: getDictOptions(DICT_TYPE.AI_PLATFORM, 'string'), + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + allowClear: true, + placeholder: '请选择状态', + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'platform', + title: '所属平台', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_PLATFORM }, + }, + minWidth: 100, + }, + { + field: 'name', + title: '名称', + minWidth: 120, + }, + { + field: 'apiKey', + title: '密钥', + minWidth: 140, + }, + { + field: 'url', + title: '自定义 API URL', + minWidth: 180, + }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + minWidth: 80, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/model/apiKey/index.vue b/apps/web-ele/src/views/ai/model/apiKey/index.vue new file mode 100644 index 000000000..a6b5181ad --- /dev/null +++ b/apps/web-ele/src/views/ai/model/apiKey/index.vue @@ -0,0 +1,128 @@ + + + diff --git a/apps/web-ele/src/views/ai/model/apiKey/modules/form.vue b/apps/web-ele/src/views/ai/model/apiKey/modules/form.vue new file mode 100644 index 000000000..c7e5388ea --- /dev/null +++ b/apps/web-ele/src/views/ai/model/apiKey/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/ai/model/chatRole/data.ts b/apps/web-ele/src/views/ai/model/chatRole/data.ts new file mode 100644 index 000000000..ab7022777 --- /dev/null +++ b/apps/web-ele/src/views/ai/model/chatRole/data.ts @@ -0,0 +1,318 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { AiModelTypeEnum, CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleKnowledgeList } from '#/api/ai/knowledge/knowledge'; +import { getModelSimpleList } from '#/api/ai/model/model'; +import { getToolSimpleList } from '#/api/ai/model/tool'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'formType', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '角色名称', + rules: 'required', + componentProps: { + placeholder: '请输入角色名称', + }, + }, + { + component: 'ImageUpload', + fieldName: 'avatar', + label: '角色头像', + rules: 'required', + }, + { + fieldName: 'modelId', + label: '绑定模型', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择绑定模型', + api: () => getModelSimpleList(AiModelTypeEnum.CHAT), + labelField: 'name', + valueField: 'id', + allowClear: true, + }, + dependencies: { + triggerFields: ['formType'], + show: (values) => { + return values.formType === 'create' || values.formType === 'update'; + }, + }, + }, + { + component: 'Input', + fieldName: 'category', + label: '角色类别', + rules: 'required', + componentProps: { + placeholder: '请输入角色类别', + }, + dependencies: { + triggerFields: ['formType'], + show: (values) => { + return values.formType === 'create' || values.formType === 'update'; + }, + }, + }, + { + component: 'Textarea', + fieldName: 'description', + label: '角色描述', + componentProps: { + placeholder: '请输入角色描述', + }, + rules: 'required', + }, + { + fieldName: 'systemMessage', + label: '角色设定', + component: 'Textarea', + componentProps: { + placeholder: '请输入角色设定', + }, + rules: 'required', + }, + { + fieldName: 'knowledgeIds', + label: '引用知识库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择引用知识库', + api: getSimpleKnowledgeList, + labelField: 'name', + mode: 'multiple', + valueField: 'id', + allowClear: true, + }, + }, + { + fieldName: 'toolIds', + label: '引用工具', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择引用工具', + api: getToolSimpleList, + mode: 'multiple', + labelField: 'name', + valueField: 'id', + allowClear: true, + }, + }, + { + fieldName: 'mcpClientNames', + label: '引用 MCP', + component: 'Select', + componentProps: { + placeholder: '请选择 MCP', + options: getDictOptions(DICT_TYPE.AI_MCP_CLIENT_NAME, 'string'), + mode: 'multiple', + allowClear: true, + }, + }, + { + fieldName: 'publicStatus', + label: '是否公开', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + }, + defaultValue: true, + dependencies: { + triggerFields: ['formType'], + show: (values) => { + return values.formType === 'create' || values.formType === 'update'; + }, + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '角色排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入角色排序', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['formType'], + show: (values) => { + return values.formType === 'create' || values.formType === 'update'; + }, + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + dependencies: { + triggerFields: ['formType'], + show: (values) => { + return values.formType === 'create' || values.formType === 'update'; + }, + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + componentProps: { + placeholder: '请输入角色名称', + }, + }, + { + fieldName: 'category', + label: '角色类别', + component: 'Input', + componentProps: { + placeholder: '请输入角色类别', + }, + }, + { + fieldName: 'publicStatus', + label: '是否公开', + component: 'Select', + componentProps: { + placeholder: '请选择是否公开', + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + allowClear: true, + }, + defaultValue: true, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '角色名称', + minWidth: 100, + }, + { + title: '绑定模型', + field: 'modelName', + minWidth: 100, + }, + { + title: '角色头像', + field: 'avatar', + minWidth: 140, + cellRender: { + name: 'CellImage', + props: { + width: 40, + height: 40, + }, + }, + }, + { + title: '角色类别', + field: 'category', + minWidth: 100, + }, + { + title: '角色描述', + field: 'description', + minWidth: 100, + }, + { + title: '角色设定', + field: 'systemMessage', + minWidth: 100, + }, + { + title: '知识库', + field: 'knowledgeIds', + minWidth: 100, + formatter: ({ cellValue }) => { + return !cellValue || cellValue.length === 0 + ? '-' + : `引用${cellValue.length}个`; + }, + }, + { + title: '工具', + field: 'toolIds', + minWidth: 100, + formatter: ({ cellValue }) => { + return !cellValue || cellValue.length === 0 + ? '-' + : `引用${cellValue.length}个`; + }, + }, + { + title: 'MCP', + field: 'mcpClientNames', + minWidth: 100, + formatter: ({ cellValue }) => { + return !cellValue || cellValue.length === 0 + ? '-' + : `引用${cellValue.length}个`; + }, + }, + { + field: 'publicStatus', + title: '是否公开', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + minWidth: 80, + }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + minWidth: 80, + }, + { + title: '角色排序', + field: 'sort', + minWidth: 80, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/model/chatRole/index.vue b/apps/web-ele/src/views/ai/model/chatRole/index.vue new file mode 100644 index 000000000..16e784047 --- /dev/null +++ b/apps/web-ele/src/views/ai/model/chatRole/index.vue @@ -0,0 +1,128 @@ + + + diff --git a/apps/web-ele/src/views/ai/model/chatRole/modules/form.vue b/apps/web-ele/src/views/ai/model/chatRole/modules/form.vue new file mode 100644 index 000000000..35c44d2aa --- /dev/null +++ b/apps/web-ele/src/views/ai/model/chatRole/modules/form.vue @@ -0,0 +1,87 @@ + + + diff --git a/apps/web-ele/src/views/ai/model/model/data.ts b/apps/web-ele/src/views/ai/model/model/data.ts new file mode 100644 index 000000000..a29e31ae7 --- /dev/null +++ b/apps/web-ele/src/views/ai/model/model/data.ts @@ -0,0 +1,272 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { AiModelApiKeyApi } from '#/api/ai/model/apiKey'; + +import { AiModelTypeEnum, CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getApiKeySimpleList } from '#/api/ai/model/apiKey'; + +/** 关联数据 */ +let apiKeyList: AiModelApiKeyApi.ApiKey[] = []; +getApiKeySimpleList().then((data) => (apiKeyList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'platform', + label: '所属平台', + component: 'Select', + componentProps: { + placeholder: '请选择所属平台', + options: getDictOptions(DICT_TYPE.AI_PLATFORM, 'string'), + allowClear: true, + }, + rules: 'required', + }, + { + fieldName: 'type', + label: '模型类型', + component: 'Select', + componentProps: (values) => { + return { + placeholder: '请输入模型类型', + disabled: !!values.id, + options: getDictOptions(DICT_TYPE.AI_MODEL_TYPE, 'number'), + allowClear: true, + }; + }, + rules: 'required', + }, + { + fieldName: 'keyId', + label: 'API 秘钥', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择 API 秘钥', + api: getApiKeySimpleList, + labelField: 'name', + valueField: 'id', + allowClear: true, + }, + rules: 'required', + }, + { + component: 'Input', + fieldName: 'name', + label: '模型名字', + rules: 'required', + componentProps: { + placeholder: '请输入模型名字', + }, + }, + { + component: 'Input', + fieldName: 'model', + label: '模型标识', + rules: 'required', + componentProps: { + placeholder: '请输入模型标识', + }, + }, + { + fieldName: 'sort', + label: '模型排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入模型排序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'temperature', + label: '温度参数', + component: 'InputNumber', + componentProps: { + placeholder: '请输入温度参数', + min: 0, + max: 2, + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [AiModelTypeEnum.CHAT].includes(values.type); + }, + }, + rules: 'required', + }, + { + fieldName: 'maxTokens', + label: '回复数 Token 数', + component: 'InputNumber', + componentProps: { + min: 0, + max: 8192, + placeholder: '请输入回复数 Token 数', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [AiModelTypeEnum.CHAT].includes(values.type); + }, + }, + rules: 'required', + }, + { + fieldName: 'maxContexts', + label: '上下文数量', + component: 'InputNumber', + componentProps: { + min: 0, + max: 20, + placeholder: '请输入上下文数量', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [AiModelTypeEnum.CHAT].includes(values.type); + }, + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '模型名字', + component: 'Input', + componentProps: { + placeholder: '请输入模型名字', + allowClear: true, + }, + }, + { + fieldName: 'model', + label: '模型标识', + component: 'Input', + componentProps: { + placeholder: '请输入模型标识', + allowClear: true, + }, + }, + { + fieldName: 'platform', + label: '模型平台', + component: 'Input', + componentProps: { + placeholder: '请输入模型平台', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'platform', + title: '所属平台', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_PLATFORM }, + }, + minWidth: 100, + }, + { + field: 'type', + title: '模型类型', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_MODEL_TYPE }, + }, + minWidth: 100, + }, + { + field: 'name', + title: '模型名字', + minWidth: 180, + }, + { + title: '模型标识', + field: 'model', + minWidth: 180, + }, + { + title: 'API 秘钥', + field: 'keyId', + formatter: ({ cellValue }) => { + return ( + apiKeyList.find((apiKey) => apiKey.id === cellValue)?.name || '-' + ); + }, + minWidth: 140, + }, + { + title: '排序', + field: 'sort', + minWidth: 80, + }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + minWidth: 80, + }, + { + field: 'temperature', + title: '温度参数', + minWidth: 100, + }, + { + title: '回复数 Token 数', + field: 'maxTokens', + minWidth: 140, + }, + { + title: '上下文数量', + field: 'maxContexts', + minWidth: 120, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/model/model/index.vue b/apps/web-ele/src/views/ai/model/model/index.vue new file mode 100644 index 000000000..04488099c --- /dev/null +++ b/apps/web-ele/src/views/ai/model/model/index.vue @@ -0,0 +1,128 @@ + + + diff --git a/apps/web-ele/src/views/ai/model/model/modules/form.vue b/apps/web-ele/src/views/ai/model/model/modules/form.vue new file mode 100644 index 000000000..10727a642 --- /dev/null +++ b/apps/web-ele/src/views/ai/model/model/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/ai/model/tool/data.ts b/apps/web-ele/src/views/ai/model/tool/data.ts new file mode 100644 index 000000000..d8897ed70 --- /dev/null +++ b/apps/web-ele/src/views/ai/model/tool/data.ts @@ -0,0 +1,123 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + + { + component: 'Input', + fieldName: 'name', + label: '工具名称', + rules: 'required', + componentProps: { + placeholder: '请输入工具名称', + }, + }, + { + component: 'Textarea', + fieldName: 'description', + label: '工具描述', + componentProps: { + placeholder: '请输入工具描述', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + defaultValue: CommonStatusEnum.ENABLE, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '工具名称', + component: 'Input', + componentProps: { + placeholder: '请输入工具名称', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + allowClear: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '工具编号', + minWidth: 100, + }, + { + field: 'name', + title: '工具名称', + minWidth: 120, + }, + { + field: 'description', + title: '工具描述', + minWidth: 140, + }, + { + field: 'status', + title: '状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + minWidth: 80, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/model/tool/index.vue b/apps/web-ele/src/views/ai/model/tool/index.vue new file mode 100644 index 000000000..18c2dd3bb --- /dev/null +++ b/apps/web-ele/src/views/ai/model/tool/index.vue @@ -0,0 +1,131 @@ + + + diff --git a/apps/web-ele/src/views/ai/model/tool/modules/form.vue b/apps/web-ele/src/views/ai/model/tool/modules/form.vue new file mode 100644 index 000000000..afe7c21d4 --- /dev/null +++ b/apps/web-ele/src/views/ai/model/tool/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/index/index.vue b/apps/web-ele/src/views/ai/music/index/index.vue new file mode 100644 index 000000000..af00e1819 --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/index.vue @@ -0,0 +1,29 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/index/list/audioBar/index.vue b/apps/web-ele/src/views/ai/music/index/list/audioBar/index.vue new file mode 100644 index 000000000..961620aab --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/list/audioBar/index.vue @@ -0,0 +1,99 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/index/list/index.vue b/apps/web-ele/src/views/ai/music/index/list/index.vue new file mode 100644 index 000000000..1e0dde387 --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/list/index.vue @@ -0,0 +1,100 @@ + + + + diff --git a/apps/web-ele/src/views/ai/music/index/list/songCard/index.vue b/apps/web-ele/src/views/ai/music/index/list/songCard/index.vue new file mode 100644 index 000000000..2d7bdb661 --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/list/songCard/index.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/index/list/songInfo/index.vue b/apps/web-ele/src/views/ai/music/index/list/songInfo/index.vue new file mode 100644 index 000000000..eb4fbc8ad --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/list/songInfo/index.vue @@ -0,0 +1,25 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/index/mode/desc.vue b/apps/web-ele/src/views/ai/music/index/mode/desc.vue new file mode 100644 index 000000000..bcf9a01eb --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/mode/desc.vue @@ -0,0 +1,66 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/index/mode/index.vue b/apps/web-ele/src/views/ai/music/index/mode/index.vue new file mode 100644 index 000000000..813ea73ba --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/mode/index.vue @@ -0,0 +1,38 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/index/mode/lyric.vue b/apps/web-ele/src/views/ai/music/index/mode/lyric.vue new file mode 100644 index 000000000..c64f91564 --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/mode/lyric.vue @@ -0,0 +1,107 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/index/title/index.vue b/apps/web-ele/src/views/ai/music/index/title/index.vue new file mode 100644 index 000000000..04dc58bcc --- /dev/null +++ b/apps/web-ele/src/views/ai/music/index/title/index.vue @@ -0,0 +1,27 @@ + + + diff --git a/apps/web-ele/src/views/ai/music/manager/data.ts b/apps/web-ele/src/views/ai/music/manager/data.ts new file mode 100644 index 000000000..7b5c63a32 --- /dev/null +++ b/apps/web-ele/src/views/ai/music/manager/data.ts @@ -0,0 +1,202 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择用户编号', + clearable: true, + }, + }, + { + fieldName: 'title', + label: '音乐名称', + component: 'Input', + componentProps: { + placeholder: '请输入音乐名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '绘画状态', + component: 'Select', + componentProps: { + placeholder: '请选择绘画状态', + clearable: true, + options: getDictOptions(DICT_TYPE.AI_MUSIC_STATUS, 'number'), + }, + }, + { + fieldName: 'generateMode', + label: '生成模式', + component: 'Select', + componentProps: { + placeholder: '请选择生成模式', + clearable: true, + options: getDictOptions(DICT_TYPE.AI_GENERATE_MODE, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'publicStatus', + label: '是否发布', + component: 'Select', + componentProps: { + placeholder: '请选择是否发布', + clearable: true, + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onPublicStatusChange?: ( + newStatus: boolean, + row: any, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 180, + fixed: 'left', + }, + { + title: '音乐名称', + minWidth: 180, + fixed: 'left', + field: 'title', + }, + { + minWidth: 180, + title: '用户', + field: 'userId', + formatter: ({ cellValue }) => { + return userList.find((user) => user.id === cellValue)?.nickname || '-'; + }, + }, + { + field: 'status', + title: '音乐状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_MUSIC_STATUS }, + }, + }, + { + field: 'model', + title: '模型', + minWidth: 180, + }, + { + title: '内容', + minWidth: 180, + slots: { default: 'content' }, + }, + { + field: 'duration', + title: '时长(秒)', + minWidth: 100, + }, + { + field: 'prompt', + title: '提示词', + minWidth: 180, + }, + { + field: 'lyric', + title: '歌词', + minWidth: 180, + }, + { + field: 'gptDescriptionPrompt', + title: '描述', + minWidth: 180, + }, + { + field: 'generateMode', + title: '生成模式', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_GENERATE_MODE }, + }, + }, + { + field: 'tags', + title: '风格标签', + minWidth: 180, + cellRender: { + name: 'CellTags', + }, + }, + { + minWidth: 100, + title: '是否发布', + field: 'publicStatus', + align: 'center', + cellRender: { + attrs: { beforeChange: onPublicStatusChange }, + name: 'CellSwitch', + props: { + activeValue: true, + inactiveValue: false, + }, + }, + }, + { + field: 'taskId', + title: '任务编号', + minWidth: 180, + }, + { + field: 'errorMessage', + title: '错误信息', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/music/manager/index.vue b/apps/web-ele/src/views/ai/music/manager/index.vue new file mode 100644 index 000000000..d0e04cb57 --- /dev/null +++ b/apps/web-ele/src/views/ai/music/manager/index.vue @@ -0,0 +1,152 @@ + + + diff --git a/apps/web-ele/src/views/ai/workflow/data.ts b/apps/web-ele/src/views/ai/workflow/data.ts new file mode 100644 index 000000000..5decf9f3f --- /dev/null +++ b/apps/web-ele/src/views/ai/workflow/data.ts @@ -0,0 +1,97 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'code', + label: '流程标识', + component: 'Input', + componentProps: { + placeholder: '请输入流程标识', + allowClear: true, + }, + }, + { + fieldName: 'name', + label: '流程名称', + component: 'Input', + componentProps: { + placeholder: '请输入流程名称', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'code', + title: '流程标识', + minWidth: 150, + }, + { + field: 'name', + title: '流程名称', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/workflow/form/index.vue b/apps/web-ele/src/views/ai/workflow/form/index.vue new file mode 100644 index 000000000..ca206ffbe --- /dev/null +++ b/apps/web-ele/src/views/ai/workflow/form/index.vue @@ -0,0 +1,288 @@ + + + diff --git a/apps/web-ele/src/views/ai/workflow/form/modules/basic-info.vue b/apps/web-ele/src/views/ai/workflow/form/modules/basic-info.vue new file mode 100644 index 000000000..09f87253e --- /dev/null +++ b/apps/web-ele/src/views/ai/workflow/form/modules/basic-info.vue @@ -0,0 +1,70 @@ + + + diff --git a/apps/web-ele/src/views/ai/workflow/form/modules/workflow-design.vue b/apps/web-ele/src/views/ai/workflow/form/modules/workflow-design.vue new file mode 100644 index 000000000..3acb5ad5b --- /dev/null +++ b/apps/web-ele/src/views/ai/workflow/form/modules/workflow-design.vue @@ -0,0 +1,286 @@ + + + diff --git a/apps/web-ele/src/views/ai/workflow/index.vue b/apps/web-ele/src/views/ai/workflow/index.vue new file mode 100644 index 000000000..ab31d4756 --- /dev/null +++ b/apps/web-ele/src/views/ai/workflow/index.vue @@ -0,0 +1,124 @@ + + + diff --git a/apps/web-ele/src/views/ai/write/index/index.vue b/apps/web-ele/src/views/ai/write/index/index.vue new file mode 100644 index 000000000..04b55144b --- /dev/null +++ b/apps/web-ele/src/views/ai/write/index/index.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/ai/write/index/modules/left.vue b/apps/web-ele/src/views/ai/write/index/modules/left.vue new file mode 100644 index 000000000..a364265db --- /dev/null +++ b/apps/web-ele/src/views/ai/write/index/modules/left.vue @@ -0,0 +1,250 @@ + + + diff --git a/apps/web-ele/src/views/ai/write/index/modules/right.vue b/apps/web-ele/src/views/ai/write/index/modules/right.vue new file mode 100644 index 000000000..bd0a46831 --- /dev/null +++ b/apps/web-ele/src/views/ai/write/index/modules/right.vue @@ -0,0 +1,144 @@ + + + + diff --git a/apps/web-ele/src/views/ai/write/index/modules/tag.vue b/apps/web-ele/src/views/ai/write/index/modules/tag.vue new file mode 100644 index 000000000..ed758fc0a --- /dev/null +++ b/apps/web-ele/src/views/ai/write/index/modules/tag.vue @@ -0,0 +1,33 @@ + + + + diff --git a/apps/web-ele/src/views/ai/write/manager/data.ts b/apps/web-ele/src/views/ai/write/manager/data.ts new file mode 100644 index 000000000..02f39e8e5 --- /dev/null +++ b/apps/web-ele/src/views/ai/write/manager/data.ts @@ -0,0 +1,171 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择用户', + allowClear: true, + }, + }, + { + fieldName: 'type', + label: '写作类型', + component: 'Select', + componentProps: { + allowClear: true, + placeholder: '请选择写作类型', + options: getDictOptions(DICT_TYPE.AI_WRITE_TYPE, 'number'), + }, + }, + { + fieldName: 'platform', + label: '平台', + component: 'Select', + componentProps: { + allowClear: true, + placeholder: '请选择平台', + options: getDictOptions(DICT_TYPE.AI_PLATFORM, 'string'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 180, + fixed: 'left', + }, + { + minWidth: 180, + title: '用户', + field: 'userId', + formatter: ({ cellValue }) => { + return userList.find((user) => user.id === cellValue)?.nickname || '-'; + }, + }, + { + field: 'type', + title: '写作类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_WRITE_TYPE }, + }, + }, + { + field: 'platform', + title: '平台', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_WRITE_TYPE }, + }, + }, + { + field: 'model', + title: '模型', + minWidth: 180, + }, + { + field: 'prompt', + title: '生成内容提示', + minWidth: 180, + }, + { + field: 'generatedContent', + title: '生成的内容', + minWidth: 180, + }, + { + field: 'originalContent', + title: '原文', + minWidth: 180, + }, + { + field: 'length', + title: '长度', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_WRITE_LENGTH }, + }, + }, + { + field: 'format', + title: '格式', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_WRITE_FORMAT }, + }, + }, + { + field: 'tone', + title: '语气', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_WRITE_TONE }, + }, + }, + { + field: 'language', + title: '语言', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.AI_WRITE_LANGUAGE }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'errorMessage', + title: '错误信息', + minWidth: 180, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/ai/write/manager/index.vue b/apps/web-ele/src/views/ai/write/manager/index.vue new file mode 100644 index 000000000..fa73240a4 --- /dev/null +++ b/apps/web-ele/src/views/ai/write/manager/index.vue @@ -0,0 +1,90 @@ + + + diff --git a/apps/web-ele/src/views/bpm/category/data.ts b/apps/web-ele/src/views/bpm/category/data.ts new file mode 100644 index 000000000..6aaf6e629 --- /dev/null +++ b/apps/web-ele/src/views/bpm/category/data.ts @@ -0,0 +1,178 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '分类名', + component: 'Input', + componentProps: { + placeholder: '请输入分类名', + }, + rules: 'required', + }, + { + label: '分类标志', + fieldName: 'code', + component: 'Input', + componentProps: { + placeholder: '请输入分类标志', + }, + rules: 'required', + }, + { + fieldName: 'description', + label: '分类描述', + component: 'Textarea', + componentProps: { + placeholder: '请输入分类描述', + }, + }, + { + fieldName: 'status', + label: '分类状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'sort', + label: '分类排序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入分类排序', + controlsPosition: 'right', + class: '!w-full', + }, + }, + ]; +} + +/** 重命名的表单 */ +export function useRenameFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '分类名', + component: 'Input', + componentProps: { + placeholder: '请输入分类名', + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '分类名', + component: 'Input', + componentProps: { + placeholder: '请输入分类名', + allowClear: true, + }, + }, + { + fieldName: 'code', + label: '分类标志', + component: 'Input', + componentProps: { + placeholder: '请输入分类标志', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '分类状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择分类状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '分类编号', + minWidth: 100, + }, + { + field: 'name', + title: '分类名', + minWidth: 200, + }, + { + field: 'code', + title: '分类标志', + minWidth: 200, + }, + { + field: 'description', + title: '分类描述', + minWidth: 200, + }, + { + field: 'status', + title: '分类状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'sort', + title: '分类排序', + minWidth: 100, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/category/index.vue b/apps/web-ele/src/views/bpm/category/index.vue new file mode 100644 index 000000000..c747e8a96 --- /dev/null +++ b/apps/web-ele/src/views/bpm/category/index.vue @@ -0,0 +1,129 @@ + + + diff --git a/apps/web-ele/src/views/bpm/category/modules/form.vue b/apps/web-ele/src/views/bpm/category/modules/form.vue new file mode 100644 index 000000000..4494d9d68 --- /dev/null +++ b/apps/web-ele/src/views/bpm/category/modules/form.vue @@ -0,0 +1,86 @@ + + + diff --git a/apps/web-ele/src/views/bpm/category/modules/rename-form.vue b/apps/web-ele/src/views/bpm/category/modules/rename-form.vue new file mode 100644 index 000000000..5882fe948 --- /dev/null +++ b/apps/web-ele/src/views/bpm/category/modules/rename-form.vue @@ -0,0 +1,80 @@ + + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/child-process-node-config.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/child-process-node-config.vue new file mode 100644 index 000000000..9ad51c734 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/child-process-node-config.vue @@ -0,0 +1,779 @@ + + + + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/condition-node-config.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/condition-node-config.vue new file mode 100644 index 000000000..e09097147 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/condition-node-config.vue @@ -0,0 +1,197 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/copy-task-node-config.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/copy-task-node-config.vue new file mode 100644 index 000000000..461268b09 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/copy-task-node-config.vue @@ -0,0 +1,472 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/delay-timer-node-config.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/delay-timer-node-config.vue new file mode 100644 index 000000000..2366eb9f2 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/delay-timer-node-config.vue @@ -0,0 +1,235 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/condition-dialog.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/condition-dialog.vue new file mode 100644 index 000000000..5fc304226 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/condition-dialog.vue @@ -0,0 +1,79 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/condition.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/condition.vue new file mode 100644 index 000000000..e018cdde6 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/condition.vue @@ -0,0 +1,312 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/http-request-param-setting.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/http-request-param-setting.vue new file mode 100644 index 000000000..c8501824c --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/http-request-param-setting.vue @@ -0,0 +1,237 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/http-request-setting.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/http-request-setting.vue new file mode 100644 index 000000000..2446effd2 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/http-request-setting.vue @@ -0,0 +1,166 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/user-task-listener.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/user-task-listener.vue new file mode 100644 index 000000000..244e4727b --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/modules/user-task-listener.vue @@ -0,0 +1,109 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/router-node-config.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/router-node-config.vue new file mode 100644 index 000000000..8f8907061 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/router-node-config.vue @@ -0,0 +1,291 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/start-user-node-config.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/start-user-node-config.vue new file mode 100644 index 000000000..bae452fa3 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/start-user-node-config.vue @@ -0,0 +1,268 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/trigger-node-config.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/trigger-node-config.vue new file mode 100644 index 000000000..f01543c3e --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/trigger-node-config.vue @@ -0,0 +1,684 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/user-task-node-config.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/user-task-node-config.vue new file mode 100644 index 000000000..effa8b404 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/user-task-node-config.vue @@ -0,0 +1,1178 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/utils.ts b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/utils.ts new file mode 100644 index 000000000..ee5e06809 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes-config/utils.ts @@ -0,0 +1,48 @@ +import { APPROVE_TYPE, ApproveType, TimeUnitType } from '../../consts'; + +/** 获取条件节点默认的名称 */ +export function getDefaultConditionNodeName( + index: number, + defaultFlow: boolean | undefined, +): string { + if (defaultFlow) { + return '其它情况'; + } + return `条件${index + 1}`; +} + +/** 获取包容分支条件节点默认的名称 */ +export function getDefaultInclusiveConditionNodeName( + index: number, + defaultFlow: boolean | undefined, +): string { + if (defaultFlow) { + return '其它情况'; + } + return `包容条件${index + 1}`; +} + +/** 转换时间单位字符串为枚举值 */ +export function convertTimeUnit(strTimeUnit: string) { + if (strTimeUnit === 'M') { + return TimeUnitType.MINUTE; + } + if (strTimeUnit === 'H') { + return TimeUnitType.HOUR; + } + if (strTimeUnit === 'D') { + return TimeUnitType.DAY; + } + return TimeUnitType.HOUR; +} + +/** 根据审批类型获取对应的文本描述 */ +export function getApproveTypeText(approveType: ApproveType): string { + let approveTypeText = ''; + APPROVE_TYPE.forEach((item) => { + if (item.value === approveType) { + approveTypeText = item.label; + } + }); + return approveTypeText; +} diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/child-process-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/child-process-node.vue new file mode 100644 index 000000000..cb4b928fe --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/child-process-node.vue @@ -0,0 +1,127 @@ + + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/copy-task-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/copy-task-node.vue new file mode 100644 index 000000000..33b3758a3 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/copy-task-node.vue @@ -0,0 +1,120 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/delay-timer-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/delay-timer-node.vue new file mode 100644 index 000000000..cdde90a61 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/delay-timer-node.vue @@ -0,0 +1,117 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/end-event-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/end-event-node.vue new file mode 100644 index 000000000..bf6a2c1a9 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/end-event-node.vue @@ -0,0 +1,61 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/exclusive-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/exclusive-node.vue new file mode 100644 index 000000000..7ad414aeb --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/exclusive-node.vue @@ -0,0 +1,307 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/inclusive-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/inclusive-node.vue new file mode 100644 index 000000000..cc2d7f195 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/inclusive-node.vue @@ -0,0 +1,309 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/process-instance-data.ts b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/process-instance-data.ts new file mode 100644 index 000000000..a9f2a06bb --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/process-instance-data.ts @@ -0,0 +1,56 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 流程实例列表字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'startUser', + title: '发起人', + slots: { + default: ({ row }: { row: any }) => { + return row.startUser?.nickname; + }, + }, + minWidth: 100, + }, + { + field: 'deptName', + title: '部门', + slots: { + default: ({ row }: { row: any }) => { + return row.startUser?.deptName; + }, + }, + minWidth: 100, + }, + { + field: 'createTime', + title: '开始时间', + formatter: 'formatDateTime', + minWidth: 140, + }, + { + field: 'endTime', + title: '结束时间', + formatter: 'formatDateTime', + minWidth: 140, + }, + { + field: 'status', + title: '流程状态', + minWidth: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS }, + }, + }, + { + field: 'durationInMillis', + title: '耗时', + minWidth: 100, + formatter: 'formatPast2', + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/process-instance-modal.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/process-instance-modal.vue new file mode 100644 index 000000000..dec77d854 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/process-instance-modal.vue @@ -0,0 +1,45 @@ + + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/task-list-data.ts b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/task-list-data.ts new file mode 100644 index 000000000..fbb2d0ea5 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/task-list-data.ts @@ -0,0 +1,61 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 审批记录列表字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'assigneeUser', + title: '审批人', + slots: { + default: ({ row }: { row: any }) => { + return row.assigneeUser?.nickname || row.ownerUser?.nickname; + }, + }, + minWidth: 100, + }, + { + field: 'deptName', + title: '部门', + slots: { + default: ({ row }: { row: any }) => { + return row.assigneeUser?.deptName || row.ownerUser?.deptName; + }, + }, + minWidth: 100, + }, + { + field: 'createTime', + title: '开始时间', + formatter: 'formatDateTime', + minWidth: 140, + }, + { + field: 'endTime', + title: '结束时间', + formatter: 'formatDateTime', + minWidth: 140, + }, + { + field: 'status', + title: '审批状态', + minWidth: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_TASK_STATUS }, + }, + }, + { + field: 'reason', + title: '审批建议', + minWidth: 160, + }, + { + field: 'durationInMillis', + title: '耗时', + minWidth: 100, + formatter: 'formatPast2', + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/task-list-modal.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/task-list-modal.vue new file mode 100644 index 000000000..38b869a49 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/modules/task-list-modal.vue @@ -0,0 +1,48 @@ + + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/node-handler.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/node-handler.vue new file mode 100644 index 000000000..af40f4588 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/node-handler.vue @@ -0,0 +1,355 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/parallel-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/parallel-node.vue new file mode 100644 index 000000000..1c13a7831 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/parallel-node.vue @@ -0,0 +1,230 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/router-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/router-node.vue new file mode 100644 index 000000000..430528360 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/router-node.vue @@ -0,0 +1,119 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/start-user-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/start-user-node.vue new file mode 100644 index 000000000..73775c52f --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/start-user-node.vue @@ -0,0 +1,128 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/trigger-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/trigger-node.vue new file mode 100644 index 000000000..37d154156 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/trigger-node.vue @@ -0,0 +1,122 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/user-task-node.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/user-task-node.vue new file mode 100644 index 000000000..a95b33348 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/nodes/user-task-node.vue @@ -0,0 +1,154 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/process-node-tree.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/process-node-tree.vue new file mode 100644 index 000000000..ea8596a85 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/process-node-tree.vue @@ -0,0 +1,164 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-designer.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-designer.vue new file mode 100644 index 000000000..d66629ccd --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-designer.vue @@ -0,0 +1,255 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-model.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-model.vue new file mode 100644 index 000000000..d4fa3b152 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-model.vue @@ -0,0 +1,267 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-viewer.vue b/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-viewer.vue new file mode 100644 index 000000000..95cf3f321 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/components/simple-process-viewer.vue @@ -0,0 +1,45 @@ + + diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/consts.ts b/apps/web-ele/src/views/bpm/components/simple-process-design/consts.ts new file mode 100644 index 000000000..e811314e5 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/consts.ts @@ -0,0 +1,888 @@ +import { BpmNodeTypeEnum, BpmTaskStatusEnum } from '@vben/constants'; + +interface DictDataType { + label: string; + value: number | string; +} + +// 用户任务的审批类型。 【参考飞书】 +export enum ApproveType { + /** + * 自动通过 + */ + AUTO_APPROVE = 2, + /** + * 自动拒绝 + */ + AUTO_REJECT = 3, + /** + * 人工审批 + */ + USER = 1, +} + +// 多人审批方式类型枚举 ( 用于审批节点 ) +export enum ApproveMethodType { + /** + * 多人或签(通过只需一人,拒绝只需一人) + */ + ANY_APPROVE = 3, + + /** + * 多人会签(按通过比例) + */ + APPROVE_BY_RATIO = 2, + + /** + * 随机挑选一人审批 + */ + RANDOM_SELECT_ONE_APPROVE = 1, + /** + * 多人依次审批 + */ + SEQUENTIAL_APPROVE = 4, +} + +export enum NodeId { + /** + * 发起人节点 Id + */ + END_EVENT_NODE_ID = 'EndEvent', + + /** + * 发起人节点 Id + */ + START_USER_NODE_ID = 'StartUserNode', +} + +// 条件配置类型 ( 用于条件节点配置 ) +export enum ConditionType { + /** + * 条件表达式 + */ + EXPRESSION = 1, + + /** + * 条件规则 + */ + RULE = 2, +} + +// 操作按钮类型枚举 (用于审批节点) +export enum OperationButtonType { + /** + * 加签 + */ + ADD_SIGN = 5, + /** + * 通过 + */ + APPROVE = 1, + /** + * 抄送 + */ + COPY = 7, + /** + * 委派 + */ + DELEGATE = 4, + /** + * 拒绝 + */ + REJECT = 2, + /** + * 退回 + */ + RETURN = 6, + /** + * 转办 + */ + TRANSFER = 3, +} + +// 审批拒绝类型枚举 +export enum RejectHandlerType { + /** + * 结束流程 + */ + FINISH_PROCESS = 1, + /** + * 驳回到指定节点 + */ + RETURN_USER_TASK = 2, +} + +// 用户任务超时处理类型枚举 +export enum TimeoutHandlerType { + /** + * 自动同意 + */ + APPROVE = 2, + /** + * 自动拒绝 + */ + REJECT = 3, + /** + * 自动提醒 + */ + REMINDER = 1, +} + +// 用户任务的审批人为空时,处理类型枚举 +export enum AssignEmptyHandlerType { + /** + * 自动通过 + */ + APPROVE = 1, + /** + * 转交给流程管理员 + */ + ASSIGN_ADMIN = 4, + /** + * 指定人员审批 + */ + ASSIGN_USER = 3, + /** + * 自动拒绝 + */ + REJECT = 2, +} + +// 用户任务的审批人与发起人相同时,处理类型枚举 +export enum AssignStartUserHandlerType { + /** + * 转交给部门负责人审批 + */ + ASSIGN_DEPT_LEADER = 3, + /** + * 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过 + */ + SKIP = 2, + /** + * 由发起人对自己审批 + */ + START_USER_AUDIT = 1, +} + +// 时间单位枚举 +export enum TimeUnitType { + /** + * 天 + */ + DAY = 3, + /** + * 小时 + */ + HOUR = 2, + /** + * 分钟 + */ + MINUTE = 1, +} + +/** + * 表单权限的枚举 + */ +export enum FieldPermissionType { + /** + * 隐藏 + */ + NONE = '3', + /** + * 只读 + */ + READ = '1', + /** + * 编辑 + */ + WRITE = '2', +} + +/** + * 延迟类型 + */ +export enum DelayTypeEnum { + /** + * 固定日期时间 + */ + FIXED_DATE_TIME = 2, + /** + * 固定时长 + */ + FIXED_TIME_DURATION = 1, +} + +/** + * 触发器类型枚举 + */ +export enum TriggerTypeEnum { + /** + * 表单数据删除触发器 + */ + FORM_DELETE = 11, + /** + * 表单数据更新触发器 + */ + FORM_UPDATE = 10, + /** + * 接收 HTTP 回调请求触发器 + */ + HTTP_CALLBACK = 2, + /** + * 发送 HTTP 请求触发器 + */ + HTTP_REQUEST = 1, +} + +export enum ChildProcessStartUserTypeEnum { + /** + * 表单 + */ + FROM_FORM = 2, + /** + * 同主流程发起人 + */ + MAIN_PROCESS_START_USER = 1, +} + +export enum ChildProcessStartUserEmptyTypeEnum { + /** + * 子流程管理员 + */ + CHILD_PROCESS_ADMIN = 2, + /** + * 主流程管理员 + */ + MAIN_PROCESS_ADMIN = 3, + /** + * 同主流程发起人 + */ + MAIN_PROCESS_START_USER = 1, +} + +export enum ChildProcessMultiInstanceSourceTypeEnum { + /** + * 固定数量 + */ + FIXED_QUANTITY = 1, + /** + * 多选表单 + */ + MULTIPLE_FORM = 3, + /** + * 数字表单 + */ + NUMBER_FORM = 2, +} + +// 候选人策略枚举 ( 用于审批节点。抄送节点 ) +export enum CandidateStrategy { + /** + * 审批人自选 + */ + APPROVE_USER_SELECT = 34, + /** + * 部门的负责人 + */ + DEPT_LEADER = 21, + /** + * 部门成员 + */ + DEPT_MEMBER = 20, + /** + * 流程表达式 + */ + EXPRESSION = 60, + /** + * 表单内部门负责人 + */ + FORM_DEPT_LEADER = 51, + /** + * 表单内用户字段 + */ + FORM_USER = 50, + /** + * 连续多级部门的负责人 + */ + MULTI_LEVEL_DEPT_LEADER = 23, + /** + * 指定岗位 + */ + POST = 22, + /** + * 指定角色 + */ + ROLE = 10, + /** + * 发起人自己 + */ + START_USER = 36, + /** + * 发起人部门负责人 + */ + START_USER_DEPT_LEADER = 37, + /** + * 发起人连续多级部门的负责人 + */ + START_USER_MULTI_LEVEL_DEPT_LEADER = 38, + /** + * 发起人自选 + */ + START_USER_SELECT = 35, + /** + * 指定用户 + */ + USER = 30, + /** + * 指定用户组 + */ + USER_GROUP = 40, +} + +export enum BpmHttpRequestParamTypeEnum { + /** + * 固定值 + */ + FIXED_VALUE = 1, + /** + * 表单 + */ + FROM_FORM = 2, +} + +// 这里定义 HTTP 请求参数类型 +export type HttpRequestParam = { + key: string; + type: number; + value: string; +}; + +// 监听器结构定义 +export type ListenerHandler = { + body?: HttpRequestParam[]; + enable: boolean; + header?: HttpRequestParam[]; + path?: string; +}; + +/** + * 条件规则结构定义 + */ +export type ConditionRule = { + leftSide: string | undefined; + opCode: string; + rightSide: string | undefined; +}; + +/** + * 条件结构定义 + */ +export type Condition = { + // 条件规则的逻辑关系是否为且 + and: boolean; + rules: ConditionRule[]; +}; + +/** + * 条件组结构定义 + */ +export type ConditionGroup = { + // 条件组的逻辑关系是否为且 + and: boolean; + // 条件数组 + conditions: Condition[]; +}; + +/** + * 条件节点设置结构定义,用于条件节点 + */ +export type ConditionSetting = { + // 条件表达式 + conditionExpression?: string; + // 条件组 + conditionGroups?: ConditionGroup; + // 条件类型 + conditionType?: ConditionType; + // 是否默认的条件 + defaultFlow?: boolean; +}; + +/** + * 审批拒绝结构定义 + */ +export type RejectHandler = { + // 退回节点 Id + returnNodeId?: string; + // 审批拒绝类型 + type: RejectHandlerType; +}; + +/** + * 审批超时结构定义 + */ +export type TimeoutHandler = { + // 是否开启超时处理 + enable: boolean; + // 执行动作是自动提醒, 最大提醒次数 + maxRemindCount?: number; + // 超时时间设置 + timeDuration?: string; + // 超时执行的动作 + type?: number; +}; + +/** + * 审批人为空的结构定义 + */ +export type AssignEmptyHandler = { + // 审批人为空的处理类型 + type: AssignEmptyHandlerType; + // 指定用户的编号数组 + userIds?: number[]; +}; + +/** + * 延迟设置 + */ +export type DelaySetting = { + // 延迟时间表达式 + delayTime: string; + // 延迟类型 + delayType: number; +}; + +/** + * 路由分支结构定义 + */ +export type RouterSetting = { + conditionExpression: string; + conditionGroups: ConditionGroup; + conditionType: ConditionType; + nodeId: string | undefined; +}; + +/** + * 操作按钮权限结构定义 + */ +export type ButtonSetting = { + displayName: string; + enable: boolean; + id: OperationButtonType; +}; + +/** + * HTTP 请求触发器结构定义 + */ +export type HttpRequestTriggerSetting = { + // 请求体参数设置 + body?: HttpRequestParam[]; + // 请求头参数设置 + header?: HttpRequestParam[]; + // 请求响应设置 + response?: Record[]; + // 请求 URL + url: string; +}; + +/** + * 流程表单触发器配置结构定义 + */ +export type FormTriggerSetting = { + // 条件表达式 + conditionExpression?: string; + // 条件组 + conditionGroups?: ConditionGroup; + // 条件类型 + conditionType?: ConditionType; + // 删除表单字段配置 + deleteFields?: string[]; + // 更新表单字段配置 + updateFormFields?: Record; +}; + +/** + * 触发器节点结构定义 + */ +export type TriggerSetting = { + formSettings?: FormTriggerSetting[]; + httpRequestSetting?: HttpRequestTriggerSetting; + type: TriggerTypeEnum; +}; + +export type IOParameter = { + source: string; + target: string; +}; + +export type StartUserSetting = { + emptyType?: ChildProcessStartUserEmptyTypeEnum; + formField?: string; + type: ChildProcessStartUserTypeEnum; +}; + +export type TimeoutSetting = { + enable: boolean; + timeExpression?: string; + type?: DelayTypeEnum; +}; + +export type MultiInstanceSetting = { + approveRatio?: number; + enable: boolean; + sequential?: boolean; + source?: string; + sourceType?: ChildProcessMultiInstanceSourceTypeEnum; +}; + +/** + * 子流程节点结构定义 + */ +export type ChildProcessSetting = { + async: boolean; + calledProcessDefinitionKey: string; + calledProcessDefinitionName: string; + inVariables?: IOParameter[]; + multiInstanceSetting: MultiInstanceSetting; + outVariables?: IOParameter[]; + skipStartUserNode: boolean; + startUserSetting: StartUserSetting; + timeoutSetting: TimeoutSetting; +}; + +/** + * 节点结构定义 + */ +export interface SimpleFlowNode { + id: string; + type: BpmNodeTypeEnum; + name: string; + showText?: string; + // 孩子节点 + childNode?: SimpleFlowNode; + // 条件节点 + conditionNodes?: SimpleFlowNode[]; + // 审批类型 + approveType?: ApproveType; + // 候选人策略 + candidateStrategy?: number; + // 候选人参数 + candidateParam?: string; + // 多人审批方式 + approveMethod?: ApproveMethodType; + // 通过比例 + approveRatio?: number; + // 审批按钮设置 + buttonsSetting?: any[]; + // 表单权限 + fieldsPermission?: Array>; + // 审批任务超时处理 + timeoutHandler?: TimeoutHandler; + // 审批任务拒绝处理 + rejectHandler?: RejectHandler; + // 审批人为空的处理 + assignEmptyHandler?: AssignEmptyHandler; + // 审批节点的审批人与发起人相同时,对应的处理类型 + assignStartUserHandlerType?: number; + // 创建任务监听器 + taskCreateListener?: ListenerHandler; + // 创建任务监听器 + taskAssignListener?: ListenerHandler; + // 创建任务监听器 + taskCompleteListener?: ListenerHandler; + // 条件设置 + conditionSetting?: ConditionSetting; + // 活动的状态,用于前端节点状态展示 + activityStatus?: BpmTaskStatusEnum; + // 延迟设置 + delaySetting?: DelaySetting; + // 路由分支 + routerGroups?: RouterSetting[]; + defaultFlowId?: string; + // 签名 + signEnable?: boolean; + // 审批意见 + reasonRequire?: boolean; + // 跳过表达式 + skipExpression?: string; + // 触发器设置 + triggerSetting?: TriggerSetting; + // 子流程 + childProcessSetting?: ChildProcessSetting; +} + +/** + * 条件组默认值 + */ +export const DEFAULT_CONDITION_GROUP_VALUE = { + and: true, + conditions: [ + { + and: true, + rules: [ + { + opCode: '==', + leftSide: undefined, + rightSide: '', + }, + ], + }, + ], +}; + +export const NODE_DEFAULT_TEXT = new Map(); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.USER_TASK_NODE, '请配置审批人'); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.COPY_TASK_NODE, '请配置抄送人'); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.CONDITION_NODE, '请设置条件'); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.START_USER_NODE, '请设置发起人'); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.DELAY_TIMER_NODE, '请设置延迟器'); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.ROUTER_BRANCH_NODE, '请设置路由节点'); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.TRIGGER_NODE, '请设置触发器'); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.TRANSACTOR_NODE, '请设置办理人'); +NODE_DEFAULT_TEXT.set(BpmNodeTypeEnum.CHILD_PROCESS_NODE, '请设置子流程'); + +export const NODE_DEFAULT_NAME = new Map(); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.USER_TASK_NODE, '审批人'); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.COPY_TASK_NODE, '抄送人'); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.CONDITION_NODE, '条件'); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.START_USER_NODE, '发起人'); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.DELAY_TIMER_NODE, '延迟器'); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.ROUTER_BRANCH_NODE, '路由分支'); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.TRIGGER_NODE, '触发器'); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.TRANSACTOR_NODE, '办理人'); +NODE_DEFAULT_NAME.set(BpmNodeTypeEnum.CHILD_PROCESS_NODE, '子流程'); + +// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序 +export const CANDIDATE_STRATEGY: DictDataType[] = [ + { label: '指定成员', value: CandidateStrategy.USER as any }, + { label: '指定角色', value: CandidateStrategy.ROLE as any }, + { label: '指定岗位', value: CandidateStrategy.POST as any }, + { label: '部门成员', value: CandidateStrategy.DEPT_MEMBER as any }, + { label: '部门负责人', value: CandidateStrategy.DEPT_LEADER as any }, + { + label: '连续多级部门负责人', + value: CandidateStrategy.MULTI_LEVEL_DEPT_LEADER as any, + }, + { label: '发起人自选', value: CandidateStrategy.START_USER_SELECT as any }, + { label: '审批人自选', value: CandidateStrategy.APPROVE_USER_SELECT as any }, + { label: '发起人本人', value: CandidateStrategy.START_USER as any }, + { + label: '发起人部门负责人', + value: CandidateStrategy.START_USER_DEPT_LEADER as any, + }, + { + label: '发起人连续部门负责人', + value: CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER as any, + }, + { label: '用户组', value: CandidateStrategy.USER_GROUP as any }, + { label: '表单内用户字段', value: CandidateStrategy.FORM_USER as any }, + { + label: '表单内部门负责人', + value: CandidateStrategy.FORM_DEPT_LEADER as any, + }, + { label: '流程表达式', value: CandidateStrategy.EXPRESSION as any }, +]; +// 审批节点 的审批类型 +export const APPROVE_TYPE: DictDataType[] = [ + { label: '人工审批', value: ApproveType.USER as any }, + { label: '自动通过', value: ApproveType.AUTO_APPROVE as any }, + { label: '自动拒绝', value: ApproveType.AUTO_REJECT as any }, +]; + +export const APPROVE_METHODS: DictDataType[] = [ + { + label: '按顺序依次审批', + value: ApproveMethodType.SEQUENTIAL_APPROVE as any, + }, + { + label: '会签(可同时审批,至少 % 人必须审批通过)', + value: ApproveMethodType.APPROVE_BY_RATIO as any, + }, + { + label: '或签(可同时审批,有一人通过即可)', + value: ApproveMethodType.ANY_APPROVE as any, + }, + { + label: '随机挑选一人审批', + value: ApproveMethodType.RANDOM_SELECT_ONE_APPROVE as any, + }, +]; + +export const CONDITION_CONFIG_TYPES: DictDataType[] = [ + { label: '条件规则', value: ConditionType.RULE as any }, + { label: '条件表达式', value: ConditionType.EXPRESSION as any }, +]; + +// 时间单位类型 +export const TIME_UNIT_TYPES: DictDataType[] = [ + { label: '分钟', value: TimeUnitType.MINUTE as any }, + { label: '小时', value: TimeUnitType.HOUR as any }, + { label: '天', value: TimeUnitType.DAY as any }, +]; +// 超时处理执行动作类型 +export const TIMEOUT_HANDLER_TYPES: DictDataType[] = [ + { label: '自动提醒', value: 1 }, + { label: '自动同意', value: 2 }, + { label: '自动拒绝', value: 3 }, +]; +export const REJECT_HANDLER_TYPES: DictDataType[] = [ + { label: '终止流程', value: RejectHandlerType.FINISH_PROCESS as any }, + { label: '驳回到指定节点', value: RejectHandlerType.RETURN_USER_TASK as any }, + // { label: '结束任务', value: RejectHandlerType.FINISH_TASK } +]; +export const ASSIGN_EMPTY_HANDLER_TYPES: DictDataType[] = [ + { label: '自动通过', value: 1 }, + { label: '自动拒绝', value: 2 }, + { label: '指定成员审批', value: 3 }, + { label: '转交给流程管理员', value: 4 }, +]; +export const ASSIGN_START_USER_HANDLER_TYPES: DictDataType[] = [ + { label: '由发起人对自己审批', value: 1 }, + { label: '自动跳过', value: 2 }, + { label: '转交给部门负责人审批', value: 3 }, +]; + +// 比较运算符 +export const COMPARISON_OPERATORS: DictDataType[] = [ + { + value: '==', + label: '等于', + }, + { + value: '!=', + label: '不等于', + }, + { + value: '>', + label: '大于', + }, + { + value: '>=', + label: '大于等于', + }, + { + value: '<', + label: '小于', + }, + { + value: '<=', + label: '小于等于', + }, +]; +// 审批操作按钮名称 +export const OPERATION_BUTTON_NAME = new Map(); +OPERATION_BUTTON_NAME.set(OperationButtonType.APPROVE, '通过'); +OPERATION_BUTTON_NAME.set(OperationButtonType.REJECT, '拒绝'); +OPERATION_BUTTON_NAME.set(OperationButtonType.TRANSFER, '转办'); +OPERATION_BUTTON_NAME.set(OperationButtonType.DELEGATE, '委派'); +OPERATION_BUTTON_NAME.set(OperationButtonType.ADD_SIGN, '加签'); +OPERATION_BUTTON_NAME.set(OperationButtonType.RETURN, '退回'); +OPERATION_BUTTON_NAME.set(OperationButtonType.COPY, '抄送'); + +// 默认的按钮权限设置 +export const DEFAULT_BUTTON_SETTING: ButtonSetting[] = [ + { id: OperationButtonType.APPROVE, displayName: '通过', enable: true }, + { id: OperationButtonType.REJECT, displayName: '拒绝', enable: true }, + { id: OperationButtonType.TRANSFER, displayName: '转办', enable: true }, + { id: OperationButtonType.DELEGATE, displayName: '委派', enable: true }, + { id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: true }, + { id: OperationButtonType.RETURN, displayName: '退回', enable: true }, +]; + +// 办理人默认的按钮权限设置 +export const TRANSACTOR_DEFAULT_BUTTON_SETTING: ButtonSetting[] = [ + { id: OperationButtonType.APPROVE, displayName: '办理', enable: true }, + { id: OperationButtonType.REJECT, displayName: '拒绝', enable: false }, + { id: OperationButtonType.TRANSFER, displayName: '转办', enable: false }, + { id: OperationButtonType.DELEGATE, displayName: '委派', enable: false }, + { id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: false }, + { id: OperationButtonType.RETURN, displayName: '退回', enable: false }, +]; + +// 发起人的按钮权限。暂时定死,不可以编辑 +export const START_USER_BUTTON_SETTING: ButtonSetting[] = [ + { id: OperationButtonType.APPROVE, displayName: '提交', enable: true }, + { id: OperationButtonType.REJECT, displayName: '拒绝', enable: false }, + { id: OperationButtonType.TRANSFER, displayName: '转办', enable: false }, + { id: OperationButtonType.DELEGATE, displayName: '委派', enable: false }, + { id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: false }, + { id: OperationButtonType.RETURN, displayName: '退回', enable: false }, +]; + +export const MULTI_LEVEL_DEPT: DictDataType[] = [ + { label: '第 1 级部门', value: 1 }, + { label: '第 2 级部门', value: 2 }, + { label: '第 3 级部门', value: 3 }, + { label: '第 4 级部门', value: 4 }, + { label: '第 5 级部门', value: 5 }, + { label: '第 6 级部门', value: 6 }, + { label: '第 7 级部门', value: 7 }, + { label: '第 8 级部门', value: 8 }, + { label: '第 9 级部门', value: 9 }, + { label: '第 10 级部门', value: 10 }, + { label: '第 11 级部门', value: 11 }, + { label: '第 12 级部门', value: 12 }, + { label: '第 13 级部门', value: 13 }, + { label: '第 14 级部门', value: 14 }, + { label: '第 15 级部门', value: 15 }, +]; + +export const DELAY_TYPE = [ + { label: '固定时长', value: DelayTypeEnum.FIXED_TIME_DURATION }, + { label: '固定日期', value: DelayTypeEnum.FIXED_DATE_TIME }, +]; + +export const BPM_HTTP_REQUEST_PARAM_TYPES = [ + { + value: 1, + label: '固定值', + }, + { + value: 2, + label: '表单', + }, +]; + +export const TRIGGER_TYPES: DictDataType[] = [ + { label: '发送 HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST as any }, + { label: '接收 HTTP 回调', value: TriggerTypeEnum.HTTP_CALLBACK as any }, + { label: '修改表单数据', value: TriggerTypeEnum.FORM_UPDATE as any }, + { label: '删除表单数据', value: TriggerTypeEnum.FORM_DELETE as any }, +]; + +export const CHILD_PROCESS_START_USER_TYPE = [ + { + label: '同主流程发起人', + value: ChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER, + }, + { label: '从表单中获取', value: ChildProcessStartUserTypeEnum.FROM_FORM }, +]; + +export const CHILD_PROCESS_START_USER_EMPTY_TYPE = [ + { + label: '同主流程发起人', + value: ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER, + }, + { + label: '子流程管理员', + value: ChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN, + }, + { + label: '主流程管理员', + value: ChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN, + }, +]; + +export const CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE = [ + { + label: '固定数量', + value: ChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY, + }, + { + label: '数字表单', + value: ChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM, + }, + { + label: '多选表单', + value: ChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM, + }, +]; diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/helpers.ts b/apps/web-ele/src/views/bpm/components/simple-process-design/helpers.ts new file mode 100644 index 000000000..3ffc2a303 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/helpers.ts @@ -0,0 +1,792 @@ +import type { Ref } from 'vue'; + +import type { + ConditionGroup, + HttpRequestParam, + SimpleFlowNode, +} from './consts'; + +import type { BpmUserGroupApi } from '#/api/bpm/userGroup'; +import type { SystemDeptApi } from '#/api/system/dept'; +import type { SystemPostApi } from '#/api/system/post'; +import type { SystemRoleApi } from '#/api/system/role'; +import type { SystemUserApi } from '#/api/system/user'; + +import { inject, nextTick, ref, toRaw, unref, watch } from 'vue'; + +import { + BpmNodeTypeEnum, + BpmTaskStatusEnum, + ProcessVariableEnum, +} from '@vben/constants'; + +import { + ApproveMethodType, + AssignEmptyHandlerType, + AssignStartUserHandlerType, + CandidateStrategy, + COMPARISON_OPERATORS, + ConditionType, + FieldPermissionType, + NODE_DEFAULT_NAME, + RejectHandlerType, +} from './consts'; + +export function useWatchNode(props: { + flowNode: SimpleFlowNode; +}): Ref { + const node = ref(props.flowNode); + watch( + () => props.flowNode, + (newValue) => { + node.value = newValue; + }, + ); + return node; +} + +// 解析 formCreate 所有表单字段, 并返回 +function parseFormCreateFields(formFields?: string[]) { + const result: Array> = []; + if (formFields) { + formFields.forEach((fieldStr: string) => { + parseFormFields(JSON.parse(fieldStr), result); + }); + } + return result; +} + +/** + * 解析表单组件的 field, title 等字段(递归,如果组件包含子组件) + * + * @param rule 组件的生成规则 https://www.form-create.com/v3/guide/rule + * @param fields 解析后表单组件字段 + * @param parentTitle 如果是子表单,子表单的标题,默认为空 + */ +export const parseFormFields = ( + rule: Record, + fields: Array> = [], + parentTitle: string = '', +) => { + const { type, field, $required, title: tempTitle, children } = rule; + if (field && tempTitle) { + let title = tempTitle; + if (parentTitle) { + title = `${parentTitle}.${tempTitle}`; + } + let required = false; + if ($required) { + required = true; + } + fields.push({ + field, + title, + type, + required, + }); + // TODO 子表单 需要处理子表单字段 + // if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) { + // // 解析子表单的字段 + // rule.props.rule.forEach((item) => { + // parseFields(item, fieldsPermission, title) + // }) + // } + } + if (children && Array.isArray(children)) { + children.forEach((rule) => { + parseFormFields(rule, fields); + }); + } +}; + +/** + * @description 表单数据权限配置,用于发起人节点 、审批节点、抄送节点 + */ +export function useFormFieldsPermission( + defaultPermission: FieldPermissionType, +) { + // 字段权限配置. 需要有 field, title, permissioin 属性 + const fieldsPermissionConfig = ref>>([]); + + const formType = inject>('formType', ref()); // 表单类型 + + const formFields = inject>('formFields', ref([])); // 流程表单字段 + + function getNodeConfigFormFields( + nodeFormFields?: Array>, + ) { + nodeFormFields = toRaw(nodeFormFields); + fieldsPermissionConfig.value = + !nodeFormFields || nodeFormFields.length === 0 + ? getDefaultFieldsPermission(unref(formFields)) + : mergeFieldsPermission(nodeFormFields, unref(formFields)); + } + // 合并已经设置的表单字段权限,当前流程表单字段 (可能新增,或删除了字段) + function mergeFieldsPermission( + formFieldsPermisson: Array>, + formFields?: string[], + ) { + let mergedFieldsPermission: Array> = []; + if (formFields) { + mergedFieldsPermission = parseFormCreateFields(formFields).map((item) => { + const found = formFieldsPermisson.find( + (fieldPermission) => fieldPermission.field === item.field, + ); + return { + field: item.field, + title: item.title, + permission: found ? found.permission : defaultPermission, + }; + }); + } + return mergedFieldsPermission; + } + + // 默认的表单权限: 获取表单的所有字段,设置字段默认权限为只读 + function getDefaultFieldsPermission(formFields?: string[]) { + let defaultFieldsPermission: Array> = []; + if (formFields) { + defaultFieldsPermission = parseFormCreateFields(formFields).map( + (item) => { + return { + field: item.field, + title: item.title, + permission: defaultPermission, + }; + }, + ); + } + return defaultFieldsPermission; + } + + // 获取表单的所有字段,作为下拉框选项 + const formFieldOptions = parseFormCreateFields(unref(formFields)); + + return { + formType, + fieldsPermissionConfig, + formFieldOptions, + getNodeConfigFormFields, + }; +} + +/** + * @description 获取流程表单的字段 + */ +export function useFormFields() { + const formFields = inject>('formFields', ref([])); // 流程表单字段 + return parseFormCreateFields(unref(formFields)); +} + +// TODO @芋艿:后续需要把各种类似 useFormFieldsPermission 的逻辑,抽成一个通用方法。 +/** + * @description 获取流程表单的字段和发起人字段 + */ +export function useFormFieldsAndStartUser() { + const injectFormFields = inject>('formFields', ref([])); // 流程表单字段 + const formFields = parseFormCreateFields(unref(injectFormFields)); + // 添加发起人 + formFields.unshift({ + field: ProcessVariableEnum.START_USER_ID, + title: '发起人', + required: true, + }); + return formFields; +} + +export type UserTaskFormType = { + approveMethod: ApproveMethodType; + approveRatio?: number; + assignEmptyHandlerType?: AssignEmptyHandlerType; + assignEmptyHandlerUserIds?: number[]; + assignStartUserHandlerType?: AssignStartUserHandlerType; + buttonsSetting: any[]; + candidateStrategy: CandidateStrategy; + deptIds?: number[]; // 部门 + deptLevel?: number; // 部门层级 + expression?: string; // 流程表达式 + formDept?: string; // 表单内部门字段 + formUser?: string; // 表单内用户字段 + maxRemindCount?: number; + postIds?: number[]; // 岗位 + reasonRequire: boolean; + rejectHandlerType?: RejectHandlerType; + returnNodeId?: string; + roleIds?: number[]; // 角色 + signEnable: boolean; + skipExpression?: string; // 跳过表达式 + taskAssignListener?: { + body: HttpRequestParam[]; + header: HttpRequestParam[]; + }; + taskAssignListenerEnable?: boolean; + taskAssignListenerPath?: string; + taskCompleteListener?: { + body: HttpRequestParam[]; + header: HttpRequestParam[]; + }; + taskCompleteListenerEnable?: boolean; + taskCompleteListenerPath?: string; + taskCreateListener?: { + body: HttpRequestParam[]; + header: HttpRequestParam[]; + }; + taskCreateListenerEnable?: boolean; + taskCreateListenerPath?: string; + timeDuration?: number; + timeoutHandlerEnable?: boolean; + timeoutHandlerType?: number; + userGroups?: number[]; // 用户组 + userIds?: number[]; // 用户 +}; + +export type CopyTaskFormType = { + candidateStrategy: CandidateStrategy; + deptIds?: number[]; // 部门 + deptLevel?: number; // 部门层级 + expression?: string; // 流程表达式 + formDept?: string; // 表单内部门字段 + formUser?: string; // 表单内用户字段 + postIds?: number[]; // 岗位 + roleIds?: number[]; // 角色 + userGroups?: number[]; // 用户组 + userIds?: number[]; // 用户 +}; + +/** + * @description 节点表单数据。 用于审批节点、抄送节点 + */ +export function useNodeForm(nodeType: BpmNodeTypeEnum) { + const roleOptions = inject>('roleList', ref([])); // 角色列表 + const postOptions = inject>('postList', ref([])); // 岗位列表 + const userOptions = inject>('userList', ref([])); // 用户列表 + const deptOptions = inject>('deptList', ref([])); // 部门列表 + const userGroupOptions = inject>( + 'userGroupList', + ref([]), + ); // 用户组列表 + const deptTreeOptions = inject>( + 'deptTree', + ref([]), + ); // 部门树 + const formFields = inject>('formFields', ref([])); // 流程表单字段 + const configForm = ref(); + + if ( + nodeType === BpmNodeTypeEnum.USER_TASK_NODE || + nodeType === BpmNodeTypeEnum.TRANSACTOR_NODE + ) { + configForm.value = { + candidateStrategy: CandidateStrategy.USER, + approveMethod: ApproveMethodType.SEQUENTIAL_APPROVE, + approveRatio: 100, + rejectHandlerType: RejectHandlerType.FINISH_PROCESS, + assignStartUserHandlerType: AssignStartUserHandlerType.START_USER_AUDIT, + returnNodeId: '', + timeoutHandlerEnable: false, + timeoutHandlerType: 1, + timeDuration: 6, // 默认 6小时 + maxRemindCount: 1, // 默认 提醒 1次 + buttonsSetting: [], + }; + } + configForm.value = { + candidateStrategy: CandidateStrategy.USER, + }; + + function getShowText(): string { + let showText = ''; + // 指定成员 + if ( + configForm.value?.candidateStrategy === CandidateStrategy.USER && + configForm.value?.userIds?.length > 0 + ) { + const candidateNames: string[] = []; + userOptions?.value.forEach((item: any) => { + if (configForm.value?.userIds?.includes(item.id)) { + candidateNames.push(item.nickname); + } + }); + showText = `指定成员:${candidateNames.join(',')}`; + } + // 指定角色 + if ( + configForm.value?.candidateStrategy === CandidateStrategy.ROLE && + configForm.value.roleIds?.length > 0 + ) { + const candidateNames: string[] = []; + roleOptions?.value.forEach((item: any) => { + if (configForm.value?.roleIds?.includes(item.id)) { + candidateNames.push(item.name); + } + }); + showText = `指定角色:${candidateNames.join(',')}`; + } + // 指定部门 + if ( + (configForm.value?.candidateStrategy === CandidateStrategy.DEPT_MEMBER || + configForm.value?.candidateStrategy === CandidateStrategy.DEPT_LEADER || + configForm.value?.candidateStrategy === + CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) && + configForm.value?.deptIds?.length > 0 + ) { + const candidateNames: string[] = []; + deptOptions?.value.forEach((item) => { + if (configForm.value?.deptIds?.includes(item.id)) { + candidateNames.push(item.name); + } + }); + if ( + configForm.value.candidateStrategy === CandidateStrategy.DEPT_MEMBER + ) { + showText = `部门成员:${candidateNames.join(',')}`; + } else if ( + configForm.value.candidateStrategy === CandidateStrategy.DEPT_LEADER + ) { + showText = `部门的负责人:${candidateNames.join(',')}`; + } else { + showText = `多级部门的负责人:${candidateNames.join(',')}`; + } + } + + // 指定岗位 + if ( + configForm.value?.candidateStrategy === CandidateStrategy.POST && + configForm.value.postIds?.length > 0 + ) { + const candidateNames: string[] = []; + postOptions?.value.forEach((item) => { + if (configForm.value?.postIds?.includes(item.id)) { + candidateNames.push(item.name); + } + }); + showText = `指定岗位: ${candidateNames.join(',')}`; + } + // 指定用户组 + if ( + configForm.value?.candidateStrategy === CandidateStrategy.USER_GROUP && + configForm.value?.userGroups?.length > 0 + ) { + const candidateNames: string[] = []; + userGroupOptions?.value.forEach((item) => { + if (configForm.value?.userGroups?.includes(item.id)) { + candidateNames.push(item.name); + } + }); + showText = `指定用户组: ${candidateNames.join(',')}`; + } + + // 表单内用户字段 + if (configForm.value?.candidateStrategy === CandidateStrategy.FORM_USER) { + const formFieldOptions = parseFormCreateFields(unref(formFields)); + const item = formFieldOptions.find( + (item) => item.field === configForm.value?.formUser, + ); + showText = `表单用户:${item?.title}`; + } + + // 表单内部门负责人 + if ( + configForm.value?.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER + ) { + showText = `表单内部门负责人`; + } + + // 审批人自选 + if ( + configForm.value?.candidateStrategy === + CandidateStrategy.APPROVE_USER_SELECT + ) { + showText = `审批人自选`; + } + + // 发起人自选 + if ( + configForm.value?.candidateStrategy === + CandidateStrategy.START_USER_SELECT + ) { + showText = `发起人自选`; + } + // 发起人自己 + if (configForm.value?.candidateStrategy === CandidateStrategy.START_USER) { + showText = `发起人自己`; + } + // 发起人的部门负责人 + if ( + configForm.value?.candidateStrategy === + CandidateStrategy.START_USER_DEPT_LEADER + ) { + showText = `发起人的部门负责人`; + } + // 发起人的部门负责人 + if ( + configForm.value?.candidateStrategy === + CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER + ) { + showText = `发起人连续部门负责人`; + } + // 流程表达式 + if (configForm.value?.candidateStrategy === CandidateStrategy.EXPRESSION) { + showText = `流程表达式:${configForm.value.expression}`; + } + return showText; + } + + /** + * 处理候选人参数的赋值 + */ + function handleCandidateParam() { + let candidateParam: string | undefined; + if (!configForm.value) { + return candidateParam; + } + switch (configForm.value.candidateStrategy) { + case CandidateStrategy.DEPT_LEADER: + case CandidateStrategy.DEPT_MEMBER: { + candidateParam = configForm.value.deptIds?.join(','); + break; + } + case CandidateStrategy.EXPRESSION: { + candidateParam = configForm.value.expression; + break; + } + // 表单内部门的负责人 + case CandidateStrategy.FORM_DEPT_LEADER: { + // 候选人参数格式: | 分隔 。左边为表单内部门字段。 右边为部门层级 + const deptFieldOnForm = configForm.value.formDept; + candidateParam = deptFieldOnForm?.concat( + `|${configForm.value.deptLevel}`, + ); + break; + } + case CandidateStrategy.FORM_USER: { + candidateParam = configForm.value?.formUser; + break; + } + // 指定连续多级部门的负责人 + case CandidateStrategy.MULTI_LEVEL_DEPT_LEADER: { + // 候选人参数格式: | 分隔 。左边为部门(多个部门用 , 分隔)。 右边为部门层级 + const deptIds = configForm.value.deptIds?.join(','); + candidateParam = deptIds?.concat(`|${configForm.value.deptLevel}`); + break; + } + case CandidateStrategy.POST: { + candidateParam = configForm.value.postIds?.join(','); + break; + } + case CandidateStrategy.ROLE: { + candidateParam = configForm.value.roleIds?.join(','); + break; + } + // 发起人部门负责人 + case CandidateStrategy.START_USER_DEPT_LEADER: + case CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER: { + candidateParam = `${configForm.value.deptLevel}`; + break; + } + case CandidateStrategy.USER: { + candidateParam = configForm.value.userIds?.join(','); + break; + } + case CandidateStrategy.USER_GROUP: { + candidateParam = configForm.value.userGroups?.join(','); + break; + } + default: { + break; + } + } + return candidateParam; + } + /** + * 解析候选人参数 + */ + function parseCandidateParam( + candidateStrategy: CandidateStrategy, + candidateParam: string | undefined, + ) { + if (!configForm.value || !candidateParam) { + return; + } + switch (candidateStrategy) { + case CandidateStrategy.DEPT_LEADER: + case CandidateStrategy.DEPT_MEMBER: { + configForm.value.deptIds = candidateParam + .split(',') + .map((item) => +item); + break; + } + case CandidateStrategy.EXPRESSION: { + configForm.value.expression = candidateParam; + break; + } + // 表单内的部门负责人 + case CandidateStrategy.FORM_DEPT_LEADER: { + // 候选人参数格式: | 分隔 。左边为表单内的部门字段。 右边为部门层级 + const paramArray = candidateParam.split('|'); + if (paramArray.length > 1) { + configForm.value.formDept = paramArray[0]; + if (paramArray[1]) configForm.value.deptLevel = +paramArray[1]; + } + break; + } + case CandidateStrategy.FORM_USER: { + configForm.value.formUser = candidateParam; + break; + } + // 指定连续多级部门的负责人 + case CandidateStrategy.MULTI_LEVEL_DEPT_LEADER: { + // 候选人参数格式: | 分隔 。左边为部门(多个部门用 , 分隔)。 右边为部门层级 + const paramArray = candidateParam.split('|') as string[]; + if (paramArray.length > 1) { + configForm.value.deptIds = paramArray[0] + ?.split(',') + .map((item) => +item); + if (paramArray[1]) configForm.value.deptLevel = +paramArray[1]; + } + break; + } + case CandidateStrategy.POST: { + configForm.value.postIds = candidateParam + .split(',') + .map((item) => +item); + break; + } + case CandidateStrategy.ROLE: { + configForm.value.roleIds = candidateParam + .split(',') + .map((item) => +item); + break; + } + // 发起人部门负责人 + case CandidateStrategy.START_USER_DEPT_LEADER: + case CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER: { + configForm.value.deptLevel = +candidateParam; + break; + } + case CandidateStrategy.USER: { + configForm.value.userIds = candidateParam + .split(',') + .map((item) => +item); + break; + } + case CandidateStrategy.USER_GROUP: { + configForm.value.userGroups = candidateParam + .split(',') + .map((item) => +item); + break; + } + default: { + break; + } + } + } + return { + configForm, + roleOptions, + postOptions, + userOptions, + userGroupOptions, + deptTreeOptions, + handleCandidateParam, + parseCandidateParam, + getShowText, + }; +} + +/** + * @description 抽屉配置 + */ +export function useDrawer() { + // 抽屉配置是否可见 + const settingVisible = ref(false); + // 关闭配置抽屉 + function closeDrawer() { + settingVisible.value = false; + } + // 打开配置抽屉 + function openDrawer() { + settingVisible.value = true; + } + return { + settingVisible, + closeDrawer, + openDrawer, + }; +} + +/** + * @description 节点名称配置 + */ +export function useNodeName(nodeType: BpmNodeTypeEnum) { + // 节点名称 + const nodeName = ref(); + // 节点名称输入框 + const showInput = ref(false); + // 输入框的引用 + const inputRef = ref(null); + // 点击节点名称编辑图标 + function clickIcon() { + showInput.value = true; + } + // 修改节点名称 + function changeNodeName() { + showInput.value = false; + nodeName.value = + nodeName.value || (NODE_DEFAULT_NAME.get(nodeType) as string); + } + // 监听 showInput 的变化,当变为 true 时自动聚焦 + watch(showInput, (value) => { + if (value) { + nextTick(() => { + inputRef.value?.focus(); + }); + } + }); + + return { + nodeName, + showInput, + inputRef, + clickIcon, + changeNodeName, + }; +} + +export function useNodeName2( + node: Ref, + nodeType: BpmNodeTypeEnum, +) { + // 显示节点名称输入框 + const showInput = ref(false); + // 输入框的引用 + const inputRef = ref(null); + + // 监听 showInput 的变化,当变为 true 时自动聚焦 + watch(showInput, (value) => { + if (value) { + nextTick(() => { + inputRef.value?.focus(); + }); + } + }); + + // 修改节点名称 + function changeNodeName() { + showInput.value = false; + node.value.name = + node.value.name || (NODE_DEFAULT_NAME.get(nodeType) as string); + console.warn('node.value.name===>', node.value.name); + } + // 点击节点标题进行输入 + function clickTitle() { + showInput.value = true; + } + return { + showInput, + inputRef, + clickTitle, + changeNodeName, + }; +} + +/** + * @description 根据节点任务状态,获取节点任务状态样式 + */ +export function useTaskStatusClass( + taskStatus: BpmTaskStatusEnum | undefined, +): string { + if (!taskStatus) { + return ''; + } + if (taskStatus === BpmTaskStatusEnum.APPROVE) { + return 'status-pass'; + } + if (taskStatus === BpmTaskStatusEnum.RUNNING) { + return 'status-running'; + } + if (taskStatus === BpmTaskStatusEnum.REJECT) { + return 'status-reject'; + } + if (taskStatus === BpmTaskStatusEnum.CANCEL) { + return 'status-cancel'; + } + return ''; +} + +/** 条件组件文字展示 */ +export function getConditionShowText( + conditionType: ConditionType | undefined, + conditionExpression: string | undefined, + conditionGroups: ConditionGroup | undefined, + fieldOptions: Array>, +) { + let showText: string | undefined; + if (conditionType === ConditionType.EXPRESSION && conditionExpression) { + showText = `表达式:${conditionExpression}`; + } + if (conditionType === ConditionType.RULE) { + // 条件组是否为与关系 + const groupAnd = conditionGroups?.and; + let warningMessage: string | undefined; + const conditionGroup = conditionGroups?.conditions.map((item) => { + return `(${item.rules + .map((rule) => { + if (rule.leftSide && rule.rightSide) { + return `${getFormFieldTitle( + fieldOptions, + rule.leftSide, + )} ${getOpName(rule.opCode)} ${rule.rightSide}`; + } else { + // 有一条规则不完善。提示错误 + warningMessage = '请完善条件规则'; + return ''; + } + }) + .join(item.and ? ' 且 ' : ' 或 ')} ) `; + }); + showText = warningMessage + ? '' + : conditionGroup?.join(groupAnd ? ' 且 ' : ' 或 '); + } + return showText; +} + +/** 获取表单字段名称*/ +function getFormFieldTitle( + fieldOptions: Array>, + field: string, +) { + const item = fieldOptions.find((item) => item.field === field); + return item?.title; +} + +/** 获取操作符名称 */ +function getOpName(opCode: string): string | undefined { + const opName = COMPARISON_OPERATORS.find( + (item: any) => item.value === opCode, + ); + return opName?.label; +} + +/** 获取条件节点默认的名称 */ +export function getDefaultConditionNodeName( + index: number, + defaultFlow: boolean | undefined, +): string { + if (defaultFlow) { + return '其它情况'; + } + return `条件${index + 1}`; +} + +/** 获取包容分支条件节点默认的名称 */ +export function getDefaultInclusiveConditionNodeName( + index: number, + defaultFlow: boolean | undefined, +): string { + if (defaultFlow) { + return '其它情况'; + } + return `包容条件${index + 1}`; +} diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/index.ts b/apps/web-ele/src/views/bpm/components/simple-process-design/index.ts new file mode 100644 index 000000000..41269eae8 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/index.ts @@ -0,0 +1,11 @@ +import './styles/simple-process-designer.scss'; + +export { default as HttpRequestSetting } from './components/nodes-config/modules/http-request-setting.vue'; + +export { default as SimpleProcessDesigner } from './components/simple-process-designer.vue'; + +export { default as SimpleProcessViewer } from './components/simple-process-viewer.vue'; + +export type { SimpleFlowNode } from './consts'; + +export { parseFormFields } from './helpers'; diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.ttf b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.ttf new file mode 100644 index 000000000..06f4e31c4 Binary files /dev/null and b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.ttf differ diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.woff b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.woff new file mode 100644 index 000000000..0724e7505 Binary files /dev/null and b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.woff differ diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.woff2 b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.woff2 new file mode 100644 index 000000000..c904bb67d Binary files /dev/null and b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/iconfont.woff2 differ diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/styles/simple-process-designer.scss b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/simple-process-designer.scss new file mode 100644 index 000000000..a36f96986 --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/simple-process-designer.scss @@ -0,0 +1,759 @@ +// TODO 整个样式是不是要重新优化一下 +// iconfont 样式 +@font-face { + font-family: iconfont; /* Project id 4495938 */ + src: + url('iconfont.woff2?t=1737639517142') format('woff2'), + url('iconfont.woff?t=1737639517142') format('woff'), + url('iconfont.ttf?t=1737639517142') format('truetype'); +} +// 配置节点头部 +.config-header { + display: flex; + flex-direction: column; + + .node-name { + display: flex; + align-items: center; + height: 24px; + font-size: 16px; + line-height: 24px; + cursor: pointer; + } + + .divide-line { + width: 100%; + height: 1px; + margin-top: 16px; + background: #eee; + } + + .config-editable-input { + max-width: 510px; + height: 24px; + font-size: 16px; + line-height: 24px; + border: 1px solid #d9d9d9; + border-radius: 4px; + transition: all 0.3s; + + &:focus { + outline: 0; + border-color: #40a9ff; + box-shadow: 0 0 0 2px rgb(24 144 255 / 20%); + } + } +} +// 节点连线气泡卡片样式 +.handler-item-wrapper { + display: flex; + flex-wrap: wrap; + width: 320px; + cursor: pointer; + + .handler-item { + display: flex; + flex-direction: column; + align-items: center; + margin-top: 12px; + } + + .handler-item-icon { + width: 50px; + height: 50px; + text-align: center; + user-select: none; + background: #fff; + border: 1px solid #e2e2e2; + border-radius: 50%; + + &:hover { + background: #e2e2e2; + box-shadow: 0 2px 4px 0 rgb(0 0 0 / 10%); + } + + .icon-size { + font-size: 25px; + line-height: 50px; + } + } + + .approve { + color: #ff943e; + } + + .copy { + color: #3296fa; + } + + .condition { + color: #67c23a; + } + + .parallel { + color: #626aef; + } + + .inclusive { + color: #345da2; + } + + .delay { + color: #e47470; + } + + .trigger { + color: #3373d2; + } + + .router { + color: #ca3a31; + } + + .transactor { + color: #309; + } + + .child-process { + color: #963; + } + + .async-child-process { + color: #066; + } + + .handler-item-text { + width: 80px; + margin-top: 4px; + font-size: 13px; + text-align: center; + } +} +// Simple 流程模型样式 +.simple-process-model-container { + width: 100%; + height: 100%; + padding-top: 32px; + overflow-x: auto; + background-color: #fafafa; + + .simple-process-model { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: fit-content; + background: url('./svg/simple-process-bg.svg') 0 0 repeat; + transform: scale(1); + transform-origin: 50% 0 0; + transition: transform 0.3s cubic-bezier(0.645, 0.045, 0.355, 1); + // 节点容器 定义节点宽度 + .node-container { + width: 200px; + } + // 节点 + .node-box { + position: relative; + display: flex; + flex-direction: column; + min-height: 70px; + padding: 5px 10px 8px; + cursor: pointer; + background-color: #fff; + border: 2px solid transparent; + border-radius: 8px; + box-shadow: 0 1px 4px 0 rgb(10 30 65 / 16%); + transition: all 0.1s cubic-bezier(0.645, 0.045, 0.355, 1); + + &.status-pass { + background-color: #a9da90; + border-color: #67c23a; + } + + &.status-pass:hover { + border-color: #67c23a; + } + + &.status-running { + background-color: #e7f0fe; + border-color: #5a9cf8; + } + + &.status-running:hover { + border-color: #5a9cf8; + } + + &.status-reject { + background-color: #f6e5e5; + border-color: #e47470; + } + + &.status-reject:hover { + border-color: #e47470; + } + + &:hover { + border-color: #0089ff; + + .node-toolbar { + opacity: 1; + } + + .branch-node-move { + display: flex; + } + } + + // 普通节点标题 + .node-title-container { + display: flex; + align-items: center; + padding: 4px; + cursor: pointer; + border-radius: 4px 4px 0 0; + + .node-title-icon { + display: flex; + align-items: center; + + &.user-task { + color: #ff943e; + } + + &.copy-task { + color: #3296fa; + } + + &.start-user { + color: #676565; + } + + &.delay-node { + color: #e47470; + } + + &.trigger-node { + color: #3373d2; + } + + &.router-node { + color: #ca3a31; + } + + &.transactor-task { + color: #309; + } + + &.child-process { + color: #963; + } + + &.async-child-process { + color: #066; + } + } + + .node-title { + margin-left: 4px; + overflow: hidden; + text-overflow: ellipsis; + font-size: 14px; + font-weight: 600; + line-height: 18px; + color: #1f1f1f; + white-space: nowrap; + + &:hover { + border-bottom: 1px dashed #f60; + } + } + } + + // 条件节点标题 + .branch-node-title-container { + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 0; + cursor: pointer; + border-radius: 4px 4px 0 0; + + .input-max-width { + max-width: 115px !important; + } + + .branch-title { + overflow: hidden; + text-overflow: ellipsis; + font-size: 13px; + font-weight: 600; + color: #f60; + white-space: nowrap; + + &:hover { + border-bottom: 1px dashed #000; + } + } + + .branch-priority { + min-width: 50px; + font-size: 12px; + } + } + + .node-content { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 32px; + padding: 4px 8px; + margin-top: 4px; + line-height: 32px; + color: #111f2c; + background: rgb(0 0 0 / 3%); + border-radius: 4px; + + .node-text { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 2; /* 这将限制文本显示为两行 */ + font-size: 14px; + line-height: 24px; + word-break: break-all; + -webkit-box-orient: vertical; + } + } + + //条件节点内容 + .branch-node-content { + display: flex; + align-items: center; + min-height: 32px; + padding: 4px 0; + margin-top: 4px; + line-height: 32px; + color: #111f2c; + background: rgb(0 0 0 / 3%); + border-radius: 4px; + + .branch-node-text { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 1; /* 这将限制文本显示为一行 */ + font-size: 12px; + line-height: 24px; + word-break: break-all; + -webkit-box-orient: vertical; + } + } + + // 节点操作 :删除 + .node-toolbar { + position: absolute; + top: -20px; + right: 0; + display: flex; + opacity: 0; + + .toolbar-icon { + vertical-align: middle; + text-align: center; + } + } + + // 条件节点左右移动 + .branch-node-move { + position: absolute; + display: none; + align-items: center; + justify-content: center; + width: 10px; + height: 100%; + cursor: pointer; + } + + .move-node-left { + top: 0; + left: -2px; + background: rgb(126 134 142 / 8%); + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; + } + + .move-node-right { + top: 0; + right: -2px; + background: rgb(126 134 142 / 8%); + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; + } + } + + .node-config-error { + border-color: #ff5219 !important; + } + // 普通节点包装 + .node-wrapper { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + // 节点连线处理 + .node-handler-wrapper { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 70px; + user-select: none; + + &::before { + position: absolute; + top: 0; + z-index: 0; + width: 2px; + height: 100%; + margin: auto; + content: ''; + background-color: #dedede; + } + + .node-handler { + .add-icon { + position: relative; + top: -5px; + display: flex; + align-items: center; + justify-content: center; + width: 25px; + height: 25px; + color: #fff; + cursor: pointer; + background-color: #0089ff; + border-radius: 50%; + + &:hover { + transform: scale(1.1); + } + } + } + + .node-handler-arrow { + position: absolute; + bottom: 0; + left: 50%; + display: flex; + transform: translateX(-50%); + } + } + + // 条件节点包装 + .branch-node-wrapper { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-top: 16px; + + .branch-node-container { + position: relative; + display: flex; + min-width: fit-content; + + &::before { + position: absolute; + left: 50%; + width: 4px; + height: 100%; + content: ''; + background-color: #fafafa; + transform: translate(-50%); + } + + .branch-node-add { + position: absolute; + top: -18px; + left: 50%; + z-index: 1; + display: flex; + align-items: center; + height: 36px; + padding: 0 10px; + font-size: 12px; + line-height: 36px; + border: 2px solid #dedede; + border-radius: 18px; + transform: translateX(-50%); + transform-origin: center center; + } + + .branch-node-readonly { + position: absolute; + top: -18px; + left: 50%; + z-index: 1; + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + background-color: #fff; + border: 2px solid #dedede; + border-radius: 50%; + transform: translateX(-50%); + transform-origin: center center; + + &.status-pass { + background-color: #e9f4e2; + border-color: #6bb63c; + } + + &.status-pass:hover { + border-color: #6bb63c; + } + + .icon-size { + font-size: 22px; + + &.condition { + color: #67c23a; + } + + &.parallel { + color: #626aef; + } + + &.inclusive { + color: #345da2; + } + } + } + + .branch-node-item { + position: relative; + display: flex; + flex-shrink: 0; + flex-direction: column; + align-items: center; + min-width: 280px; + padding: 40px 40px 0; + background: transparent; + border-top: 2px solid #dedede; + border-bottom: 2px solid #dedede; + + &::before { + position: absolute; + inset: 0; + width: 2px; + height: 100%; + margin: auto; + content: ''; + background-color: #dedede; + } + } + // 覆盖条件节点第一个节点左上角的线 + .branch-line-first-top { + position: absolute; + top: -5px; + left: -1px; + width: 50%; + height: 7px; + content: ''; + background-color: #fafafa; + } + // 覆盖条件节点第一个节点左下角的线 + .branch-line-first-bottom { + position: absolute; + bottom: -5px; + left: -1px; + width: 50%; + height: 7px; + content: ''; + background-color: #fafafa; + } + // 覆盖条件节点最后一个节点右上角的线 + .branch-line-last-top { + position: absolute; + top: -5px; + right: -1px; + width: 50%; + height: 7px; + content: ''; + background-color: #fafafa; + } + // 覆盖条件节点最后一个节点右下角的线 + .branch-line-last-bottom { + position: absolute; + right: -1px; + bottom: -5px; + width: 50%; + height: 7px; + content: ''; + background-color: #fafafa; + } + } + } + + .node-fixed-name { + display: inline-block; + width: auto; + padding: 0 4px; + overflow: hidden; + text-overflow: ellipsis; + text-align: center; + white-space: nowrap; + } + // 开始节点包装 + .start-node-wrapper { + position: relative; + margin-top: 16px; + + .start-node-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + + .start-node-box { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + width: 90px; + height: 36px; + padding: 3px 4px; + color: #212121; + cursor: pointer; + background: #fafafa; + border-radius: 30px; + box-shadow: 0 1px 5px 0 rgb(10 30 65 / 8%); + } + } + } + + // 结束节点包装 + .end-node-wrapper { + margin-bottom: 16px; + + .end-node-box { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + width: 80px; + height: 36px; + color: #212121; + background-color: #fff; + border: 2px solid transparent; + border-radius: 30px; + box-shadow: 0 1px 5px 0 rgb(10 30 65 / 8%); + + &.status-pass { + background-color: #a9da90; + border-color: #6bb63c; + } + + &.status-pass:hover { + border-color: #6bb63c; + } + + &.status-reject { + background-color: #f6e5e5; + border-color: #e47470; + } + + &.status-reject:hover { + border-color: #e47470; + } + + &.status-cancel { + background-color: #eaeaeb; + border-color: #919398; + } + + &.status-cancel:hover { + border-color: #919398; + } + } + } + + // 可编辑的 title 输入框 + .editable-title-input { + max-width: 145px; + height: 20px; + margin-left: 4px; + font-size: 12px; + line-height: 20px; + border: 1px solid #d9d9d9; + border-radius: 4px; + transition: all 0.3s; + + &:focus { + outline: 0; + border-color: #40a9ff; + box-shadow: 0 0 0 2px rgb(24 144 255 / 20%); + } + } + } +} + +.iconfont { + font-family: iconfont !important; + font-size: 16px; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-trigger::before { + content: '\e6d3'; +} + +.icon-router::before { + content: '\e6b2'; +} + +.icon-delay::before { + content: '\e600'; +} + +.icon-start-user::before { + content: '\e679'; +} + +.icon-inclusive::before { + content: '\e602'; +} + +.icon-copy::before { + content: '\e7eb'; +} + +.icon-transactor::before { + content: '\e61c'; +} + +.icon-exclusive::before { + content: '\e717'; +} + +.icon-approve::before { + content: '\e715'; +} + +.icon-parallel::before { + content: '\e688'; +} + +.icon-async-child-process::before { + content: '\e6f2'; +} + +.icon-child-process::before { + content: '\e6c1'; +} diff --git a/apps/web-ele/src/views/bpm/components/simple-process-design/styles/svg/simple-process-bg.svg b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/svg/simple-process-bg.svg new file mode 100644 index 000000000..eb23ab5ae --- /dev/null +++ b/apps/web-ele/src/views/bpm/components/simple-process-design/styles/svg/simple-process-bg.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web-ele/src/views/bpm/form/data.ts b/apps/web-ele/src/views/bpm/form/data.ts new file mode 100644 index 000000000..315c41db6 --- /dev/null +++ b/apps/web-ele/src/views/bpm/form/data.ts @@ -0,0 +1,61 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '表单名称', + component: 'Input', + componentProps: { + placeholder: '请输入表单名称', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '表单名称', + minWidth: 200, + }, + { + field: 'status', + title: '状态', + minWidth: 200, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/form/designer/data.ts b/apps/web-ele/src/views/bpm/form/designer/data.ts new file mode 100644 index 000000000..20740cbbb --- /dev/null +++ b/apps/web-ele/src/views/bpm/form/designer/data.ts @@ -0,0 +1,46 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '表单名称', + component: 'Input', + componentProps: { + placeholder: '请输入表单名称', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/form/designer/index.vue b/apps/web-ele/src/views/bpm/form/designer/index.vue new file mode 100644 index 000000000..f3e03e0c2 --- /dev/null +++ b/apps/web-ele/src/views/bpm/form/designer/index.vue @@ -0,0 +1,154 @@ + + + diff --git a/apps/web-ele/src/views/bpm/form/designer/modules/form.vue b/apps/web-ele/src/views/bpm/form/designer/modules/form.vue new file mode 100644 index 000000000..9b9587030 --- /dev/null +++ b/apps/web-ele/src/views/bpm/form/designer/modules/form.vue @@ -0,0 +1,111 @@ + + + diff --git a/apps/web-ele/src/views/bpm/form/index.vue b/apps/web-ele/src/views/bpm/form/index.vue new file mode 100644 index 000000000..c4e612544 --- /dev/null +++ b/apps/web-ele/src/views/bpm/form/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/apps/web-ele/src/views/bpm/form/modules/detail.vue b/apps/web-ele/src/views/bpm/form/modules/detail.vue new file mode 100644 index 000000000..ad23d235b --- /dev/null +++ b/apps/web-ele/src/views/bpm/form/modules/detail.vue @@ -0,0 +1,49 @@ + + + diff --git a/apps/web-ele/src/views/bpm/group/data.ts b/apps/web-ele/src/views/bpm/group/data.ts new file mode 100644 index 000000000..4ecf77cf8 --- /dev/null +++ b/apps/web-ele/src/views/bpm/group/data.ts @@ -0,0 +1,167 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { h } from 'vue'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { ElTag } from 'element-plus'; + +import { z } from '#/adapter/form'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '组名', + component: 'Input', + componentProps: { + placeholder: '请输入组名', + }, + rules: 'required', + }, + { + fieldName: 'description', + label: '描述', + component: 'Textarea', + componentProps: { + placeholder: '请输入描述', + }, + }, + { + fieldName: 'userIds', + label: '成员', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择成员', + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + mode: 'tags', + }, + rules: z.array(z.number()).min(1, '请选择成员').default([]), + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '组名', + component: 'Input', + componentProps: { + placeholder: '请输入组名', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '组名', + minWidth: 200, + }, + { + field: 'description', + title: '描述', + minWidth: 200, + }, + { + field: 'userIds', + title: '成员', + minWidth: 200, + slots: { + default: ({ row }) => { + const userIds = row.userIds || []; + return userIds.map((userId: number) => + h( + ElTag, + { + type: 'primary', + class: 'mr-1', + }, + () => userList.find((u) => u.id === userId)?.nickname, + ), + ); + }, + }, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/group/index.vue b/apps/web-ele/src/views/bpm/group/index.vue new file mode 100644 index 000000000..bc65d6112 --- /dev/null +++ b/apps/web-ele/src/views/bpm/group/index.vue @@ -0,0 +1,129 @@ + + + diff --git a/apps/web-ele/src/views/bpm/group/modules/form.vue b/apps/web-ele/src/views/bpm/group/modules/form.vue new file mode 100644 index 000000000..c470eedde --- /dev/null +++ b/apps/web-ele/src/views/bpm/group/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/bpm/model/data.ts b/apps/web-ele/src/views/bpm/model/data.ts new file mode 100644 index 000000000..59405e77a --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/data.ts @@ -0,0 +1,49 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { BpmModelApi } from '#/api/bpm/model'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '流程名称', + minWidth: 200, + slots: { default: 'name' }, + }, + { + field: 'startUserIds', + title: '可见范围', + minWidth: 150, + slots: { default: 'startUserIds' }, + }, + { + field: 'type', + title: '流程类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_MODEL_TYPE }, + }, + }, + { + field: 'formType', + title: '表单信息', + minWidth: 150, + slots: { default: 'formInfo' }, + }, + { + field: 'deploymentTime', + title: '最后发布', + minWidth: 280, + slots: { default: 'deploymentTime' }, + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/model/definition/data.ts b/apps/web-ele/src/views/bpm/model/definition/data.ts new file mode 100644 index 000000000..c5dff8010 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/definition/data.ts @@ -0,0 +1,73 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '定义编号', + minWidth: 250, + }, + { + field: 'name', + title: '流程名称', + minWidth: 150, + }, + { + field: 'icon', + title: '流程图标', + minWidth: 100, + cellRender: { + name: 'CellImage', + props: { + width: 32, + height: 32, + class: 'rounded', + }, + }, + }, + { + field: 'startUsers', + title: '可见范围', + minWidth: 100, + slots: { default: 'startUsers' }, + }, + { + field: 'modelType', + title: '流程类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_MODEL_TYPE }, + }, + }, + { + field: 'formType', + title: '表单信息', + minWidth: 150, + slots: { default: 'formInfo' }, + }, + { + field: 'version', + title: '流程版本', + minWidth: 80, + cellRender: { + name: 'CellTag', + }, + }, + { + field: 'deploymentTime', + title: '部署时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/model/definition/index.vue b/apps/web-ele/src/views/bpm/model/definition/index.vue new file mode 100644 index 000000000..7934c7617 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/definition/index.vue @@ -0,0 +1,151 @@ + + + diff --git a/apps/web-ele/src/views/bpm/model/form/index.vue b/apps/web-ele/src/views/bpm/model/form/index.vue new file mode 100644 index 000000000..8c42659c1 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/index.vue @@ -0,0 +1,498 @@ + + + diff --git a/apps/web-ele/src/views/bpm/model/form/modules/basic-info.vue b/apps/web-ele/src/views/bpm/model/form/modules/basic-info.vue new file mode 100644 index 000000000..8d1bdc79d --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/modules/basic-info.vue @@ -0,0 +1,450 @@ + + + diff --git a/apps/web-ele/src/views/bpm/model/form/modules/bpm-model-editor.vue b/apps/web-ele/src/views/bpm/model/form/modules/bpm-model-editor.vue new file mode 100644 index 000000000..4f756afc3 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/modules/bpm-model-editor.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/apps/web-ele/src/views/bpm/model/form/modules/custom-print-template.vue b/apps/web-ele/src/views/bpm/model/form/modules/custom-print-template.vue new file mode 100644 index 000000000..3a75ed61d --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/modules/custom-print-template.vue @@ -0,0 +1,117 @@ + + + diff --git a/apps/web-ele/src/views/bpm/model/form/modules/extra-setting.vue b/apps/web-ele/src/views/bpm/model/form/modules/extra-setting.vue new file mode 100644 index 000000000..739229387 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/modules/extra-setting.vue @@ -0,0 +1,618 @@ + + diff --git a/apps/web-ele/src/views/bpm/model/form/modules/form-design.vue b/apps/web-ele/src/views/bpm/model/form/modules/form-design.vue new file mode 100644 index 000000000..534001d77 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/modules/form-design.vue @@ -0,0 +1,184 @@ + + diff --git a/apps/web-ele/src/views/bpm/model/form/modules/process-design.vue b/apps/web-ele/src/views/bpm/model/form/modules/process-design.vue new file mode 100644 index 000000000..8f2495b75 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/modules/process-design.vue @@ -0,0 +1,81 @@ + + diff --git a/apps/web-ele/src/views/bpm/model/form/modules/simple-model-design.vue b/apps/web-ele/src/views/bpm/model/form/modules/simple-model-design.vue new file mode 100644 index 000000000..ba8ae33a8 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/modules/simple-model-design.vue @@ -0,0 +1,48 @@ + + diff --git a/apps/web-ele/src/views/bpm/model/form/modules/tinymce-plugin.ts b/apps/web-ele/src/views/bpm/model/form/modules/tinymce-plugin.ts new file mode 100644 index 000000000..145dc894b --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/form/modules/tinymce-plugin.ts @@ -0,0 +1,78 @@ +/** TinyMCE 自定义功能: + * - processrecord 按钮:插入流程记录占位元素 + * - @ 自动补全:插入 mention 占位元素 + */ + +// @ts-ignore TinyMCE 全局或通过打包器提供 +import type { Editor } from 'tinymce'; + +export interface MentionItem { + id: string; + name: string; +} + +/** 在编辑器 setup 回调中注册流程记录按钮和 @ 自动补全 */ +export function setupTinyPlugins( + editor: Editor, + getMentionList: () => MentionItem[], +) { + // 按钮:流程记录 + editor.ui.registry.addButton('processrecord', { + text: '流程记录', + tooltip: '插入流程记录占位', + onAction: () => { + // 流程记录占位显示, 仅用于显示。process-print.vue 组件中会替换掉 + editor.insertContent( + [ + '
', + '', + '', + '', + '', + '', + '', + '
流程记录
节点操作
', + '
', + ].join(''), + ); + }, + }); + + // @ 自动补全 + editor.ui.registry.addAutocompleter('bpmMention', { + trigger: '@', + minChars: 0, + columns: 1, + fetch: ( + pattern: string, + _maxResults: number, + _fetchOptions: Record, + ) => { + const list = getMentionList(); + const keyword = (pattern || '').toLowerCase().trim(); + const data = list + .filter((i) => i.name.toLowerCase().includes(keyword)) + .map((i) => ({ + value: i.id, + text: i.name, + })); + return Promise.resolve(data); + }, + onAction: ( + autocompleteApi: any, + rng: Range, + value: string, + _meta: Record, + ) => { + const list = getMentionList(); + const item = list.find((i) => i.id === value); + const name = item ? item.name : value; + const info = encodeURIComponent(JSON.stringify({ id: value })); + editor.selection.setRng(rng); + editor.insertContent( + `@${name}`, + ); + autocompleteApi.hide(); + }, + }); +} diff --git a/apps/web-ele/src/views/bpm/model/index.vue b/apps/web-ele/src/views/bpm/model/index.vue new file mode 100644 index 000000000..e66062b6b --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/index.vue @@ -0,0 +1,220 @@ + + + diff --git a/apps/web-ele/src/views/bpm/model/modules/category-draggable-model.vue b/apps/web-ele/src/views/bpm/model/modules/category-draggable-model.vue new file mode 100644 index 000000000..bb6fd0058 --- /dev/null +++ b/apps/web-ele/src/views/bpm/model/modules/category-draggable-model.vue @@ -0,0 +1,750 @@ + + + + + diff --git a/apps/web-ele/src/views/bpm/processExpression/data.ts b/apps/web-ele/src/views/bpm/processExpression/data.ts new file mode 100644 index 000000000..76b16d2e1 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processExpression/data.ts @@ -0,0 +1,126 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'expression', + label: '表达式', + component: 'Textarea', + componentProps: { + placeholder: '请输入表达式', + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '名字', + minWidth: 200, + }, + + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'expression', + title: '表达式', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/processExpression/index.vue b/apps/web-ele/src/views/bpm/processExpression/index.vue new file mode 100644 index 000000000..658ea4544 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processExpression/index.vue @@ -0,0 +1,135 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processExpression/modules/form.vue b/apps/web-ele/src/views/bpm/processExpression/modules/form.vue new file mode 100644 index 000000000..9fb981483 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processExpression/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/create/index.vue b/apps/web-ele/src/views/bpm/processInstance/create/index.vue new file mode 100644 index 000000000..df5507705 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/create/index.vue @@ -0,0 +1,339 @@ + + + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/create/modules/form.vue b/apps/web-ele/src/views/bpm/processInstance/create/modules/form.vue new file mode 100644 index 000000000..7ba63b5fa --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/create/modules/form.vue @@ -0,0 +1,372 @@ + + + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/data.ts b/apps/web-ele/src/views/bpm/processInstance/data.ts new file mode 100644 index 000000000..883cc97e2 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/data.ts @@ -0,0 +1,122 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getCategorySimpleList } from '#/api/bpm/category'; +import { getSimpleProcessDefinitionList } from '#/api/bpm/definition'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '流程名称', + component: 'Input', + componentProps: { + placeholder: '请输入流程名称', + allowClear: true, + }, + }, + { + fieldName: 'processDefinitionId', + label: '所属流程', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择流程定义', + allowClear: true, + api: getSimpleProcessDefinitionList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'category', + label: '流程分类', + component: 'ApiSelect', + componentProps: { + placeholder: '请输入流程分类', + allowClear: true, + api: getCategorySimpleList, + labelField: 'name', + valueField: 'code', + }, + }, + { + fieldName: 'status', + label: '流程状态', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, + 'number', + ), + placeholder: '请选择流程状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '发起时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '流程名称', + minWidth: 200, + fixed: 'left', + }, + { + field: 'summary', + title: '摘要', + minWidth: 200, + slots: { + default: 'slot-summary', + }, + }, + { + field: 'categoryName', + title: '流程分类', + minWidth: 120, + fixed: 'left', + }, + { + field: 'status', + title: '流程状态', + minWidth: 250, + slots: { + default: 'slot-status', + }, + }, + { + field: 'startTime', + title: '发起时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'endTime', + title: '结束时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/processInstance/detail/index.vue b/apps/web-ele/src/views/bpm/processInstance/detail/index.vue new file mode 100644 index 000000000..fad5ed688 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/detail/index.vue @@ -0,0 +1,423 @@ + + + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/detail/modules/bpm-viewer.vue b/apps/web-ele/src/views/bpm/processInstance/detail/modules/bpm-viewer.vue new file mode 100644 index 000000000..2b9dfdeee --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/detail/modules/bpm-viewer.vue @@ -0,0 +1,59 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/detail/modules/operation-button.vue b/apps/web-ele/src/views/bpm/processInstance/detail/modules/operation-button.vue new file mode 100644 index 000000000..19a5fd407 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/detail/modules/operation-button.vue @@ -0,0 +1,1424 @@ + + diff --git a/apps/web-ele/src/views/bpm/processInstance/detail/modules/process-print.vue b/apps/web-ele/src/views/bpm/processInstance/detail/modules/process-print.vue new file mode 100644 index 000000000..85693735b --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/detail/modules/process-print.vue @@ -0,0 +1,299 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/detail/modules/signature.vue b/apps/web-ele/src/views/bpm/processInstance/detail/modules/signature.vue new file mode 100644 index 000000000..a3f1b0861 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/detail/modules/signature.vue @@ -0,0 +1,64 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue b/apps/web-ele/src/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue new file mode 100644 index 000000000..751f390ca --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue @@ -0,0 +1,178 @@ + + diff --git a/apps/web-ele/src/views/bpm/processInstance/detail/modules/task-list.vue b/apps/web-ele/src/views/bpm/processInstance/detail/modules/task-list.vue new file mode 100644 index 000000000..5b64e38ad --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/detail/modules/task-list.vue @@ -0,0 +1,197 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/detail/modules/time-line.vue b/apps/web-ele/src/views/bpm/processInstance/detail/modules/time-line.vue new file mode 100644 index 000000000..f085a6385 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/detail/modules/time-line.vue @@ -0,0 +1,486 @@ + + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/index.vue b/apps/web-ele/src/views/bpm/processInstance/index.vue new file mode 100644 index 000000000..52936bcbc --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/index.vue @@ -0,0 +1,205 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/manager/data.ts b/apps/web-ele/src/views/bpm/processInstance/manager/data.ts new file mode 100644 index 000000000..a356cd076 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/manager/data.ts @@ -0,0 +1,156 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getCategorySimpleList } from '#/api/bpm/category'; +import { getSimpleProcessDefinitionList } from '#/api/bpm/definition'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'startUserId', + label: '发起人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择发起人', + allowClear: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'name', + label: '流程名称', + component: 'Input', + componentProps: { + placeholder: '请输入流程名称', + allowClear: true, + }, + }, + { + fieldName: 'processDefinitionId', + label: '所属流程', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择流程定义', + allowClear: true, + api: getSimpleProcessDefinitionList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'category', + label: '流程分类', + component: 'ApiSelect', + componentProps: { + placeholder: '请输入流程分类', + allowClear: true, + api: getCategorySimpleList, + labelField: 'name', + valueField: 'code', + }, + }, + { + fieldName: 'status', + label: '流程状态', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, + 'number', + ), + placeholder: '请选择流程状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '发起时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '流程编号', + minWidth: 320, + fixed: 'left', + }, + { + field: 'name', + title: '流程名称', + minWidth: 200, + fixed: 'left', + }, + { + field: 'categoryName', + title: '流程分类', + minWidth: 120, + fixed: 'left', + }, + { + field: 'startUser.nickname', + title: '流程发起人', + minWidth: 120, + }, + { + field: 'startUser.deptName', + title: '发起部门', + minWidth: 120, + }, + { + field: 'status', + title: '流程状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS }, + }, + }, + { + field: 'startTime', + title: '发起时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'endTime', + title: '结束时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'durationInMillis', + title: '流程耗时', + minWidth: 180, + formatter: 'formatPast2', + }, + { + field: 'tasks', + title: '当前审批任务', + minWidth: 320, + slots: { default: 'tasks' }, + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/processInstance/manager/index.vue b/apps/web-ele/src/views/bpm/processInstance/manager/index.vue new file mode 100644 index 000000000..0afcbc425 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/manager/index.vue @@ -0,0 +1,148 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processInstance/report/data.ts b/apps/web-ele/src/views/bpm/processInstance/report/data.ts new file mode 100644 index 000000000..dc099b5bf --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/report/data.ts @@ -0,0 +1,157 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { + VxeGridPropTypes, + VxeTableGridOptions, +} from '#/adapter/vxe-table'; +import type { BpmProcessInstanceApi } from '#/api/bpm/processInstance'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +interface FormField { + field: string; + title: string; + type: string; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema( + formFields: FormField[] = [], +): VbenFormSchema[] { + // 基础搜索字段配置 + const baseFormSchema: VbenFormSchema[] = [ + { + fieldName: 'startUserId', + label: '发起人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择发起人', + allowClear: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'name', + label: '流程名称', + component: 'Input', + componentProps: { + placeholder: '请输入流程名称', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '流程状态', + component: 'Select', + componentProps: { + placeholder: '请选择流程状态', + allowClear: true, + options: getDictOptions( + DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, + 'number', + ), + }, + }, + { + fieldName: 'createTime', + label: '发起时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'endTime', + label: '结束时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; + + // 动态表单字段配置:目前支持 input 和 textarea 类型 + const dynamicFormSchema: VbenFormSchema[] = formFields + .filter((item) => ['input', 'textarea'].includes(item.type)) + .map((item) => ({ + fieldName: `formFieldsParams.${item.field}`, + label: item.title, + component: 'Input', + componentProps: { + placeholder: `请输入${item.title}`, + allowClear: true, + }, + })); + + return [...baseFormSchema, ...dynamicFormSchema]; +} + +/** 列表的字段 */ +export function useGridColumns( + formFields: FormField[] = [], +): VxeTableGridOptions['columns'] { + // 基础列配置 + const baseColumns: VxeGridPropTypes.Columns = + [ + { + field: 'name', + title: '流程名称', + minWidth: 250, + fixed: 'left', + }, + { + field: 'startUser.nickname', + title: '流程发起人', + minWidth: 200, + }, + { + field: 'status', + title: '流程状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS }, + }, + }, + { + field: 'startTime', + title: '发起时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'endTime', + title: '结束时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + ]; + + // 动态表单字段列配置:根据表单字段生成对应的列,从 formVariables 中获取值 + const formFieldColumns = formFields.map((item) => ({ + field: `formVariables.${item.field}`, + title: item.title, + minWidth: 120, + formatter: ({ row }: any) => { + return row.formVariables?.[item.field] ?? ''; + }, + })); + + return [ + ...baseColumns, + ...formFieldColumns, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/processInstance/report/index.vue b/apps/web-ele/src/views/bpm/processInstance/report/index.vue new file mode 100644 index 000000000..5c8641a3d --- /dev/null +++ b/apps/web-ele/src/views/bpm/processInstance/report/index.vue @@ -0,0 +1,163 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processListener/data.ts b/apps/web-ele/src/views/bpm/processListener/data.ts new file mode 100644 index 000000000..339f2b8bf --- /dev/null +++ b/apps/web-ele/src/views/bpm/processListener/data.ts @@ -0,0 +1,210 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +export const EVENT_EXECUTION_OPTIONS = [ + { + label: 'start', + value: 'start', + }, + { + label: 'end', + value: 'end', + }, +]; + +export const EVENT_OPTIONS = [ + { label: 'create', value: 'create' }, + { label: 'assignment', value: 'assignment' }, + { label: 'complete', value: 'complete' }, + { label: 'delete', value: 'delete' }, + { label: 'update', value: 'update' }, + { label: 'timeout', value: 'timeout' }, +]; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'type', + label: '类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.BPM_PROCESS_LISTENER_TYPE, 'string'), + placeholder: '请选择类型', + allowClear: true, + }, + rules: 'required', + }, + { + fieldName: 'event', + label: '事件', + component: 'Select', + componentProps: { + options: EVENT_OPTIONS, + placeholder: '请选择事件', + allowClear: true, + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + trigger: (values) => (values.event = undefined), + componentProps: (values) => ({ + options: + values.type === 'execution' + ? EVENT_EXECUTION_OPTIONS + : EVENT_OPTIONS, + }), + }, + }, + { + fieldName: 'valueType', + label: '值类型', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE, + 'string', + ), + placeholder: '请选择值类型', + allowClear: true, + }, + rules: 'required', + }, + { + fieldName: 'value', + label: '类路径|表达式', + component: 'Input', + rules: 'required', + dependencies: { + triggerFields: ['valueType'], + trigger: (values) => (values.value = undefined), + componentProps: (values) => ({ + placeholder: + values.valueType === 'class' ? '请输入类路径' : '请输入表达式', + }), + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + allowClear: true, + }, + }, + { + fieldName: 'type', + label: '类型', + component: 'Select', + componentProps: { + placeholder: '请选择类型', + options: getDictOptions(DICT_TYPE.BPM_PROCESS_LISTENER_TYPE, 'string'), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '名字', + minWidth: 200, + }, + { + field: 'type', + title: '类型', + minWidth: 200, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_PROCESS_LISTENER_TYPE }, + }, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'event', + title: '事件', + minWidth: 200, + }, + { + field: 'valueType', + title: '值类型', + minWidth: 200, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE }, + }, + }, + { + field: 'value', + title: '值', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/processListener/index.vue b/apps/web-ele/src/views/bpm/processListener/index.vue new file mode 100644 index 000000000..fee637190 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processListener/index.vue @@ -0,0 +1,135 @@ + + + diff --git a/apps/web-ele/src/views/bpm/processListener/modules/form.vue b/apps/web-ele/src/views/bpm/processListener/modules/form.vue new file mode 100644 index 000000000..0e9a92672 --- /dev/null +++ b/apps/web-ele/src/views/bpm/processListener/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/bpm/task/copy/data.ts b/apps/web-ele/src/views/bpm/task/copy/data.ts new file mode 100644 index 000000000..202887d73 --- /dev/null +++ b/apps/web-ele/src/views/bpm/task/copy/data.ts @@ -0,0 +1,92 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '流程名称', + component: 'Input', + componentProps: { + placeholder: '请输入流程名称', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '抄送时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'processInstanceName', + title: '流程名称', + minWidth: 200, + }, + { + field: 'summary', + title: '摘要', + minWidth: 200, + formatter: ({ cellValue }) => { + return cellValue && cellValue.length > 0 + ? cellValue + .map((item: any) => `${item.key} : ${item.value}`) + .join('\n') + : '-'; + }, + }, + { + field: 'startUser.nickname', + title: '流程发起人', + minWidth: 120, + }, + { + field: 'processInstanceStartTime', + title: '流程发起时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'activityName', + title: '抄送节点', + minWidth: 120, + }, + { + field: 'createUser.nickname', + title: '抄送人', + minWidth: 120, + formatter: ({ cellValue }) => { + return cellValue || '-'; + }, + }, + { + field: 'reason', + title: '抄送意见', + minWidth: 180, + }, + { + field: 'createTime', + title: '抄送时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/task/copy/index.vue b/apps/web-ele/src/views/bpm/task/copy/index.vue new file mode 100644 index 000000000..21dcec3c9 --- /dev/null +++ b/apps/web-ele/src/views/bpm/task/copy/index.vue @@ -0,0 +1,85 @@ + + + diff --git a/apps/web-ele/src/views/bpm/task/done/data.ts b/apps/web-ele/src/views/bpm/task/done/data.ts new file mode 100644 index 000000000..3c5e9f678 --- /dev/null +++ b/apps/web-ele/src/views/bpm/task/done/data.ts @@ -0,0 +1,154 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getCategorySimpleList } from '#/api/bpm/category'; +import { getSimpleProcessDefinitionList } from '#/api/bpm/definition'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '任务名称', + component: 'Input', + componentProps: { + placeholder: '请输入任务名称', + allowClear: true, + }, + }, + { + fieldName: 'processDefinitionKey', + label: '所属流程', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择流程定义', + allowClear: true, + api: getSimpleProcessDefinitionList, + labelField: 'name', + valueField: 'key', + }, + }, + { + fieldName: 'category', + label: '流程分类', + component: 'ApiSelect', + componentProps: { + placeholder: '请输入流程分类', + allowClear: true, + api: getCategorySimpleList, + labelField: 'name', + valueField: 'code', + }, + }, + { + fieldName: 'status', + label: '审批状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.BPM_TASK_STATUS, 'number'), + placeholder: '请选择审批状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '发起时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'processInstance.name', + title: '流程', + minWidth: 200, + }, + { + field: 'processInstance.summary', + title: '摘要', + minWidth: 200, + formatter: ({ cellValue }) => { + return cellValue && cellValue.length > 0 + ? cellValue + .map((item: any) => `${item.key} : ${item.value}`) + .join('\n') + : '-'; + }, + }, + { + field: 'processInstance.startUser.nickname', + title: '发起人', + minWidth: 120, + }, + { + field: 'processInstance.createTime', + title: '发起时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'name', + title: '当前任务', + minWidth: 180, + }, + { + field: 'createTime', + title: '任务开始时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'endTime', + title: '任务结束时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'status', + title: '审批状态', + minWidth: 180, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_TASK_STATUS }, + }, + }, + { + field: 'reason', + title: '审批建议', + minWidth: 180, + }, + { + field: 'durationInMillis', + title: '耗时', + minWidth: 180, + formatter: 'formatPast2', + }, + { + field: 'processInstanceId', + title: '流程编号', + minWidth: 280, + }, + { + field: 'id', + title: '任务编号', + minWidth: 280, + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/task/done/index.vue b/apps/web-ele/src/views/bpm/task/done/index.vue new file mode 100644 index 000000000..cc1aba780 --- /dev/null +++ b/apps/web-ele/src/views/bpm/task/done/index.vue @@ -0,0 +1,114 @@ + + + diff --git a/apps/web-ele/src/views/bpm/task/manager/data.ts b/apps/web-ele/src/views/bpm/task/manager/data.ts new file mode 100644 index 000000000..947982b23 --- /dev/null +++ b/apps/web-ele/src/views/bpm/task/manager/data.ts @@ -0,0 +1,104 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '任务名称', + component: 'Input', + componentProps: { + placeholder: '请输入任务名称', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'processInstance.name', + title: '流程', + minWidth: 200, + }, + { + field: 'processInstance.startUser.nickname', + title: '发起人', + minWidth: 120, + }, + { + field: 'name', + title: '当前任务', + minWidth: 180, + }, + { + field: 'createTime', + title: '任务开始时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'endTime', + title: '任务结束时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'assigneeUser.nickname', + title: '审批人', + minWidth: 180, + }, + { + field: 'status', + title: '审批状态', + minWidth: 180, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BPM_TASK_STATUS }, + }, + }, + { + field: 'reason', + title: '审批建议', + minWidth: 180, + }, + { + field: 'durationInMillis', + title: '耗时', + minWidth: 180, + formatter: 'formatPast2', + }, + { + field: 'processInstanceId', + title: '流程编号', + minWidth: 280, + }, + { + field: 'id', + title: '任务编号', + minWidth: 280, + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/task/manager/index.vue b/apps/web-ele/src/views/bpm/task/manager/index.vue new file mode 100644 index 000000000..37575579b --- /dev/null +++ b/apps/web-ele/src/views/bpm/task/manager/index.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-ele/src/views/bpm/task/todo/data.ts b/apps/web-ele/src/views/bpm/task/todo/data.ts new file mode 100644 index 000000000..0b1efd71c --- /dev/null +++ b/apps/web-ele/src/views/bpm/task/todo/data.ts @@ -0,0 +1,131 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getCategorySimpleList } from '#/api/bpm/category'; +import { getSimpleProcessDefinitionList } from '#/api/bpm/definition'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '任务名称', + component: 'Input', + componentProps: { + placeholder: '请输入任务名称', + allowClear: true, + }, + }, + { + fieldName: 'processDefinitionKey', + label: '所属流程', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择流程定义', + allowClear: true, + api: getSimpleProcessDefinitionList, + labelField: 'name', + valueField: 'key', + }, + }, + { + fieldName: 'category', + label: '流程分类', + component: 'ApiSelect', + componentProps: { + placeholder: '请输入流程分类', + allowClear: true, + api: getCategorySimpleList, + labelField: 'name', + valueField: 'code', + }, + }, + { + fieldName: 'status', + label: '流程状态', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS, + 'number', + ), + placeholder: '请选择流程状态', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '发起时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'processInstance.name', + title: '流程', + minWidth: 200, + }, + { + field: 'processInstance.summary', + title: '摘要', + minWidth: 200, + formatter: ({ cellValue }) => { + return cellValue && cellValue.length > 0 + ? cellValue + .map((item: any) => `${item.key} : ${item.value}`) + .join('\n') + : '-'; + }, + }, + { + field: 'processInstance.startUser.nickname', + title: '发起人', + minWidth: 120, + }, + { + field: 'processInstance.createTime', + title: '发起时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'name', + title: '当前任务', + minWidth: 180, + }, + { + field: 'createTime', + title: '任务时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'processInstanceId', + title: '流程编号', + minWidth: 280, + }, + { + field: 'id', + title: '任务编号', + minWidth: 280, + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/bpm/task/todo/index.vue b/apps/web-ele/src/views/bpm/task/todo/index.vue new file mode 100644 index 000000000..d80830739 --- /dev/null +++ b/apps/web-ele/src/views/bpm/task/todo/index.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/crm/backlog/data.ts b/apps/web-ele/src/views/crm/backlog/data.ts new file mode 100644 index 000000000..d0b66d13e --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/data.ts @@ -0,0 +1,102 @@ +import type { Ref } from 'vue'; + +export interface LeftSideItem { + name: string; + menu: string; + count: Ref; +} + +/** 跟进状态 */ +export const FOLLOWUP_STATUS = [ + { label: '待跟进', value: false }, + { label: '已跟进', value: true }, +]; + +/** 归属范围 */ +export const SCENE_TYPES = [ + { label: '我负责的', value: 1 }, + { label: '我参与的', value: 2 }, + { label: '下属负责的', value: 3 }, +]; + +/** 联系状态 */ +export const CONTACT_STATUS = [ + { label: '今日需联系', value: 1 }, + { label: '已逾期', value: 2 }, + { label: '已联系', value: 3 }, +]; + +/** 审批状态 */ +export const AUDIT_STATUS = [ + { label: '待审批', value: 10 }, + { label: '审核通过', value: 20 }, + { label: '审核不通过', value: 30 }, +]; + +/** 回款提醒类型 */ +export const RECEIVABLE_REMIND_TYPE = [ + { label: '待回款', value: 1 }, + { label: '已逾期', value: 2 }, + { label: '已回款', value: 3 }, +]; + +/** 合同过期状态 */ +export const CONTRACT_EXPIRY_TYPE = [ + { label: '即将过期', value: 1 }, + { label: '已过期', value: 2 }, +]; + +/** 左侧菜单 */ +export const useLeftSides = ( + customerTodayContactCount: Ref, + clueFollowCount: Ref, + customerFollowCount: Ref, + customerPutPoolRemindCount: Ref, + contractAuditCount: Ref, + contractRemindCount: Ref, + receivableAuditCount: Ref, + receivablePlanRemindCount: Ref, +): LeftSideItem[] => { + return [ + { + name: '今日需联系客户', + menu: 'customerTodayContact', + count: customerTodayContactCount, + }, + { + name: '分配给我的线索', + menu: 'clueFollow', + count: clueFollowCount, + }, + { + name: '分配给我的客户', + menu: 'customerFollow', + count: customerFollowCount, + }, + { + name: '待进入公海的客户', + menu: 'customerPutPoolRemind', + count: customerPutPoolRemindCount, + }, + { + name: '待审核合同', + menu: 'contractAudit', + count: contractAuditCount, + }, + { + name: '待审核回款', + menu: 'receivableAudit', + count: receivableAuditCount, + }, + { + name: '待回款提醒', + menu: 'receivablePlanRemind', + count: receivablePlanRemindCount, + }, + { + name: '即将到期的合同', + menu: 'contractRemind', + count: contractRemindCount, + }, + ]; +}; diff --git a/apps/web-ele/src/views/crm/backlog/index.vue b/apps/web-ele/src/views/crm/backlog/index.vue new file mode 100644 index 000000000..fed22419c --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/index.vue @@ -0,0 +1,115 @@ + + diff --git a/apps/web-ele/src/views/crm/backlog/modules/clue-follow-list.vue b/apps/web-ele/src/views/crm/backlog/modules/clue-follow-list.vue new file mode 100644 index 000000000..bd072ba70 --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/modules/clue-follow-list.vue @@ -0,0 +1,79 @@ + + + + diff --git a/apps/web-ele/src/views/crm/backlog/modules/contract-audit-list.vue b/apps/web-ele/src/views/crm/backlog/modules/contract-audit-list.vue new file mode 100644 index 000000000..5e4631c96 --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/modules/contract-audit-list.vue @@ -0,0 +1,124 @@ + + + + diff --git a/apps/web-ele/src/views/crm/backlog/modules/contract-remind-list.vue b/apps/web-ele/src/views/crm/backlog/modules/contract-remind-list.vue new file mode 100644 index 000000000..e35662ce9 --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/modules/contract-remind-list.vue @@ -0,0 +1,125 @@ + + + + diff --git a/apps/web-ele/src/views/crm/backlog/modules/customer-follow-list.vue b/apps/web-ele/src/views/crm/backlog/modules/customer-follow-list.vue new file mode 100644 index 000000000..09ac5de3f --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/modules/customer-follow-list.vue @@ -0,0 +1,79 @@ + + + + diff --git a/apps/web-ele/src/views/crm/backlog/modules/customer-put-pool-remind-list.vue b/apps/web-ele/src/views/crm/backlog/modules/customer-put-pool-remind-list.vue new file mode 100644 index 000000000..15ac870f3 --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/modules/customer-put-pool-remind-list.vue @@ -0,0 +1,79 @@ + + + + diff --git a/apps/web-ele/src/views/crm/backlog/modules/customer-today-contact-list.vue b/apps/web-ele/src/views/crm/backlog/modules/customer-today-contact-list.vue new file mode 100644 index 000000000..f1770d384 --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/modules/customer-today-contact-list.vue @@ -0,0 +1,89 @@ + + + + diff --git a/apps/web-ele/src/views/crm/backlog/modules/receivable-audit-list.vue b/apps/web-ele/src/views/crm/backlog/modules/receivable-audit-list.vue new file mode 100644 index 000000000..d8f8d16d0 --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/modules/receivable-audit-list.vue @@ -0,0 +1,106 @@ + + + + diff --git a/apps/web-ele/src/views/crm/backlog/modules/receivable-plan-remind-list.vue b/apps/web-ele/src/views/crm/backlog/modules/receivable-plan-remind-list.vue new file mode 100644 index 000000000..7561331d2 --- /dev/null +++ b/apps/web-ele/src/views/crm/backlog/modules/receivable-plan-remind-list.vue @@ -0,0 +1,104 @@ + + + + diff --git a/apps/web-ele/src/views/crm/business/components/data.ts b/apps/web-ele/src/views/crm/business/components/data.ts new file mode 100644 index 000000000..10ecb5a0f --- /dev/null +++ b/apps/web-ele/src/views/crm/business/components/data.ts @@ -0,0 +1,52 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** 商机关联列表列定义 */ +export function useBusinessDetailListColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'name', + title: '商机名称', + fixed: 'left', + slots: { default: 'name' }, + }, + { + field: 'customerName', + title: '客户名称', + fixed: 'left', + slots: { default: 'customerName' }, + }, + { + field: 'totalPrice', + title: '商机金额(元)', + formatter: 'formatAmount2', + }, + { + field: 'dealTime', + title: '预计成交日期', + formatter: 'formatDate', + }, + { + field: 'ownerUserName', + title: '负责人', + }, + { + field: 'ownerUserDeptName', + title: '所属部门', + }, + { + field: 'statusTypeName', + title: '商机状态组', + fixed: 'right', + }, + { + field: 'statusName', + title: '商机阶段', + fixed: 'right', + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/business/components/detail-list-modal.vue b/apps/web-ele/src/views/crm/business/components/detail-list-modal.vue new file mode 100644 index 000000000..9b3bfee91 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/components/detail-list-modal.vue @@ -0,0 +1,149 @@ + + + + diff --git a/apps/web-ele/src/views/crm/business/components/detail-list.vue b/apps/web-ele/src/views/crm/business/components/detail-list.vue new file mode 100644 index 000000000..e57ac8f0c --- /dev/null +++ b/apps/web-ele/src/views/crm/business/components/detail-list.vue @@ -0,0 +1,215 @@ + + + + diff --git a/apps/web-ele/src/views/crm/business/components/index.ts b/apps/web-ele/src/views/crm/business/components/index.ts new file mode 100644 index 000000000..a63de4701 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/components/index.ts @@ -0,0 +1 @@ +export { default as BusinessDetailsList } from './detail-list.vue'; diff --git a/apps/web-ele/src/views/crm/business/data.ts b/apps/web-ele/src/views/crm/business/data.ts new file mode 100644 index 000000000..93bcf41d7 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/data.ts @@ -0,0 +1,281 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { useUserStore } from '@vben/stores'; +import { erpPriceMultiply } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getBusinessStatusTypeSimpleList } from '#/api/crm/business/status'; +import { getCustomerSimpleList } from '#/api/crm/customer'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '商机名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入商机名称', + allowClear: true, + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + allowClear: true, + }, + defaultValue: userStore.userInfo?.id, + rules: 'required', + }, + { + fieldName: 'customerId', + label: '客户名称', + component: 'ApiSelect', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + allowClear: true, + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.customerDefault, + }, + rules: 'required', + }, + { + fieldName: 'contactId', + label: '合同名称', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'statusTypeId', + label: '商机状态组', + component: 'ApiSelect', + componentProps: { + api: getBusinessStatusTypeSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择商机状态组', + allowClear: true, + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + rules: 'required', + }, + { + fieldName: 'dealTime', + label: '预计成交日期', + component: 'DatePicker', + componentProps: { + showTime: false, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + placeholder: '请选择预计成交日期', + class: '!w-full', + }, + }, + { + fieldName: 'product', + label: '产品清单', + component: 'Input', + formItemClass: 'col-span-3', + componentProps: { + placeholder: '请输入产品清单', + allowClear: true, + }, + }, + { + fieldName: 'totalProductPrice', + label: '产品总金额', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + disabled: true, + placeholder: '请输入产品总金额', + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional().default(0), + }, + { + fieldName: 'discountPercent', + label: '整单折扣(%)', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + placeholder: '请输入整单折扣', + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).max(100).optional().default(0), + }, + { + fieldName: 'totalPrice', + label: '折扣后金额', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + disabled: true, + placeholder: '请输入折扣后金额', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['totalProductPrice', 'discountPercent'], + trigger(values, form) { + const discountPrice = + erpPriceMultiply( + values.totalProductPrice, + values.discountPercent / 100, + ) ?? 0; + form.setFieldValue( + 'totalPrice', + values.totalProductPrice - discountPrice, + ); + }, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '商机名称', + component: 'Input', + componentProps: { + placeholder: '请输入商机名称', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '商机名称', + fixed: 'left', + width: 160, + slots: { default: 'name' }, + }, + { + field: 'customerName', + title: '客户名称', + fixed: 'left', + width: 120, + slots: { default: 'customerName' }, + }, + { + field: 'totalPrice', + title: '商机金额(元)', + width: 140, + formatter: 'formatAmount2', + }, + { + field: 'dealTime', + title: '预计成交日期', + formatter: 'formatDate', + width: 180, + }, + { + field: 'remark', + title: '备注', + width: 200, + }, + { + field: 'contactNextTime', + title: '下次联系时间', + formatter: 'formatDateTime', + width: 180, + }, + { + field: 'ownerUserName', + title: '负责人', + width: 100, + }, + { + field: 'ownerUserDeptName', + title: '所属部门', + width: 100, + }, + { + field: 'contactLastTime', + title: '最后跟进时间', + formatter: 'formatDateTime', + width: 180, + }, + { + field: 'updateTime', + title: '更新时间', + formatter: 'formatDateTime', + width: 180, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + width: 180, + }, + { + field: 'creatorName', + title: '创建人', + width: 100, + }, + { + field: 'statusTypeName', + title: '商机状态组', + fixed: 'right', + width: 140, + }, + { + field: 'statusName', + title: '商机阶段', + fixed: 'right', + width: 120, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/business/detail/data.ts b/apps/web-ele/src/views/crm/business/detail/data.ts new file mode 100644 index 000000000..1cb1c4949 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/detail/data.ts @@ -0,0 +1,141 @@ +import type { Ref } from 'vue'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { CrmBusinessApi } from '#/api/crm/business'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { + DEFAULT_STATUSES, + getBusinessStatusSimpleList, +} from '#/api/crm/business/status'; + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'totalPrice', + label: '商机金额(元)', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'statusTypeName', + label: '商机组', + }, + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'name', + label: '商机名称', + }, + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'totalPrice', + label: '商机金额(元)', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'dealTime', + label: '预计成交日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'contactNextTime', + label: '下次联系时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'statusTypeName', + label: '商机状态组', + }, + { + field: 'statusName', + label: '商机阶段', + }, + { + field: 'remark', + label: '备注', + }, + ]; +} + +/** 商机状态更新表单 */ +export function useStatusFormSchema( + formData: Ref, +): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'statusId', + label: '商机状态', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'endStatus', + label: '商机状态', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'status', + label: '商机阶段', + component: 'Select', + dependencies: { + triggerFields: [''], + async componentProps() { + const statusList = await getBusinessStatusSimpleList( + formData.value?.statusTypeId ?? 0, + ); + const statusOptions = statusList.map((item) => ({ + label: `${item.name}(赢单率:${item.percent}%)`, + value: item.id, + })); + const options = DEFAULT_STATUSES.map((item) => ({ + label: `${item.name}(赢单率:${item.percent}%)`, + value: item.endStatus, + })); + statusOptions.push(...options); + return { + options: statusOptions, + }; + }, + }, + rules: 'required', + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/business/detail/index.vue b/apps/web-ele/src/views/crm/business/detail/index.vue new file mode 100644 index 000000000..1a3fbb883 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/detail/index.vue @@ -0,0 +1,191 @@ + + + diff --git a/apps/web-ele/src/views/crm/business/detail/modules/info.vue b/apps/web-ele/src/views/crm/business/detail/modules/info.vue new file mode 100644 index 000000000..7e8753b74 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/detail/modules/info.vue @@ -0,0 +1,36 @@ + + + diff --git a/apps/web-ele/src/views/crm/business/detail/modules/status-form.vue b/apps/web-ele/src/views/crm/business/detail/modules/status-form.vue new file mode 100644 index 000000000..53df20aed --- /dev/null +++ b/apps/web-ele/src/views/crm/business/detail/modules/status-form.vue @@ -0,0 +1,85 @@ + + + diff --git a/apps/web-ele/src/views/crm/business/index.vue b/apps/web-ele/src/views/crm/business/index.vue new file mode 100644 index 000000000..f5eae10bf --- /dev/null +++ b/apps/web-ele/src/views/crm/business/index.vue @@ -0,0 +1,208 @@ + + + diff --git a/apps/web-ele/src/views/crm/business/modules/form.vue b/apps/web-ele/src/views/crm/business/modules/form.vue new file mode 100644 index 000000000..051cfe75d --- /dev/null +++ b/apps/web-ele/src/views/crm/business/modules/form.vue @@ -0,0 +1,121 @@ + + + diff --git a/apps/web-ele/src/views/crm/business/status/data.ts b/apps/web-ele/src/views/crm/business/status/data.ts new file mode 100644 index 000000000..86f20d819 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/status/data.ts @@ -0,0 +1,112 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { handleTree } from '@vben/utils'; + +import { getDeptList } from '#/api/system/dept'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '状态组名', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入状态组名', + }, + }, + { + fieldName: 'deptIds', + label: '应用部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getDeptList(); + return handleTree(data); + }, + multiple: true, + fieldNames: { label: 'name', value: 'id', children: 'children' }, + placeholder: '请选择应用部门', + treeDefaultExpandAll: true, + }, + help: '不选择部门时,默认全公司生效', + }, + { + fieldName: 'statuses', + label: '阶段设置', + component: 'Input', + rules: 'required', + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '状态组名', + }, + { + field: 'deptNames', + title: '应用部门', + formatter: ({ cellValue }) => + cellValue?.length > 0 ? cellValue.join(' ') : '全公司', + }, + { + field: 'creator', + title: '创建人', + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 商机状态阶段列表列配置 */ +export function useFormColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'defaultStatus', + title: '阶段', + minWidth: 100, + slots: { default: 'defaultStatus' }, + }, + { + field: 'name', + title: '阶段名称', + minWidth: 100, + slots: { default: 'name' }, + }, + { + field: 'percent', + title: '赢单率(%)', + minWidth: 100, + slots: { default: 'percent' }, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/business/status/index.vue b/apps/web-ele/src/views/crm/business/status/index.vue new file mode 100644 index 000000000..f04101cc1 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/status/index.vue @@ -0,0 +1,136 @@ + + + diff --git a/apps/web-ele/src/views/crm/business/status/modules/form.vue b/apps/web-ele/src/views/crm/business/status/modules/form.vue new file mode 100644 index 000000000..3be655c33 --- /dev/null +++ b/apps/web-ele/src/views/crm/business/status/modules/form.vue @@ -0,0 +1,208 @@ + + + diff --git a/apps/web-ele/src/views/crm/clue/data.ts b/apps/web-ele/src/views/crm/clue/data.ts new file mode 100644 index 000000000..fccd2bfde --- /dev/null +++ b/apps/web-ele/src/views/crm/clue/data.ts @@ -0,0 +1,327 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; + +import { getAreaTree } from '#/api/system/area'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '线索名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入线索名称', + }, + }, + { + fieldName: 'source', + label: '客户来源', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE, 'number'), + placeholder: '请选择客户来源', + }, + rules: 'required', + }, + { + fieldName: 'mobile', + label: '手机', + component: 'Input', + componentProps: { + placeholder: '请输入手机号', + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + clearable: true, + placeholder: '请选择负责人', + }, + defaultValue: userStore.userInfo?.id, + rules: 'required', + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + }, + { + fieldName: 'telephone', + label: '电话', + component: 'Input', + componentProps: { + placeholder: '请输入电话', + }, + }, + { + fieldName: 'email', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + }, + }, + { + fieldName: 'wechat', + label: '微信', + component: 'Input', + componentProps: { + placeholder: '请输入微信', + }, + }, + { + fieldName: 'qq', + label: 'QQ', + component: 'Input', + componentProps: { + placeholder: '请输入 QQ', + }, + }, + { + fieldName: 'industryId', + label: '客户行业', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY, 'number'), + placeholder: '请选择客户行业', + }, + }, + { + fieldName: 'level', + label: '客户级别', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL, 'number'), + placeholder: '请选择客户级别', + }, + }, + { + fieldName: 'areaId', + label: '地址', + component: 'ApiTreeSelect', + componentProps: { + api: getAreaTree, + fieldNames: { label: 'name', value: 'id', children: 'children' }, + placeholder: '请选择地址', + }, + }, + { + fieldName: 'detailAddress', + label: '详细地址', + component: 'Input', + componentProps: { + placeholder: '请输入详细地址', + }, + }, + { + fieldName: 'contactNextTime', + label: '下次联系时间', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + placeholder: '请选择下次联系时间', + class: '!w-full', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '线索名称', + component: 'Input', + componentProps: { + placeholder: '请输入线索名称', + clearable: true, + }, + }, + { + fieldName: 'transformStatus', + label: '转化状态', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '未转化', value: false }, + { label: '已转化', value: true }, + ], + placeholder: '请选择转化状态', + clearable: true, + }, + defaultValue: false, + }, + { + fieldName: 'mobile', + label: '手机号', + component: 'Input', + componentProps: { + placeholder: '请输入手机号', + clearable: true, + }, + }, + { + fieldName: 'telephone', + label: '电话', + component: 'Input', + componentProps: { + placeholder: '请输入电话', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + placeholder: ['开始日期', '结束日期'], + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '线索名称', + fixed: 'left', + minWidth: 160, + slots: { + default: 'name', + }, + }, + { + field: 'source', + title: '线索来源', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE }, + }, + }, + { + field: 'mobile', + title: '手机', + minWidth: 120, + }, + { + field: 'telephone', + title: '电话', + minWidth: 130, + }, + { + field: 'email', + title: '邮箱', + minWidth: 180, + }, + { + field: 'detailAddress', + title: '地址', + minWidth: 180, + }, + { + field: 'industryId', + title: '客户行业', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY }, + }, + }, + { + field: 'level', + title: '客户级别', + minWidth: 135, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_LEVEL }, + }, + }, + { + field: 'contactNextTime', + title: '下次联系时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'contactLastTime', + title: '最后跟进时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'contactLastContent', + title: '最后跟进记录', + minWidth: 200, + }, + { + field: 'ownerUserName', + title: '负责人', + minWidth: 100, + }, + { + field: 'ownerUserDeptName', + title: '所属部门', + minWidth: 100, + }, + { + field: 'updateTime', + title: '更新时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + title: '操作', + width: 140, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/clue/detail/data.ts b/apps/web-ele/src/views/crm/clue/detail/data.ts new file mode 100644 index 000000000..9536eb79b --- /dev/null +++ b/apps/web-ele/src/views/crm/clue/detail/data.ts @@ -0,0 +1,111 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情头部的配置 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'source', + label: '线索来源', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_CUSTOMER_SOURCE, + value: val, + }), + }, + { + field: 'mobile', + label: '手机', + }, + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} + +/** 详情基本信息的配置 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'name', + label: '线索名称', + }, + { + field: 'source', + label: '客户来源', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_CUSTOMER_SOURCE, + value: val, + }), + }, + { + field: 'mobile', + label: '手机', + }, + { + field: 'telephone', + label: '电话', + }, + { + field: 'email', + label: '邮箱', + }, + { + field: 'areaName', + label: '地址', + render: (val, data) => { + const areaName = val ?? ''; + const detailAddress = data?.detailAddress ?? ''; + return [areaName, detailAddress].filter((item) => !!item).join(' '); + }, + }, + { + field: 'qq', + label: 'QQ', + }, + { + field: 'wechat', + label: '微信', + }, + { + field: 'industryId', + label: '客户行业', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY, + value: val, + }), + }, + { + field: 'level', + label: '客户级别', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_CUSTOMER_LEVEL, + value: val, + }), + }, + { + field: 'contactNextTime', + label: '下次联系时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'remark', + label: '备注', + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/clue/detail/index.vue b/apps/web-ele/src/views/crm/clue/detail/index.vue new file mode 100644 index 000000000..e7e6f8d22 --- /dev/null +++ b/apps/web-ele/src/views/crm/clue/detail/index.vue @@ -0,0 +1,174 @@ + + + diff --git a/apps/web-ele/src/views/crm/clue/detail/modules/info.vue b/apps/web-ele/src/views/crm/clue/detail/modules/info.vue new file mode 100644 index 000000000..676b00510 --- /dev/null +++ b/apps/web-ele/src/views/crm/clue/detail/modules/info.vue @@ -0,0 +1,36 @@ + + + diff --git a/apps/web-ele/src/views/crm/clue/index.vue b/apps/web-ele/src/views/crm/clue/index.vue new file mode 100644 index 000000000..db011a943 --- /dev/null +++ b/apps/web-ele/src/views/crm/clue/index.vue @@ -0,0 +1,193 @@ + + + diff --git a/apps/web-ele/src/views/crm/clue/modules/form.vue b/apps/web-ele/src/views/crm/clue/modules/form.vue new file mode 100644 index 000000000..db3f75365 --- /dev/null +++ b/apps/web-ele/src/views/crm/clue/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/crm/contact/components/data.ts b/apps/web-ele/src/views/crm/contact/components/data.ts new file mode 100644 index 000000000..b2e63f76e --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/components/data.ts @@ -0,0 +1,62 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 联系人明细列表列配置 */ +export function useDetailListColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'name', + title: '姓名', + fixed: 'left', + slots: { default: 'name' }, + }, + { + field: 'customerName', + title: '客户名称', + fixed: 'left', + slots: { default: 'customerName' }, + }, + { + field: 'sex', + title: '性别', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'mobile', + title: '手机', + }, + { + field: 'telephone', + title: '电话', + }, + { + field: 'email', + title: '邮箱', + }, + { + field: 'post', + title: '职位', + }, + { + field: 'detailAddress', + title: '地址', + }, + { + field: 'master', + title: '关键决策人', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/contact/components/detail-list-modal.vue b/apps/web-ele/src/views/crm/contact/components/detail-list-modal.vue new file mode 100644 index 000000000..9d665ce42 --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/components/detail-list-modal.vue @@ -0,0 +1,148 @@ + + + + diff --git a/apps/web-ele/src/views/crm/contact/components/detail-list.vue b/apps/web-ele/src/views/crm/contact/components/detail-list.vue new file mode 100644 index 000000000..b49e768ac --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/components/detail-list.vue @@ -0,0 +1,210 @@ + + + + diff --git a/apps/web-ele/src/views/crm/contact/components/index.ts b/apps/web-ele/src/views/crm/contact/components/index.ts new file mode 100644 index 000000000..d16cd533d --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/components/index.ts @@ -0,0 +1 @@ +export { default as ContactDetailsList } from './detail-list.vue'; diff --git a/apps/web-ele/src/views/crm/contact/data.ts b/apps/web-ele/src/views/crm/contact/data.ts new file mode 100644 index 000000000..c0d13b25a --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/data.ts @@ -0,0 +1,366 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; + +import { getSimpleContactList } from '#/api/crm/contact'; +import { getCustomerSimpleList } from '#/api/crm/customer'; +import { getAreaTree } from '#/api/system/area'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '联系人姓名', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入联系人姓名', + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + rules: 'required', + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + }, + defaultValue: userStore.userInfo?.id, + }, + { + fieldName: 'customerId', + label: '客户名称', + component: 'ApiSelect', + rules: 'required', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + }, + }, + { + fieldName: 'mobile', + label: '手机', + component: 'Input', + componentProps: { + placeholder: '请输入手机号', + }, + }, + { + fieldName: 'telephone', + label: '电话', + component: 'Input', + componentProps: { + placeholder: '请输入电话', + }, + }, + { + fieldName: 'email', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + }, + }, + { + fieldName: 'wechat', + label: '微信', + component: 'Input', + componentProps: { + placeholder: '请输入微信', + }, + }, + { + fieldName: 'qq', + label: 'QQ', + component: 'Input', + componentProps: { + placeholder: '请输入QQ', + }, + }, + { + fieldName: 'post', + label: '职位', + component: 'Input', + componentProps: { + placeholder: '请输入职位', + }, + }, + { + fieldName: 'master', + label: '关键决策人', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + placeholder: '请选择是否关键决策人', + }, + defaultValue: false, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'parentId', + label: '直属上级', + component: 'ApiSelect', + componentProps: { + api: getSimpleContactList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择直属上级', + }, + }, + { + fieldName: 'areaId', + label: '地址', + component: 'ApiTreeSelect', + componentProps: { + api: getAreaTree, + fieldNames: { label: 'name', value: 'id', children: 'children' }, + placeholder: '请选择地址', + }, + }, + { + fieldName: 'detailAddress', + label: '详细地址', + component: 'Input', + componentProps: { + placeholder: '请输入详细地址', + }, + }, + { + fieldName: 'contactNextTime', + label: '下次联系时间', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + placeholder: '请选择下次联系时间', + class: '!w-full', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + }, + }, + { + fieldName: 'name', + label: '姓名', + component: 'Input', + componentProps: { + placeholder: '请输入联系人姓名', + allowClear: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号', + component: 'Input', + componentProps: { + placeholder: '请输入手机号', + allowClear: true, + }, + }, + { + fieldName: 'telephone', + label: '电话', + component: 'Input', + componentProps: { + placeholder: '请输入电话', + allowClear: true, + }, + }, + { + fieldName: 'wechat', + label: '微信', + component: 'Input', + componentProps: { + placeholder: '请输入微信', + allowClear: true, + }, + }, + { + fieldName: 'email', + label: '电子邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入电子邮箱', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '联系人姓名', + fixed: 'left', + minWidth: 240, + slots: { default: 'name' }, + }, + { + field: 'customerName', + title: '客户名称', + fixed: 'left', + minWidth: 240, + slots: { default: 'customerName' }, + }, + { + field: 'mobile', + title: '手机', + minWidth: 120, + }, + { + field: 'telephone', + title: '电话', + minWidth: 130, + }, + { + field: 'email', + title: '邮箱', + minWidth: 180, + }, + { + field: 'post', + title: '职位', + minWidth: 120, + }, + { + field: 'areaName', + title: '地址', + minWidth: 120, + }, + { + field: 'detailAddress', + title: '详细地址', + minWidth: 180, + }, + { + field: 'master', + title: '关键决策人', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'parentId', + title: '直属上级', + minWidth: 120, + slots: { default: 'parentId' }, + }, + { + field: 'contactNextTime', + title: '下次联系时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'contactLastTime', + title: '最后跟进时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'ownerUserName', + title: '负责人', + minWidth: 120, + }, + { + field: 'ownerUserDeptName', + title: '所属部门', + minWidth: 120, + }, + { + field: 'updateTime', + title: '更新时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/contact/detail/data.ts b/apps/web-ele/src/views/crm/contact/detail/data.ts new file mode 100644 index 000000000..3f1019325 --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/detail/data.ts @@ -0,0 +1,106 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情页的基础字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'post', + label: '职务', + }, + { + field: 'mobile', + label: '手机', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'name', + label: '姓名', + }, + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'mobile', + label: '手机', + }, + { + field: 'telephone', + label: '电话', + }, + { + field: 'email', + label: '邮箱', + }, + { + field: 'qq', + label: 'QQ', + }, + { + field: 'wechat', + label: '微信', + }, + { + field: 'areaName', + label: '地址', + render: (val, data) => { + const areaName = val ?? ''; + const detailAddress = data?.detailAddress ?? ''; + return [areaName, detailAddress].filter((item) => !!item).join(' '); + }, + }, + { + field: 'post', + label: '职务', + }, + { + field: 'parentName', + label: '直属上级', + }, + { + field: 'master', + label: '关键决策人', + render: (val) => + h(DictTag, { + type: DICT_TYPE.INFRA_BOOLEAN_STRING, + value: val, + }), + }, + { + field: 'sex', + label: '性别', + render: (val) => + h(DictTag, { type: DICT_TYPE.SYSTEM_USER_SEX, value: val }), + }, + { + field: 'contactNextTime', + label: '下次联系时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'remark', + label: '备注', + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/contact/detail/index.vue b/apps/web-ele/src/views/crm/contact/detail/index.vue new file mode 100644 index 000000000..4cf2286ca --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/detail/index.vue @@ -0,0 +1,158 @@ + + + diff --git a/apps/web-ele/src/views/crm/contact/detail/modules/info.vue b/apps/web-ele/src/views/crm/contact/detail/modules/info.vue new file mode 100644 index 000000000..0a56400f6 --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/detail/modules/info.vue @@ -0,0 +1,36 @@ + + + diff --git a/apps/web-ele/src/views/crm/contact/index.vue b/apps/web-ele/src/views/crm/contact/index.vue new file mode 100644 index 000000000..7f1ea9e4d --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/index.vue @@ -0,0 +1,213 @@ + + + diff --git a/apps/web-ele/src/views/crm/contact/modules/form.vue b/apps/web-ele/src/views/crm/contact/modules/form.vue new file mode 100644 index 000000000..5b0b3c7d9 --- /dev/null +++ b/apps/web-ele/src/views/crm/contact/modules/form.vue @@ -0,0 +1,80 @@ + + + diff --git a/apps/web-ele/src/views/crm/contract/components/data.ts b/apps/web-ele/src/views/crm/contract/components/data.ts new file mode 100644 index 000000000..c314faa99 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/components/data.ts @@ -0,0 +1,92 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { erpPriceInputFormatter } from '@vben/utils'; + +export function useDetailListColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '合同编号', + field: 'no', + minWidth: 150, + fixed: 'left', + }, + { + title: '合同名称', + field: 'name', + minWidth: 150, + fixed: 'left', + slots: { default: 'name' }, + }, + { + title: '合同金额(元)', + field: 'totalPrice', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '合同开始时间', + field: 'startTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '合同结束时间', + field: 'endTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '已回款金额(元)', + field: 'totalReceivablePrice', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '未回款金额(元)', + field: 'unpaidPrice', + minWidth: 150, + formatter: ({ row }) => { + return erpPriceInputFormatter( + row.totalPrice - row.totalReceivablePrice, + ); + }, + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 150, + }, + { + title: '所属部门', + field: 'ownerUserDeptName', + minWidth: 150, + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '创建人', + field: 'creatorName', + minWidth: 150, + }, + { + title: '备注', + field: 'remark', + minWidth: 150, + }, + { + title: '合同状态', + field: 'auditStatus', + fixed: 'right', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_AUDIT_STATUS }, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/contract/components/detail-list.vue b/apps/web-ele/src/views/crm/contract/components/detail-list.vue new file mode 100644 index 000000000..63dc35ca2 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/components/detail-list.vue @@ -0,0 +1,133 @@ + + + + diff --git a/apps/web-ele/src/views/crm/contract/components/index.ts b/apps/web-ele/src/views/crm/contract/components/index.ts new file mode 100644 index 000000000..d9f61793c --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/components/index.ts @@ -0,0 +1 @@ +export { default as ContractDetailsList } from './detail-list.vue'; diff --git a/apps/web-ele/src/views/crm/contract/config/data.ts b/apps/web-ele/src/views/crm/contract/config/data.ts new file mode 100644 index 000000000..c45dc5045 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/config/data.ts @@ -0,0 +1,40 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { z } from '#/adapter/form'; + +export const schema: VbenFormSchema[] = [ + { + component: 'RadioGroup', + fieldName: 'notifyEnabled', + label: '提前提醒设置', + componentProps: { + options: [ + { label: '提醒', value: true }, + { label: '不提醒', value: false }, + ], + }, + defaultValue: true, + }, + { + component: 'Input', + fieldName: 'notifyDays', + componentProps: { + placeholder: '请输入天数', + class: '!w-full', + }, + renderComponentContent: () => ({ + prepend: () => '提前', + append: () => '天提醒', + }), + rules: z.coerce.number().int().min(0, '天数不能小于 0'), + dependencies: { + triggerFields: ['notifyEnabled'], + show: (values) => values.notifyEnabled, + trigger(values) { + if (!values.notifyEnabled) { + values.notifyDays = undefined; + } + }, + }, + }, +]; diff --git a/apps/web-ele/src/views/crm/contract/config/index.vue b/apps/web-ele/src/views/crm/contract/config/index.vue new file mode 100644 index 000000000..c3dbb0845 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/config/index.vue @@ -0,0 +1,61 @@ + + + diff --git a/apps/web-ele/src/views/crm/contract/data.ts b/apps/web-ele/src/views/crm/contract/data.ts new file mode 100644 index 000000000..db2ee86c0 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/data.ts @@ -0,0 +1,421 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { useUserStore } from '@vben/stores'; +import { erpPriceInputFormatter, erpPriceMultiply } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getSimpleBusinessList } from '#/api/crm/business'; +import { getSimpleContactList } from '#/api/crm/contact'; +import { getCustomerSimpleList } from '#/api/crm/customer'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '合同编号', + component: 'Input', + componentProps: { + placeholder: '保存时自动生成', + disabled: true, + }, + }, + { + fieldName: 'name', + label: '合同名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入合同名称', + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + defaultValue: userStore.userInfo?.id, + rules: 'required', + }, + { + fieldName: 'customerId', + label: '客户名称', + component: 'ApiSelect', + rules: 'required', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + }, + }, + { + fieldName: 'businessId', + label: '商机名称', + component: 'Select', + componentProps: { + options: [], + placeholder: '请选择商机', + }, + dependencies: { + triggerFields: ['customerId'], + disabled: (values) => !values.customerId, + async componentProps(values) { + if (!values.customerId) { + return { + options: [], + placeholder: '请选择客户', + }; + } + const res = await getSimpleBusinessList(); + const list = res.filter( + (item) => item.customerId === values.customerId, + ); + return { + options: list.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择商机', + }; + }, + }, + }, + { + fieldName: 'orderDate', + label: '下单日期', + component: 'DatePicker', + rules: 'required', + componentProps: { + format: 'YYYY-MM-DD', + valueFormat: 'x', + placeholder: '请选择下单日期', + class: '!w-full', + }, + }, + { + fieldName: 'startTime', + label: '合同开始时间', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD', + valueFormat: 'x', + placeholder: '请选择合同开始时间', + class: '!w-full', + }, + }, + { + fieldName: 'endTime', + label: '合同结束时间', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD', + valueFormat: 'x', + placeholder: '请选择合同结束时间', + class: '!w-full', + }, + }, + { + fieldName: 'signUserId', + label: '公司签约人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + defaultValue: userStore.userInfo?.id, + }, + { + fieldName: 'signContactId', + label: '客户签约人', + component: 'Select', + componentProps: { + options: [], + placeholder: '请选择客户签约人', + }, + dependencies: { + triggerFields: ['customerId'], + disabled: (values) => !values.customerId, + async componentProps(values) { + if (!values.customerId) { + return { + options: [], + placeholder: '请选择客户', + }; + } + const res = await getSimpleContactList(); + const list = res.filter( + (item) => item.customerId === values.customerId, + ); + return { + options: list.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择客户签约人', + }; + }, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + }, + { + fieldName: 'product', + label: '产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'totalProductPrice', + label: '产品总金额', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + placeholder: '请输入产品总金额', + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional().default(0), + }, + { + fieldName: 'discountPercent', + label: '整单折扣(%)', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + placeholder: '请输入整单折扣', + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).max(100).optional().default(0), + }, + { + fieldName: 'totalPrice', + label: '折扣后金额', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['totalProductPrice', 'discountPercent'], + trigger(values, form) { + const discountPrice = + erpPriceMultiply( + values.totalProductPrice, + values.discountPercent / 100, + ) ?? 0; + form.setFieldValue( + 'totalPrice', + values.totalProductPrice - discountPrice, + ); + }, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '合同编号', + component: 'Input', + componentProps: { + placeholder: '请输入合同编号', + clearable: true, + }, + }, + { + fieldName: 'name', + label: '合同名称', + component: 'Input', + componentProps: { + placeholder: '请输入合同名称', + clearable: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + clearable: true, + }, + }, + ]; +} + +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '合同编号', + field: 'no', + minWidth: 180, + fixed: 'left', + }, + { + title: '合同名称', + field: 'name', + minWidth: 160, + fixed: 'left', + slots: { default: 'name' }, + }, + { + title: '客户名称', + field: 'customerName', + minWidth: 120, + slots: { default: 'customerName' }, + }, + { + title: '商机名称', + field: 'businessName', + minWidth: 130, + slots: { default: 'businessName' }, + }, + { + title: '合同金额(元)', + field: 'totalPrice', + minWidth: 140, + formatter: 'formatAmount2', + }, + { + title: '下单时间', + field: 'orderDate', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '合同开始时间', + field: 'startTime', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '合同结束时间', + field: 'endTime', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '客户签约人', + field: 'signContactName', + minWidth: 130, + slots: { default: 'signContactName' }, + }, + { + title: '公司签约人', + field: 'signUserName', + minWidth: 130, + }, + { + title: '备注', + field: 'remark', + minWidth: 200, + }, + { + title: '已回款金额(元)', + field: 'totalReceivablePrice', + minWidth: 140, + formatter: 'formatAmount2', + }, + { + title: '未回款金额(元)', + field: 'unReceivablePrice', + minWidth: 140, + formatter: ({ row }) => { + return erpPriceInputFormatter( + row.totalPrice - row.totalReceivablePrice, + ); + }, + }, + { + title: '最后跟进时间', + field: 'contactLastTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 120, + }, + { + title: '所属部门', + field: 'ownerUserDeptName', + minWidth: 100, + }, + { + title: '更新时间', + field: 'updateTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '创建人', + field: 'creatorName', + minWidth: 120, + }, + { + title: '合同状态', + field: 'auditStatus', + fixed: 'right', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_AUDIT_STATUS }, + }, + }, + { + title: '操作', + field: 'actions', + fixed: 'right', + minWidth: 130, + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/contract/detail/data.ts b/apps/web-ele/src/views/crm/contract/detail/data.ts new file mode 100644 index 000000000..bfe7b5606 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/detail/data.ts @@ -0,0 +1,100 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情头部的配置 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'totalPrice', + label: '合同金额(元)', + render: (val) => erpPriceInputFormatter(val) as string, + }, + { + field: 'orderDate', + label: '下单时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'totalReceivablePrice', + label: '回款金额(元)', + render: (val) => erpPriceInputFormatter(val) as string, + }, + { + field: 'ownerUserName', + label: '负责人', + }, + ]; +} + +/** 详情基本信息的配置 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'no', + label: '合同编号', + }, + { + field: 'name', + label: '合同名称', + }, + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'businessName', + label: '商机名称', + }, + { + field: 'totalPrice', + label: '合同金额(元)', + render: (val) => erpPriceInputFormatter(val) as string, + }, + { + field: 'orderDate', + label: '下单时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'startTime', + label: '合同开始时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'endTime', + label: '合同结束时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'signContactName', + label: '客户签约人', + }, + { + field: 'signUserName', + label: '公司签约人', + }, + { + field: 'remark', + label: '备注', + }, + { + field: 'auditStatus', + label: '合同状态', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_AUDIT_STATUS, + value: val, + }), + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/contract/detail/index.vue b/apps/web-ele/src/views/crm/contract/detail/index.vue new file mode 100644 index 000000000..9581c8159 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/detail/index.vue @@ -0,0 +1,170 @@ + + + diff --git a/apps/web-ele/src/views/crm/contract/detail/modules/info.vue b/apps/web-ele/src/views/crm/contract/detail/modules/info.vue new file mode 100644 index 000000000..31f1d8c4d --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/detail/modules/info.vue @@ -0,0 +1,36 @@ + + + diff --git a/apps/web-ele/src/views/crm/contract/index.vue b/apps/web-ele/src/views/crm/contract/index.vue new file mode 100644 index 000000000..50adfc105 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/index.vue @@ -0,0 +1,267 @@ + + + diff --git a/apps/web-ele/src/views/crm/contract/modules/form.vue b/apps/web-ele/src/views/crm/contract/modules/form.vue new file mode 100644 index 000000000..b39065435 --- /dev/null +++ b/apps/web-ele/src/views/crm/contract/modules/form.vue @@ -0,0 +1,121 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/data.ts b/apps/web-ele/src/views/crm/customer/data.ts new file mode 100644 index 000000000..a3fdcf403 --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/data.ts @@ -0,0 +1,396 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { z } from '@vben/common-ui'; +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; + +import { getAreaTree } from '#/api/system/area'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '客户名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入客户名称', + allowClear: true, + }, + }, + { + fieldName: 'source', + label: '客户来源', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE, 'number'), + placeholder: '请选择客户来源', + allowClear: true, + }, + }, + { + fieldName: 'mobile', + label: '手机', + component: 'Input', + componentProps: { + placeholder: '请输入手机', + allowClear: true, + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + allowClear: true, + }, + defaultValue: userStore.userInfo?.id, + rules: 'required', + }, + { + fieldName: 'telephone', + label: '电话', + component: 'Input', + componentProps: { + placeholder: '请输入电话', + allowClear: true, + }, + }, + { + fieldName: 'email', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + allowClear: true, + }, + }, + { + fieldName: 'wechat', + label: '微信', + component: 'Input', + componentProps: { + placeholder: '请输入微信', + allowClear: true, + }, + }, + { + fieldName: 'qq', + label: 'QQ', + component: 'Input', + componentProps: { + placeholder: '请输入QQ', + allowClear: true, + }, + }, + { + fieldName: 'industryId', + label: '客户行业', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY, 'number'), + placeholder: '请选择客户行业', + allowClear: true, + }, + }, + { + fieldName: 'level', + label: '客户级别', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL, 'number'), + placeholder: '请选择客户级别', + allowClear: true, + }, + }, + { + fieldName: 'areaId', + label: '地址', + component: 'ApiTreeSelect', + componentProps: { + api: getAreaTree, + fieldNames: { label: 'name', value: 'id', children: 'children' }, + placeholder: '请选择地址', + allowClear: true, + }, + }, + { + fieldName: 'detailAddress', + label: '详细地址', + component: 'Input', + componentProps: { + placeholder: '请输入详细地址', + allowClear: true, + }, + }, + { + fieldName: 'contactNextTime', + label: '下次联系时间', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + placeholder: '请选择下次联系时间', + allowClear: true, + class: '!w-full', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '客户名称', + component: 'Input', + componentProps: { + placeholder: '请输入客户名称', + allowClear: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号', + component: 'Input', + componentProps: { + placeholder: '请输入手机号', + allowClear: true, + }, + }, + { + fieldName: 'telephone', + label: '电话', + component: 'Input', + componentProps: { + placeholder: '请输入电话', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + placeholder: ['开始日期', '结束日期'], + allowClear: true, + }, + }, + ]; +} + +/** 导入客户的表单 */ +export function useImportFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + allowClear: true, + class: 'w-full', + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + rules: 'required', + }, + { + fieldName: 'file', + label: '客户数据', + component: 'Upload', + rules: 'required', + help: '仅允许导入 xls、xlsx 格式文件', + }, + { + fieldName: 'updateSupport', + label: '是否覆盖', + component: 'Switch', + componentProps: { + activeValue: true, + inactiveValue: false, + }, + rules: z.boolean().default(false), + help: '是否更新已经存在的客户数据', + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '客户名称', + fixed: 'left', + minWidth: 160, + slots: { default: 'name' }, + }, + { + field: 'source', + title: '客户来源', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE }, + }, + }, + { + field: 'mobile', + title: '手机', + minWidth: 120, + }, + { + field: 'telephone', + title: '电话', + minWidth: 130, + }, + { + field: 'email', + title: '邮箱', + minWidth: 180, + }, + { + field: 'level', + title: '客户级别', + minWidth: 135, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_LEVEL }, + }, + }, + { + field: 'industryId', + title: '客户行业', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY }, + }, + }, + { + field: 'contactNextTime', + title: '下次联系时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'lockStatus', + title: '锁定状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'dealStatus', + title: '成交状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'contactLastTime', + title: '最后跟进时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'contactLastContent', + title: '最后跟进记录', + minWidth: 200, + }, + { + field: 'detailAddress', + title: '地址', + minWidth: 180, + }, + { + field: 'poolDay', + title: '距离进入公海天数', + minWidth: 140, + formatter: ({ cellValue }) => + cellValue === null ? '-' : `${cellValue} 天`, + }, + { + field: 'ownerUserName', + title: '负责人', + minWidth: 100, + }, + { + field: 'ownerUserDeptName', + title: '所属部门', + minWidth: 100, + }, + { + field: 'updateTime', + title: '更新时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 100, + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/customer/detail/data.ts b/apps/web-ele/src/views/crm/customer/detail/data.ts new file mode 100644 index 000000000..9d55b4077 --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/detail/data.ts @@ -0,0 +1,130 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { useUserStore } from '@vben/stores'; +import { formatDateTime } from '@vben/utils'; + +import { getSimpleUserList } from '#/api/system/user'; +import { DictTag } from '#/components/dict-tag'; + +/** 分配客户表单 */ +export function useDistributeFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + defaultValue: userStore.userInfo?.id, + rules: 'required', + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'level', + label: '客户级别', + render: (val) => + h(DictTag, { type: DICT_TYPE.CRM_CUSTOMER_LEVEL, value: val }), + }, + { + field: 'dealStatus', + label: '成交状态', + render: (val) => (val ? '已成交' : '未成交'), + }, + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'name', + label: '客户名称', + }, + { + field: 'source', + label: '客户来源', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_CUSTOMER_SOURCE, + value: val, + }), + }, + { + field: 'mobile', + label: '手机', + }, + { + field: 'telephone', + label: '电话', + }, + { + field: 'email', + label: '邮箱', + }, + { + field: 'areaName', + label: '地址', + render: (val, data) => { + const areaName = val ?? ''; + const detailAddress = data?.detailAddress ?? ''; + return [areaName, detailAddress].filter(Boolean).join(' '); + }, + }, + { + field: 'qq', + label: 'QQ', + }, + { + field: 'wechat', + label: '微信', + }, + { + field: 'industryId', + label: '客户行业', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY, + value: val, + }), + }, + { + field: 'contactNextTime', + label: '下次联系时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'remark', + label: '备注', + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/customer/detail/index.vue b/apps/web-ele/src/views/crm/customer/detail/index.vue new file mode 100644 index 000000000..77c0e1b9c --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/detail/index.vue @@ -0,0 +1,311 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/detail/modules/distribute-form.vue b/apps/web-ele/src/views/crm/customer/detail/modules/distribute-form.vue new file mode 100644 index 000000000..aa7ca2c05 --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/detail/modules/distribute-form.vue @@ -0,0 +1,69 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/detail/modules/info.vue b/apps/web-ele/src/views/crm/customer/detail/modules/info.vue new file mode 100644 index 000000000..abeaa243d --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/detail/modules/info.vue @@ -0,0 +1,36 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/index.vue b/apps/web-ele/src/views/crm/customer/index.vue new file mode 100644 index 000000000..b3f67b093 --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/index.vue @@ -0,0 +1,217 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/limitConfig/data.ts b/apps/web-ele/src/views/crm/customer/limitConfig/data.ts new file mode 100644 index 000000000..3b05ebfc2 --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/limitConfig/data.ts @@ -0,0 +1,156 @@ +import type { VbenFormSchema } from '@vben/common-ui'; + +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { handleTree } from '@vben/utils'; + +import { LimitConfType } from '#/api/crm/customer/limitConfig'; +import { getSimpleDeptList } from '#/api/system/dept'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(confType: LimitConfType): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'type', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'userIds', + label: '规则适用人群', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + mode: 'multiple', + allowClear: true, + placeholder: '请选择规则适用人群', + }, + }, + { + fieldName: 'deptIds', + label: '规则适用部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getSimpleDeptList(); + return handleTree(data); + }, + multiple: true, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择规则适用部门', + treeDefaultExpandAll: true, + }, + }, + { + fieldName: 'maxCount', + label: + confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT + ? '拥有客户数上限' + : '锁定客户数上限', + component: 'InputNumber', + componentProps: { + placeholder: `请输入${ + LimitConfType.CUSTOMER_QUANTITY_LIMIT === confType + ? '拥有客户数上限' + : '锁定客户数上限' + }`, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'dealCountEnabled', + label: '成交客户是否占用拥有客户数', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + }, + dependencies: { + triggerFields: [''], + show: () => confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT, + }, + defaultValue: false, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + confType: LimitConfType, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + fixed: 'left', + }, + { + field: 'users', + title: '规则适用人群', + formatter: ({ cellValue }) => { + return cellValue + .map((user: any) => { + return user.nickname; + }) + .join(','); + }, + }, + { + field: 'depts', + title: '规则适用部门', + formatter: ({ cellValue }) => { + return cellValue + .map((dept: any) => { + return dept.name; + }) + .join(','); + }, + }, + { + field: 'maxCount', + title: + confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT + ? '拥有客户数上限' + : '锁定客户数上限', + }, + { + field: 'dealCountEnabled', + title: '成交客户是否占用拥有客户数', + visible: confType === LimitConfType.CUSTOMER_QUANTITY_LIMIT, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/customer/limitConfig/index.vue b/apps/web-ele/src/views/crm/customer/limitConfig/index.vue new file mode 100644 index 000000000..a4cedb13f --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/limitConfig/index.vue @@ -0,0 +1,172 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/limitConfig/modules/form.vue b/apps/web-ele/src/views/crm/customer/limitConfig/modules/form.vue new file mode 100644 index 000000000..ded5a127d --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/limitConfig/modules/form.vue @@ -0,0 +1,100 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/modules/form.vue b/apps/web-ele/src/views/crm/customer/modules/form.vue new file mode 100644 index 000000000..549adc9e3 --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/modules/form.vue @@ -0,0 +1,85 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/modules/import-form.vue b/apps/web-ele/src/views/crm/customer/modules/import-form.vue new file mode 100644 index 000000000..f85545dd4 --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/modules/import-form.vue @@ -0,0 +1,86 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/pool/data.ts b/apps/web-ele/src/views/crm/customer/pool/data.ts new file mode 100644 index 000000000..d057b67ed --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/pool/data.ts @@ -0,0 +1,161 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '客户名称', + component: 'Input', + componentProps: { + placeholder: '请输入客户名称', + allowClear: true, + }, + }, + { + fieldName: 'mobile', + label: '手机', + component: 'Input', + componentProps: { + placeholder: '请输入手机', + allowClear: true, + }, + }, + { + fieldName: 'industryId', + label: '所属行业', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_INDUSTRY, 'number'), + placeholder: '请选择所属行业', + allowClear: true, + }, + }, + { + fieldName: 'level', + label: '客户级别', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_LEVEL, 'number'), + placeholder: '请选择客户级别', + allowClear: true, + }, + }, + { + fieldName: 'source', + label: '客户来源', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_CUSTOMER_SOURCE, 'number'), + placeholder: '请选择客户来源', + allowClear: true, + }, + }, + ]; +} + +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '客户名称', + field: 'name', + minWidth: 160, + fixed: 'left', + slots: { default: 'name' }, + }, + { + title: '客户来源', + field: 'source', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE }, + }, + }, + { + title: '手机', + field: 'mobile', + minWidth: 120, + }, + { + title: '电话', + field: 'telephone', + minWidth: 120, + }, + { + title: '邮箱', + field: 'email', + minWidth: 140, + }, + { + title: '客户级别', + field: 'level', + minWidth: 135, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_LEVEL }, + }, + }, + { + title: '客户行业', + field: 'industryId', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY }, + }, + }, + { + title: '下次联系时间', + field: 'contactNextTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '备注', + field: 'remark', + minWidth: 200, + }, + { + title: '成交状态', + field: 'dealStatus', + minWidth: 80, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + title: '最后跟进时间', + field: 'contactLastTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '最后跟进记录', + field: 'contactLastContent', + minWidth: 200, + }, + { + title: '更新时间', + field: 'updateTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '创建人', + field: 'creatorName', + minWidth: 100, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/customer/pool/index.vue b/apps/web-ele/src/views/crm/customer/pool/index.vue new file mode 100644 index 000000000..abdc0d3ec --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/pool/index.vue @@ -0,0 +1,97 @@ + + + diff --git a/apps/web-ele/src/views/crm/customer/poolConfig/data.ts b/apps/web-ele/src/views/crm/customer/poolConfig/data.ts new file mode 100644 index 000000000..5fa6e264f --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/poolConfig/data.ts @@ -0,0 +1,83 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { z } from '#/adapter/form'; + +export const schema: VbenFormSchema[] = [ + { + component: 'RadioGroup', + fieldName: 'enabled', + label: '客户公海规则设置', + componentProps: { + options: [ + { label: '开启', value: true }, + { label: '关闭', value: false }, + ], + }, + }, + { + component: 'Input', + fieldName: 'contactExpireDays', + componentProps: { + placeholder: '请输入天数', + class: '!w-full', + }, + renderComponentContent: () => ({ + append: () => '天不跟进或', + }), + rules: z.coerce.number().int().min(0, '天数不能小于 0'), + dependencies: { + triggerFields: ['enabled'], + show: (value) => value.enabled, + }, + }, + { + component: 'Input', + fieldName: 'dealExpireDays', + componentProps: { + placeholder: '请输入天数', + class: '!w-full', + }, + renderComponentContent: () => ({ + prepend: () => '或', + append: () => '天未成交', + }), + rules: z.coerce.number().int().min(0, '天数不能小于 0'), + dependencies: { + triggerFields: ['enabled'], + show: (value) => value.enabled, + }, + }, + { + component: 'RadioGroup', + fieldName: 'notifyEnabled', + label: '提前提醒设置', + componentProps: { + options: [ + { label: '开启', value: true }, + { label: '关闭', value: false }, + ], + }, + dependencies: { + triggerFields: ['enabled'], + show: (value) => value.enabled, + }, + defaultValue: false, + }, + { + component: 'Input', + fieldName: 'notifyDays', + componentProps: { + placeholder: '请输入天数', + class: '!w-full', + }, + renderComponentContent: () => ({ + prepend: () => '提前', + append: () => '天提醒', + }), + rules: z.coerce.number().int().min(0, '天数不能小于 0'), + dependencies: { + triggerFields: ['notifyEnabled'], + show: (value) => value.enabled && value.notifyEnabled, + }, + }, +]; diff --git a/apps/web-ele/src/views/crm/customer/poolConfig/index.vue b/apps/web-ele/src/views/crm/customer/poolConfig/index.vue new file mode 100644 index 000000000..5a40003c6 --- /dev/null +++ b/apps/web-ele/src/views/crm/customer/poolConfig/index.vue @@ -0,0 +1,69 @@ + + + diff --git a/apps/web-ele/src/views/crm/followup/data.ts b/apps/web-ele/src/views/crm/followup/data.ts new file mode 100644 index 000000000..514a2d98d --- /dev/null +++ b/apps/web-ele/src/views/crm/followup/data.ts @@ -0,0 +1,195 @@ +import type { Ref } from 'vue'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { getBusinessPageByCustomer } from '#/api/crm/business'; +import { getContactPageByCustomer } from '#/api/crm/contact'; +import { BizTypeEnum } from '#/api/crm/permission'; + +/** 新增/修改的表单 */ +export function useFormSchema( + bizId: Ref, +): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'bizId', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'bizType', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'type', + label: '跟进类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_FOLLOW_UP_TYPE, 'number'), + }, + rules: 'required', + }, + { + fieldName: 'nextTime', + label: '下次联系时间', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + placeholder: '请选择下次联系时间', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'content', + label: '跟进内容', + component: 'Textarea', + rules: 'required', + }, + { + fieldName: 'picUrls', + label: '图片', + component: 'ImageUpload', + }, + { + fieldName: 'fileUrls', + label: '附件', + component: 'FileUpload', + }, + { + fieldName: 'contactIds', + label: '关联联系人', + component: 'ApiSelect', + componentProps: { + api: async () => { + if (!bizId.value) { + return []; + } + const res = await getContactPageByCustomer({ + pageNo: 1, + pageSize: 100, + customerId: bizId.value, + }); + return res.list; + }, + labelField: 'name', + valueField: 'id', + mode: 'multiple', + }, + }, + { + fieldName: 'businessIds', + label: '关联商机', + component: 'ApiSelect', + componentProps: { + api: async () => { + if (!bizId.value) { + return []; + } + const res = await getBusinessPageByCustomer({ + pageNo: 1, + pageSize: 100, + customerId: bizId.value, + }); + return res.list; + }, + labelField: 'name', + valueField: 'id', + mode: 'multiple', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + bizType: number, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + }, + { field: 'creatorName', title: '跟进人' }, + { + field: 'type', + title: '跟进类型', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_FOLLOW_UP_TYPE }, + }, + }, + { field: 'content', title: '跟进内容' }, + { + field: 'nextTime', + title: '下次联系时间', + formatter: 'formatDateTime', + }, + { + field: 'contacts', + title: '关联联系人', + visible: bizType === BizTypeEnum.CRM_CUSTOMER, + slots: { default: 'contacts' }, + }, + { + field: 'businesses', + title: '关联商机', + visible: bizType === BizTypeEnum.CRM_CUSTOMER, + slots: { default: 'businesses' }, + }, + { + field: 'actions', + title: '操作', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的系统字段 */ +export function useFollowUpDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'contactLastContent', + label: '最后跟进记录', + }, + { + field: 'contactLastTime', + label: '最后跟进时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'creatorName', + label: '创建人', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'updateTime', + label: '更新时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/followup/index.ts b/apps/web-ele/src/views/crm/followup/index.ts new file mode 100644 index 000000000..911cbed51 --- /dev/null +++ b/apps/web-ele/src/views/crm/followup/index.ts @@ -0,0 +1 @@ +export { default as FollowUp } from './index.vue'; diff --git a/apps/web-ele/src/views/crm/followup/index.vue b/apps/web-ele/src/views/crm/followup/index.vue new file mode 100644 index 000000000..03fea5534 --- /dev/null +++ b/apps/web-ele/src/views/crm/followup/index.vue @@ -0,0 +1,165 @@ + + + diff --git a/apps/web-ele/src/views/crm/followup/modules/form.vue b/apps/web-ele/src/views/crm/followup/modules/form.vue new file mode 100644 index 000000000..51df040a9 --- /dev/null +++ b/apps/web-ele/src/views/crm/followup/modules/form.vue @@ -0,0 +1,81 @@ + + + diff --git a/apps/web-ele/src/views/crm/permission/index.ts b/apps/web-ele/src/views/crm/permission/index.ts new file mode 100644 index 000000000..2988df4dd --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/index.ts @@ -0,0 +1,2 @@ +export { default as PermissionList } from './modules/list.vue'; +export { default as TransferForm } from './modules/transfer-form.vue'; diff --git a/apps/web-ele/src/views/crm/permission/modules/data.ts b/apps/web-ele/src/views/crm/permission/modules/data.ts new file mode 100644 index 000000000..fdbe9bcbd --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/modules/data.ts @@ -0,0 +1,233 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { BizTypeEnum, PermissionLevelEnum } from '#/api/crm/permission'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useTransferFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'newOwnerUserId', + label: '选择新负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'oldOwnerHandler', + label: '老负责人', + component: 'RadioGroup', + componentProps: { + options: [ + { + label: '加入团队', + value: true, + }, + { + label: '移除', + value: false, + }, + ], + }, + rules: 'required', + }, + { + fieldName: 'oldOwnerPermissionLevel', + label: '老负责人权限级别', + component: 'RadioGroup', + componentProps: { + options: getDictOptions( + DICT_TYPE.CRM_PERMISSION_LEVEL, + 'number', + ).filter((dict) => dict.value !== PermissionLevelEnum.OWNER), + }, + dependencies: { + triggerFields: ['oldOwnerHandler'], + show: (values) => values.oldOwnerHandler, + trigger(values) { + if (!values.oldOwnerHandler) { + values.oldOwnerPermissionLevel = undefined; + } + }, + }, + rules: 'required', + }, + { + fieldName: 'toBizTypes', + label: '同时转移', + component: 'CheckboxGroup', + componentProps: { + options: [ + { + label: '联系人', + value: BizTypeEnum.CRM_CONTACT, + }, + { + label: '商机', + value: BizTypeEnum.CRM_BUSINESS, + }, + { + label: '合同', + value: BizTypeEnum.CRM_CONTRACT, + }, + ], + }, + }, + ]; +} + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'bizId', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'ids', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'userId', + label: '选择人员', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + dependencies: { + triggerFields: ['ids'], + show: (values) => { + return values.ids === undefined; + }, + }, + }, + { + fieldName: 'level', + label: '权限级别', + component: 'RadioGroup', + componentProps: { + options: getDictOptions( + DICT_TYPE.CRM_PERMISSION_LEVEL, + 'number', + ).filter((dict) => dict.value !== PermissionLevelEnum.OWNER), + }, + rules: 'required', + }, + { + fieldName: 'bizType', + label: 'Crm 类型', + component: 'RadioGroup', + componentProps: { + options: [ + { + label: '联系人', + value: BizTypeEnum.CRM_CONTACT, + }, + { + label: '商机', + value: BizTypeEnum.CRM_BUSINESS, + }, + { + label: '合同', + value: BizTypeEnum.CRM_CONTRACT, + }, + ], + }, + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'toBizTypes', + label: '同时添加至', + component: 'CheckboxGroup', + componentProps: { + options: [ + { + label: '联系人', + value: BizTypeEnum.CRM_CONTACT, + }, + { + label: '商机', + value: BizTypeEnum.CRM_BUSINESS, + }, + { + label: '合同', + value: BizTypeEnum.CRM_CONTRACT, + }, + ], + }, + dependencies: { + triggerFields: ['ids', 'bizType'], + show: (values) => { + return ( + values.ids === undefined && + values.bizType === BizTypeEnum.CRM_CUSTOMER + ); + }, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + }, + { + field: 'nickname', + title: '姓名', + }, + { + field: 'deptName', + title: '部门', + }, + { + field: 'postNames', + title: '岗位', + }, + { + field: 'level', + title: '权限级别', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PERMISSION_LEVEL }, + }, + }, + { + field: 'createTime', + title: '加入时间', + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/permission/modules/form.vue b/apps/web-ele/src/views/crm/permission/modules/form.vue new file mode 100644 index 000000000..949a77f6d --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/modules/form.vue @@ -0,0 +1,90 @@ + + + diff --git a/apps/web-ele/src/views/crm/permission/modules/list.vue b/apps/web-ele/src/views/crm/permission/modules/list.vue new file mode 100644 index 000000000..957f0dd8b --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/modules/list.vue @@ -0,0 +1,265 @@ + + + diff --git a/apps/web-ele/src/views/crm/permission/modules/transfer-form.vue b/apps/web-ele/src/views/crm/permission/modules/transfer-form.vue new file mode 100644 index 000000000..e823d4893 --- /dev/null +++ b/apps/web-ele/src/views/crm/permission/modules/transfer-form.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/category/data.ts b/apps/web-ele/src/views/crm/product/category/data.ts new file mode 100644 index 000000000..beca87319 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/category/data.ts @@ -0,0 +1,97 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { CrmProductCategoryApi } from '#/api/crm/product/category'; + +import { handleTree } from '@vben/utils'; + +import { getProductCategoryList } from '#/api/crm/product/category'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级分类', + component: 'ApiTreeSelect', + componentProps: { + clearable: true, + api: async () => { + const data = await getProductCategoryList(); + data.unshift({ + id: 0, + name: '顶级分类', + } as CrmProductCategoryApi.ProductCategory); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级分类', + showSearch: true, + treeDefaultExpandAll: true, + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '分类名称', + component: 'Input', + componentProps: { + placeholder: '请输入分类名称', + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '分类名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入分类名称', + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '分类名称', + treeNode: true, + }, + { + field: 'id', + title: '分类编号', + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + }, + { + field: 'actions', + title: '操作', + width: 250, + fixed: 'right', + slots: { + default: 'actions', + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/product/category/index.vue b/apps/web-ele/src/views/crm/product/category/index.vue new file mode 100644 index 000000000..3aa4c03c3 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/category/index.vue @@ -0,0 +1,168 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/category/modules/form.vue b/apps/web-ele/src/views/crm/product/category/modules/form.vue new file mode 100644 index 000000000..329dcdf6f --- /dev/null +++ b/apps/web-ele/src/views/crm/product/category/modules/form.vue @@ -0,0 +1,94 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/components/data.ts b/apps/web-ele/src/views/crm/product/components/data.ts new file mode 100644 index 000000000..266a91291 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/components/data.ts @@ -0,0 +1,111 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 产品详情列表的列定义 */ +export function useDetailListColumns( + showBusinessPrice: boolean, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'productName', + title: '产品名称', + }, + { + field: 'productNo', + title: '产品条码', + }, + { + field: 'productUnit', + title: '产品单位', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_UNIT }, + }, + }, + { + field: 'productPrice', + title: '产品价格(元)', + formatter: 'formatAmount2', + }, + { + field: 'businessPrice', + title: '商机价格(元)', + formatter: 'formatAmount2', + visible: showBusinessPrice, + }, + { + field: 'contractPrice', + title: '合同价格(元)', + formatter: 'formatAmount2', + visible: !showBusinessPrice, + }, + { + field: 'count', + title: '数量', + formatter: 'formatAmount3', + }, + { + field: 'totalPrice', + title: '合计金额(元)', + formatter: 'formatAmount2', + }, + ]; +} + +/** 产品编辑表格的列定义 */ +export function useProductEditTableColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50 }, + { + field: 'productId', + title: '产品名称', + minWidth: 100, + slots: { default: 'productId' }, + }, + { + field: 'productNo', + title: '条码', + minWidth: 150, + }, + { + field: 'productUnit', + title: '单位', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_UNIT }, + }, + }, + { + field: 'productPrice', + title: '价格(元)', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'sellingPrice', + title: '售价(元)', + minWidth: 100, + slots: { default: 'sellingPrice' }, + }, + { + field: 'count', + title: '数量', + minWidth: 100, + slots: { default: 'count' }, + }, + { + field: 'totalPrice', + title: '合计', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/product/components/detail-list.vue b/apps/web-ele/src/views/crm/product/components/detail-list.vue new file mode 100644 index 000000000..5ce23bc0d --- /dev/null +++ b/apps/web-ele/src/views/crm/product/components/detail-list.vue @@ -0,0 +1,79 @@ + + + + diff --git a/apps/web-ele/src/views/crm/product/components/edit-table.vue b/apps/web-ele/src/views/crm/product/components/edit-table.vue new file mode 100644 index 000000000..0be88081c --- /dev/null +++ b/apps/web-ele/src/views/crm/product/components/edit-table.vue @@ -0,0 +1,201 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/components/index.ts b/apps/web-ele/src/views/crm/product/components/index.ts new file mode 100644 index 000000000..dc527926a --- /dev/null +++ b/apps/web-ele/src/views/crm/product/components/index.ts @@ -0,0 +1,2 @@ +export { default as ProductDetailsList } from './detail-list.vue'; +export { default as ProductEditTable } from './edit-table.vue'; diff --git a/apps/web-ele/src/views/crm/product/data.ts b/apps/web-ele/src/views/crm/product/data.ts new file mode 100644 index 000000000..ef6efe9fe --- /dev/null +++ b/apps/web-ele/src/views/crm/product/data.ts @@ -0,0 +1,231 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; +import { handleTree } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getProductCategoryList } from '#/api/crm/product/category'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '产品名称', + rules: 'required', + componentProps: { + placeholder: '请输入产品名称', + clearable: true, + }, + }, + { + component: 'ApiSelect', + fieldName: 'ownerUserId', + label: '负责人', + rules: 'required', + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + clearable: true, + }, + defaultValue: userStore.userInfo?.id, + }, + { + component: 'Input', + fieldName: 'no', + label: '产品编码', + rules: 'required', + componentProps: { + placeholder: '请输入产品编码', + clearable: true, + }, + }, + { + component: 'ApiTreeSelect', + fieldName: 'categoryId', + label: '产品类型', + rules: 'required', + componentProps: { + api: async () => { + const data = await getProductCategoryList(); + return handleTree(data); + }, + fieldNames: { label: 'name', value: 'id', children: 'children' }, + placeholder: '请选择产品类型', + clearable: true, + }, + }, + { + fieldName: 'unit', + label: '产品单位', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_PRODUCT_UNIT, 'number'), + placeholder: '请选择产品单位', + clearable: true, + }, + rules: 'required', + }, + { + component: 'InputNumber', + fieldName: 'price', + label: '价格(元)', + rules: 'required', + componentProps: { + min: 0, + precision: 2, + step: 0.1, + placeholder: '请输入产品价格', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + component: 'Textarea', + fieldName: 'description', + label: '产品描述', + componentProps: { + placeholder: '请输入产品描述', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '上架状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '产品名称', + component: 'Input', + componentProps: { + placeholder: '请输入产品名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '上架状态', + component: 'Select', + componentProps: { + clearable: true, + placeholder: '请选择上架状态', + options: getDictOptions(DICT_TYPE.CRM_PRODUCT_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '产品编号', + visible: false, + }, + { + field: 'name', + title: '产品名称', + minWidth: 240, + slots: { default: 'name' }, + }, + { + field: 'categoryName', + title: '产品类型', + minWidth: 120, + }, + { + field: 'unit', + title: '产品单位', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_UNIT }, + }, + }, + { + field: 'no', + title: '产品编码', + minWidth: 120, + }, + { + field: 'price', + title: '价格(元)', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'description', + title: '产品描述', + minWidth: 200, + }, + { + field: 'status', + title: '上架状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_PRODUCT_STATUS }, + }, + minWidth: 120, + }, + { + field: 'ownerUserName', + title: '负责人', + minWidth: 120, + }, + { + field: 'updateTime', + title: '更新时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/product/detail/data.ts b/apps/web-ele/src/views/crm/product/detail/data.ts new file mode 100644 index 000000000..0adf792cb --- /dev/null +++ b/apps/web-ele/src/views/crm/product/detail/data.ts @@ -0,0 +1,72 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { erpPriceInputFormatter } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'categoryName', + label: '产品类别', + }, + { + field: 'unit', + label: '产品单位', + render: (val) => + h(DictTag, { type: DICT_TYPE.CRM_PRODUCT_UNIT, value: val }), + }, + { + field: 'price', + label: '产品价格(元)', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'no', + label: '产品编码', + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'name', + label: '产品名称', + }, + { + field: 'no', + label: '产品编码', + }, + { + field: 'price', + label: '价格(元)', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'description', + label: '产品描述', + }, + { + field: 'categoryName', + label: '产品类型', + }, + { + field: 'status', + label: '是否上下架', + render: (val) => + h(DictTag, { type: DICT_TYPE.CRM_PRODUCT_STATUS, value: val }), + }, + { + field: 'unit', + label: '产品单位', + render: (val) => + h(DictTag, { type: DICT_TYPE.CRM_PRODUCT_UNIT, value: val }), + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/product/detail/index.vue b/apps/web-ele/src/views/crm/product/detail/index.vue new file mode 100644 index 000000000..7886b6ce2 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/detail/index.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/detail/modules/info.vue b/apps/web-ele/src/views/crm/product/detail/modules/info.vue new file mode 100644 index 000000000..813bad22e --- /dev/null +++ b/apps/web-ele/src/views/crm/product/detail/modules/info.vue @@ -0,0 +1,23 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/index.vue b/apps/web-ele/src/views/crm/product/index.vue new file mode 100644 index 000000000..07ad1caef --- /dev/null +++ b/apps/web-ele/src/views/crm/product/index.vue @@ -0,0 +1,157 @@ + + + diff --git a/apps/web-ele/src/views/crm/product/modules/form.vue b/apps/web-ele/src/views/crm/product/modules/form.vue new file mode 100644 index 000000000..ef022e6a2 --- /dev/null +++ b/apps/web-ele/src/views/crm/product/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/components/data.ts b/apps/web-ele/src/views/crm/receivable/components/data.ts new file mode 100644 index 000000000..4f3f0e134 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/components/data.ts @@ -0,0 +1,73 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 详情列表的字段 */ +export function useDetailListColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '回款编号', + field: 'no', + minWidth: 150, + fixed: 'left', + }, + { + title: '客户名称', + field: 'customerName', + minWidth: 150, + }, + { + title: '合同编号', + field: 'contract.no', + minWidth: 150, + }, + { + title: '回款日期', + field: 'returnTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '回款金额(元)', + field: 'price', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '回款方式', + field: 'returnType', + minWidth: 150, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE }, + }, + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 150, + }, + { + title: '备注', + field: 'remark', + minWidth: 150, + }, + { + title: '回款状态', + field: 'auditStatus', + minWidth: 100, + fixed: 'right', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_AUDIT_STATUS }, + }, + }, + { + title: '操作', + field: 'actions', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/components/detail-list.vue b/apps/web-ele/src/views/crm/receivable/components/detail-list.vue new file mode 100644 index 000000000..fb2f93576 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/components/detail-list.vue @@ -0,0 +1,144 @@ + + + + diff --git a/apps/web-ele/src/views/crm/receivable/components/index.ts b/apps/web-ele/src/views/crm/receivable/components/index.ts new file mode 100644 index 000000000..971ab650b --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/components/index.ts @@ -0,0 +1 @@ +export { default as ReceivableDetailsList } from './detail-list.vue'; diff --git a/apps/web-ele/src/views/crm/receivable/data.ts b/apps/web-ele/src/views/crm/receivable/data.ts new file mode 100644 index 000000000..d81d1474b --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/data.ts @@ -0,0 +1,301 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; + +import { getContractSimpleList } from '#/api/crm/contract'; +import { getCustomerSimpleList } from '#/api/crm/customer'; +import { + getReceivablePlan, + getReceivablePlanSimpleList, +} from '#/api/crm/receivable/plan'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '回款编号', + component: 'Input', + componentProps: { + placeholder: '保存时自动生成', + disabled: true, + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + rules: 'required', + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + allowClear: true, + }, + defaultValue: userStore.userInfo?.id, + }, + { + fieldName: 'customerId', + label: '客户名称', + component: 'ApiSelect', + rules: 'required', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + }, + { + fieldName: 'contractId', + label: '合同名称', + component: 'Select', + rules: 'required', + dependencies: { + triggerFields: ['customerId'], + disabled: (values) => !values.customerId || values.id, + async componentProps(values) { + if (values.customerId) { + if (!values.id) { + // 特殊:只有在【新增】时,才清空合同编号 + values.contractId = undefined; + } + const contracts = await getContractSimpleList(values.customerId); + return { + options: contracts.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择合同', + } as any; + } + }, + }, + }, + { + fieldName: 'planId', + label: '回款期数', + component: 'Select', + rules: 'required', + dependencies: { + triggerFields: ['contractId'], + disabled: (values) => !values.contractId, + async componentProps(values) { + if (values.contractId) { + values.planId = undefined; + const plans = await getReceivablePlanSimpleList( + values.customerId, + values.contractId, + ); + return { + options: plans.map((item) => ({ + label: item.period, + value: item.id, + })), + placeholder: '请选择回款期数', + onChange: async (value: any) => { + const plan = await getReceivablePlan(value); + values.returnTime = plan?.returnTime; + values.price = plan?.price; + values.returnType = plan?.returnType; + }, + } as any; + } + }, + }, + }, + { + fieldName: 'returnType', + label: '回款方式', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE, 'number'), + placeholder: '请选择回款方式', + }, + }, + { + fieldName: 'price', + label: '回款金额', + component: 'InputNumber', + rules: 'required', + componentProps: { + placeholder: '请输入回款金额', + min: 0, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'returnTime', + label: '回款日期', + component: 'DatePicker', + rules: 'required', + componentProps: { + placeholder: '请选择回款日期', + valueFormat: 'x', + format: 'YYYY-MM-DD', + class: '!w-full', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + formItemClass: 'md:col-span-2', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '回款编号', + component: 'Input', + componentProps: { + placeholder: '请输入回款编号', + allowClear: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + allowClear: true, + }, + }, + ]; +} + +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '回款编号', + field: 'no', + minWidth: 160, + fixed: 'left', + slots: { default: 'no' }, + }, + { + title: '客户名称', + field: 'customerName', + minWidth: 150, + slots: { default: 'customerName' }, + }, + { + title: '合同编号', + field: 'contract', + minWidth: 160, + slots: { default: 'contractNo' }, + }, + { + title: '回款日期', + field: 'returnTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '回款金额(元)', + field: 'price', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '回款方式', + field: 'returnType', + minWidth: 150, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE }, + }, + }, + { + title: '备注', + field: 'remark', + minWidth: 150, + }, + { + title: '合同金额(元)', + field: 'contract.totalPrice', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 150, + }, + { + title: '所属部门', + field: 'ownerUserDeptName', + minWidth: 150, + }, + { + title: '更新时间', + field: 'updateTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '创建人', + field: 'creatorName', + minWidth: 150, + }, + { + title: '回款状态', + field: 'auditStatus', + minWidth: 100, + fixed: 'right', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_AUDIT_STATUS }, + }, + }, + { + title: '操作', + field: 'actions', + minWidth: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/detail/data.ts b/apps/web-ele/src/views/crm/receivable/detail/data.ts new file mode 100644 index 000000000..dcceec53a --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/detail/data.ts @@ -0,0 +1,105 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'totalPrice', + label: '合同金额(元)', + render: (val, data) => + erpPriceInputFormatter(val ?? data?.contract?.totalPrice ?? 0), + }, + { + field: 'returnTime', + label: '回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'price', + label: '回款金额(元)', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'ownerUserName', + label: '负责人', + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'no', + label: '回款编号', + }, + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'contract', + label: '合同编号', + render: (val, data) => + val && data?.contract?.no ? data?.contract?.no : '', + }, + { + field: 'returnTime', + label: '回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'price', + label: '回款金额', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'returnType', + label: '回款方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE, + value: val, + }), + }, + { + field: 'remark', + label: '备注', + }, + ]; +} + +/** 系统信息字段 */ +export function useDetailSystemSchema(): DescriptionItemSchema[] { + return [ + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'creatorName', + label: '创建人', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'updateTime', + label: '更新时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/detail/index.vue b/apps/web-ele/src/views/crm/receivable/detail/index.vue new file mode 100644 index 000000000..70579ef30 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/detail/index.vue @@ -0,0 +1,132 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/detail/modules/info.vue b/apps/web-ele/src/views/crm/receivable/detail/modules/info.vue new file mode 100644 index 000000000..f7d18e31c --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/detail/modules/info.vue @@ -0,0 +1,35 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/index.vue b/apps/web-ele/src/views/crm/receivable/index.vue new file mode 100644 index 000000000..a5a16045b --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/index.vue @@ -0,0 +1,263 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/modules/form.vue b/apps/web-ele/src/views/crm/receivable/modules/form.vue new file mode 100644 index 000000000..ada8f56f9 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/modules/form.vue @@ -0,0 +1,102 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/components/data.ts b/apps/web-ele/src/views/crm/receivable/plan/components/data.ts new file mode 100644 index 000000000..9a67c0be2 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/components/data.ts @@ -0,0 +1,62 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** 详情列表的字段 */ +export function useDetailListColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '客户名称', + field: 'customerName', + minWidth: 150, + }, + { + title: '合同编号', + field: 'contractNo', + minWidth: 150, + }, + { + title: '期数', + field: 'period', + minWidth: 150, + }, + { + title: '计划回款(元)', + field: 'price', + minWidth: 150, + formatter: 'formatAmount2', + }, + { + title: '计划回款日期', + field: 'returnTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '提前几天提醒', + field: 'remindDays', + minWidth: 150, + }, + { + title: '提醒日期', + field: 'remindTime', + minWidth: 150, + formatter: 'formatDateTime', + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 150, + }, + { + title: '备注', + field: 'remark', + minWidth: 150, + }, + { + title: '操作', + field: 'actions', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/plan/components/detail-list.vue b/apps/web-ele/src/views/crm/receivable/plan/components/detail-list.vue new file mode 100644 index 000000000..1aa30b9dc --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/components/detail-list.vue @@ -0,0 +1,147 @@ + + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/components/index.ts b/apps/web-ele/src/views/crm/receivable/plan/components/index.ts new file mode 100644 index 000000000..cbb4b1216 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/components/index.ts @@ -0,0 +1,2 @@ +export { default as ReceivablePlanDetailsInfo } from '../detail/modules/info.vue'; +export { default as ReceivablePlanDetailsList } from './detail-list.vue'; diff --git a/apps/web-ele/src/views/crm/receivable/plan/data.ts b/apps/web-ele/src/views/crm/receivable/plan/data.ts new file mode 100644 index 000000000..50b67f68c --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/data.ts @@ -0,0 +1,289 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; +import { erpPriceInputFormatter } from '@vben/utils'; + +import { getContractSimpleList } from '#/api/crm/contract'; +import { getCustomerSimpleList } from '#/api/crm/customer'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + const userStore = useUserStore(); + return [ + { + fieldName: 'period', + label: '期数', + component: 'Input', + componentProps: { + placeholder: '保存时自动生成', + disabled: true, + }, + }, + { + fieldName: 'ownerUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + dependencies: { + triggerFields: ['id'], + disabled: (values) => values.id, + }, + defaultValue: userStore.userInfo?.id, + rules: 'required', + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + rules: 'required', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + allowClear: true, + }, + }, + { + fieldName: 'contractId', + label: '合同', + component: 'Select', + rules: 'required', + componentProps: { + options: [], + placeholder: '请选择合同', + allowClear: true, + }, + dependencies: { + triggerFields: ['customerId'], + disabled: (values) => !values.customerId, + async componentProps(values) { + if (!values.customerId) { + return { + options: [], + placeholder: '请选择客户', + }; + } + const res = await getContractSimpleList(values.customerId); + return { + options: res.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择合同', + onChange: (value: number) => { + const contract = res.find((item) => item.id === value); + if (contract) { + values.price = + contract.totalPrice - contract.totalReceivablePrice; + } + }, + }; + }, + }, + }, + { + fieldName: 'price', + label: '计划回款金额', + component: 'InputNumber', + rules: 'required', + componentProps: { + placeholder: '请输入计划回款金额', + min: 0, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'returnTime', + label: '计划回款日期', + component: 'DatePicker', + rules: 'required', + componentProps: { + placeholder: '请选择计划回款日期', + valueFormat: 'x', + format: 'YYYY-MM-DD', + class: '!w-full', + }, + defaultValue: new Date(), + }, + { + fieldName: 'remindDays', + label: '提前几天提醒', + component: 'InputNumber', + componentProps: { + placeholder: '请输入提前几天提醒', + min: 0, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'returnType', + label: '回款方式', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE, 'number'), + placeholder: '请选择回款方式', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + formItemClass: 'md:col-span-2', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择客户', + allowClear: true, + }, + }, + { + fieldName: 'contractNo', + label: '合同编号', + component: 'Input', + componentProps: { + placeholder: '请输入合同编号', + allowClear: true, + }, + }, + ]; +} + +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '客户名称', + field: 'customerName', + minWidth: 150, + fixed: 'left', + slots: { default: 'customerName' }, + }, + { + title: '合同编号', + field: 'contractNo', + minWidth: 200, + }, + { + title: '期数', + field: 'period', + minWidth: 150, + slots: { default: 'period' }, + }, + { + title: '计划回款金额(元)', + field: 'price', + minWidth: 160, + formatter: 'formatAmount2', + }, + { + title: '计划回款日期', + field: 'returnTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '提前几天提醒', + field: 'remindDays', + minWidth: 150, + }, + { + title: '提醒日期', + field: 'remindTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '回款方式', + field: 'returnType', + minWidth: 130, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE }, + }, + }, + { + title: '备注', + field: 'remark', + minWidth: 120, + }, + { + title: '负责人', + field: 'ownerUserName', + minWidth: 120, + }, + { + title: '实际回款金额(元)', + field: 'receivable.price', + minWidth: 160, + formatter: 'formatAmount2', + }, + { + title: '实际回款日期', + field: 'receivable.returnTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '未回款金额(元)', + field: 'unpaidPrice', + minWidth: 160, + formatter: ({ row }) => { + if (row.receivable) { + return erpPriceInputFormatter(row.price - row.receivable.price); + } + return erpPriceInputFormatter(row.price); + }, + }, + { + title: '更新时间', + field: 'updateTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '创建人', + field: 'creatorName', + minWidth: 100, + }, + { + title: '操作', + field: 'actions', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/plan/detail/data.ts b/apps/web-ele/src/views/crm/receivable/plan/detail/data.ts new file mode 100644 index 000000000..5059f02ab --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/detail/data.ts @@ -0,0 +1,124 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'contractNo', + label: '合同编号', + }, + { + field: 'price', + label: '计划回款金额', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'returnTime', + label: '计划回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'receivable', + label: '实际回款金额', + render: (val) => erpPriceInputFormatter(val?.price ?? 0), + }, + ]; +} + +/** 详情页的基础字段 */ +export function useDetailBaseSchema(): DescriptionItemSchema[] { + return [ + { + field: 'period', + label: '期数', + }, + { + field: 'customerName', + label: '客户名称', + }, + { + field: 'contractNo', + label: '合同编号', + }, + { + field: 'returnTime', + label: '计划回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'price', + label: '计划回款金额', + render: (val) => erpPriceInputFormatter(val), + }, + { + field: 'returnType', + label: '计划回款方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.CRM_RECEIVABLE_RETURN_TYPE, + value: val, + }), + }, + { + field: 'remindDays', + label: '提前几天提醒', + }, + { + field: 'receivable', + label: '实际回款金额', + render: (val) => erpPriceInputFormatter(val ?? 0), + }, + { + field: 'receivableRemain', + label: '未回款金额', + render: (val, data) => { + const paid = data?.receivable?.price ?? 0; + return erpPriceInputFormatter(Math.max(val - paid, 0)); + }, + }, + { + field: 'receivable.returnTime', + label: '实际回款日期', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'remark', + label: '备注', + }, + ]; +} + +/** 系统信息字段 */ +export function useDetailSystemSchema(): DescriptionItemSchema[] { + return [ + { + field: 'ownerUserName', + label: '负责人', + }, + { + field: 'creatorName', + label: '创建人', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'updateTime', + label: '更新时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/receivable/plan/detail/index.vue b/apps/web-ele/src/views/crm/receivable/plan/detail/index.vue new file mode 100644 index 000000000..0a568de16 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/detail/index.vue @@ -0,0 +1,135 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/detail/modules/info.vue b/apps/web-ele/src/views/crm/receivable/plan/detail/modules/info.vue new file mode 100644 index 000000000..7aa6f8d2e --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/detail/modules/info.vue @@ -0,0 +1,35 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/index.vue b/apps/web-ele/src/views/crm/receivable/plan/index.vue new file mode 100644 index 000000000..b66269235 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/index.vue @@ -0,0 +1,228 @@ + + + diff --git a/apps/web-ele/src/views/crm/receivable/plan/modules/form.vue b/apps/web-ele/src/views/crm/receivable/plan/modules/form.vue new file mode 100644 index 000000000..7b6230fe0 --- /dev/null +++ b/apps/web-ele/src/views/crm/receivable/plan/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/crm/statistics/customer/chartOptions.ts b/apps/web-ele/src/views/crm/statistics/customer/chartOptions.ts new file mode 100644 index 000000000..deecbd370 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/customer/chartOptions.ts @@ -0,0 +1,530 @@ +import { DICT_TYPE } from '@vben/constants'; +import { getDictLabel } from '@vben/hooks'; + +export function getChartOptions(activeTabName: any, res: any): any { + switch (activeTabName) { + case 'conversionStat': { + return { + grid: { + left: 20, + right: 40, // 让 X 轴右侧显示完整 + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '客户转化率', + type: 'line', + data: res.map((item: any) => { + return { + name: item.time, + value: item.customerCreateCount + ? ( + (item.customerDealCount / item.customerCreateCount) * + 100 + ).toFixed(2) + : 0, + }; + }), + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '客户转化率分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: { + type: 'value', + name: '转化率(%)', + }, + xAxis: { + type: 'category', + name: '日期', + data: res.map((s: any) => s.time), + }, + }; + } + case 'customerSummary': { + return { + grid: { + bottom: '5%', + containLabel: true, + left: '5%', + right: '5%', + top: '5 %', + }, + legend: {}, + series: [ + { + name: '新增客户数', + type: 'bar', + yAxisIndex: 0, + data: res.map((item: any) => item.customerCreateCount), + }, + { + name: '成交客户数', + type: 'bar', + yAxisIndex: 1, + data: res.map((item: any) => item.customerDealCount), + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '客户总量分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '新增客户数', + min: 0, + minInterval: 1, // 显示整数刻度 + }, + { + type: 'value', + name: '成交客户数', + min: 0, + minInterval: 1, // 显示整数刻度 + splitLine: { + lineStyle: { + type: 'dotted', // 右侧网格线虚化, 减少混乱 + opacity: 0.7, + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '日期', + data: res.map((item: any) => item.time), + }, + }; + } + case 'dealCycleByArea': { + const data = res.map((s: any) => { + return { + areaName: s.areaName, + customerDealCycle: s.customerDealCycle, + customerDealCount: s.customerDealCount, + }; + }); + return { + grid: { + left: 20, + right: 40, // 让 X 轴右侧显示完整 + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '成交周期(天)', + type: 'bar', + data: data.map((s: any) => s.customerDealCycle), + yAxisIndex: 0, + }, + { + name: '成交客户数', + type: 'bar', + data: data.map((s: any) => s.customerDealCount), + yAxisIndex: 1, + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '成交周期分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '成交周期(天)', + min: 0, + minInterval: 1, // 显示整数刻度 + }, + { + type: 'value', + name: '成交客户数', + min: 0, + minInterval: 1, // 显示整数刻度 + splitLine: { + lineStyle: { + type: 'dotted', // 右侧网格线虚化, 减少混乱 + opacity: 0.7, + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '区域', + data: data.map((s: any) => s.areaName), + }, + }; + } + case 'dealCycleByProduct': { + const data = res.map((s: any) => { + return { + productName: s.productName ?? '未知', + customerDealCycle: s.customerDealCount, + customerDealCount: s.customerDealCount, + }; + }); + return { + grid: { + left: 20, + right: 40, // 让 X 轴右侧显示完整 + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '成交周期(天)', + type: 'bar', + data: data.map((s: any) => s.customerDealCycle), + yAxisIndex: 0, + }, + { + name: '成交客户数', + type: 'bar', + data: data.map((s: any) => s.customerDealCount), + yAxisIndex: 1, + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '成交周期分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '成交周期(天)', + min: 0, + minInterval: 1, // 显示整数刻度 + }, + { + type: 'value', + name: '成交客户数', + min: 0, + minInterval: 1, // 显示整数刻度 + splitLine: { + lineStyle: { + type: 'dotted', // 右侧网格线虚化, 减少混乱 + opacity: 0.7, + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '产品名称', + data: data.map((s: any) => s.productName), + }, + }; + } + case 'dealCycleByUser': { + const customerDealCycleByDate = res.customerDealCycleByDate; + const customerDealCycleByUser = res.customerDealCycleByUser; + return { + grid: { + left: 20, + right: 40, // 让 X 轴右侧显示完整 + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '成交周期(天)', + type: 'bar', + data: customerDealCycleByDate.map((s: any) => s.customerDealCycle), + yAxisIndex: 0, + }, + { + name: '成交客户数', + type: 'bar', + data: customerDealCycleByUser.map((s: any) => s.customerDealCount), + yAxisIndex: 1, + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '成交周期分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '成交周期(天)', + min: 0, + minInterval: 1, // 显示整数刻度 + }, + { + type: 'value', + name: '成交客户数', + min: 0, + minInterval: 1, // 显示整数刻度 + splitLine: { + lineStyle: { + type: 'dotted', // 右侧网格线虚化, 减少混乱 + opacity: 0.7, + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '日期', + data: customerDealCycleByDate.map((s: any) => s.time), + }, + }; + } + case 'followUpSummary': { + return { + grid: { + left: 20, + right: 30, // 让 X 轴右侧显示完整 + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '跟进客户数', + type: 'bar', + yAxisIndex: 0, + data: res.map((s: any) => s.followUpCustomerCount), + }, + { + name: '跟进次数', + type: 'bar', + yAxisIndex: 1, + data: res.map((s: any) => s.followUpRecordCount), + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '客户跟进次数分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '跟进客户数', + min: 0, + minInterval: 1, // 显示整数刻度 + }, + { + type: 'value', + name: '跟进次数', + min: 0, + minInterval: 1, // 显示整数刻度 + splitLine: { + lineStyle: { + type: 'dotted', // 右侧网格线虚化, 减少混乱 + opacity: 0.7, + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '日期', + axisTick: { + alignWithLabel: true, + }, + data: res.map((s: any) => s.time), + }, + }; + } + case 'followUpType': { + return { + title: { + text: '客户跟进方式分析', + left: 'center', + }, + legend: { + orient: 'vertical', + left: 'left', + }, + tooltip: { + trigger: 'item', + formatter: '{b} : {c}% ', + }, + toolbox: { + feature: { + saveAsImage: { show: true, name: '客户跟进方式分析' }, // 保存为图片 + }, + }, + series: [ + { + name: '跟进方式', + type: 'pie', + radius: '50%', + data: res.map((s: any) => { + return { + name: getDictLabel( + DICT_TYPE.CRM_FOLLOW_UP_TYPE, + s.followUpType, + ), + value: s.followUpRecordCount, + }; + }), + emphasis: { + itemStyle: { + shadowBlur: 10, + shadowOffsetX: 0, + shadowColor: 'rgba(0, 0, 0, 0.5)', + }, + }, + }, + ], + }; + } + case 'poolSummary': { + return { + grid: { + left: 20, + right: 40, // 让 X 轴右侧显示完整 + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '进入公海客户数', + type: 'bar', + yAxisIndex: 0, + data: res.map((s: any) => s.customerPutCount), + }, + { + name: '公海领取客户数', + type: 'bar', + yAxisIndex: 1, + data: res.map((s: any) => s.customerTakeCount), + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '公海客户分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '进入公海客户数', + min: 0, + minInterval: 1, // 显示整数刻度 + }, + { + type: 'value', + name: '公海领取客户数', + min: 0, + minInterval: 1, // 显示整数刻度 + splitLine: { + lineStyle: { + type: 'dotted', // 右侧网格线虚化, 减少混乱 + opacity: 0.7, + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '日期', + data: res.map((s: any) => s.time), + }, + }; + } + default: { + return {}; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/customer/data.ts b/apps/web-ele/src/views/crm/statistics/customer/data.ts new file mode 100644 index 000000000..a2ec49ecc --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/customer/data.ts @@ -0,0 +1,401 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; +import { + beginOfDay, + endOfDay, + erpCalculatePercentage, + formatDateTime, + handleTree, +} from '@vben/utils'; + +import { getSimpleDeptList } from '#/api/system/dept'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +const userStore = useUserStore(); + +export const customerSummaryTabs = [ + { + tab: '客户总量分析', + key: 'customerSummary', + }, + { + tab: '客户跟进次数分析', + key: 'followUpSummary', + }, + { + tab: '客户跟进方式分析', + key: 'followUpType', + }, + { + tab: '客户转化率分析', + key: 'conversionStat', + }, + { + tab: '公海客户分析', + key: 'poolSummary', + }, + { + tab: '员工客户成交周期分析', + key: 'dealCycleByUser', + }, + { + tab: '地区客户成交周期分析', + key: 'dealCycleByArea', + }, + { + tab: '产品客户成交周期分析', + key: 'dealCycleByProduct', + }, +]; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'times', + label: '时间范围', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + }, + defaultValue: [ + formatDateTime(beginOfDay(new Date(Date.now() - 3600 * 1000 * 24 * 7))), + formatDateTime(endOfDay(new Date(Date.now() - 3600 * 1000 * 24))), + ], + }, + { + fieldName: 'interval', + label: '时间间隔', + component: 'Select', + componentProps: { + allowClear: true, + placeholder: '请选择时间间隔', + options: getDictOptions(DICT_TYPE.DATE_INTERVAL, 'number'), + }, + defaultValue: 2, + }, + { + fieldName: 'deptId', + label: '归属部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getSimpleDeptList(); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + treeDefaultExpandAll: true, + placeholder: '请选择归属部门', + }, + defaultValue: userStore.userInfo?.deptId, + }, + { + fieldName: 'userId', + label: '员工', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择员工', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + activeTabName: any, +): VxeTableGridOptions['columns'] { + switch (activeTabName) { + case 'conversionStat': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'customerName', + title: '客户名称', + minWidth: 100, + }, + { + field: 'contractName', + title: '合同名称', + minWidth: 200, + }, + { + field: 'totalPrice', + title: '合同总金额', + minWidth: 200, + formatter: 'formatAmount2', + }, + { + field: 'receivablePrice', + title: '回款金额', + minWidth: 200, + formatter: 'formatAmount2', + }, + { + field: 'source', + title: '客户来源', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE }, + }, + }, + { + field: 'industryId', + title: '客户行业', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY }, + }, + }, + { + field: 'ownerUserName', + title: '负责人', + minWidth: 200, + }, + { + field: 'creatorUserName', + title: '创建人', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'orderDate', + title: '下单日期', + minWidth: 200, + formatter: 'formatDateTime', + }, + ]; + } + case 'customerSummary': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'ownerUserName', + title: '员工姓名', + minWidth: 100, + }, + { + field: 'customerCreateCount', + title: '新增客户数', + minWidth: 200, + }, + { + field: 'customerDealCount', + title: '成交客户数', + minWidth: 200, + }, + { + field: 'customerDealRate', + title: '客户成交率(%)', + minWidth: 200, + formatter: ({ row }) => { + return erpCalculatePercentage( + row.customerDealCount, + row.customerCreateCount, + ); + }, + }, + { + field: 'contractPrice', + title: '合同总金额', + minWidth: 200, + formatter: 'formatAmount2', + }, + { + field: 'receivablePrice', + title: '回款金额', + minWidth: 200, + formatter: 'formatAmount2', + }, + { + field: 'creceivablePrice', + title: '未回款金额', + minWidth: 200, + formatter: ({ row }) => { + return erpCalculatePercentage( + row.receivablePrice, + row.contractPrice, + ); + }, + }, + { + field: 'ccreceivablePrice', + title: '回款完成率(%)', + formatter: ({ row }) => { + return erpCalculatePercentage( + row.receivablePrice, + row.contractPrice, + ); + }, + }, + ]; + } + case 'dealCycleByArea': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'areaName', + title: '区域', + minWidth: 200, + }, + { + field: 'customerDealCycle', + title: '成交周期(天)', + minWidth: 200, + }, + { + field: 'customerDealCount', + title: '成交客户数', + minWidth: 200, + }, + ]; + } + case 'dealCycleByProduct': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'productName', + title: '产品名称', + minWidth: 200, + }, + { + field: 'customerDealCycle', + title: '成交周期(天)', + minWidth: 200, + }, + { + field: 'customerDealCount', + title: '成交客户数', + minWidth: 200, + }, + ]; + } + case 'dealCycleByUser': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'ownerUserName', + title: '日期', + minWidth: 200, + }, + { + field: 'customerDealCycle', + title: '成交周期(天)', + minWidth: 200, + }, + { + field: 'customerDealCount', + title: '成交客户数', + minWidth: 200, + }, + ]; + } + case 'followUpSummary': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'ownerUserName', + title: '员工姓名', + minWidth: 200, + }, + { + field: 'followUpRecordCount', + title: '跟进次数', + minWidth: 200, + }, + { + field: 'followUpCustomerCount', + title: '跟进客户数', + minWidth: 200, + }, + ]; + } + case 'followUpType': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'followUpType', + title: '跟进方式', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_FOLLOW_UP_TYPE }, + }, + }, + { + field: 'followUpRecordCount', + title: '个数', + minWidth: 200, + }, + { + field: 'portion', + title: '占比(%)', + minWidth: 200, + }, + ]; + } + case 'poolSummary': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'ownerUserName', + title: '员工姓名', + minWidth: 200, + }, + { + field: 'customerPutCount', + title: '进入公海客户数', + minWidth: 200, + }, + { + field: 'customerTakeCount', + title: '公海领取客户数', + minWidth: 200, + }, + ]; + } + default: { + return []; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/customer/index.vue b/apps/web-ele/src/views/crm/statistics/customer/index.vue new file mode 100644 index 000000000..58cf1ef13 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/customer/index.vue @@ -0,0 +1,98 @@ + + + diff --git a/apps/web-ele/src/views/crm/statistics/funnel/chartOptions.ts b/apps/web-ele/src/views/crm/statistics/funnel/chartOptions.ts new file mode 100644 index 000000000..e86953f4c --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/funnel/chartOptions.ts @@ -0,0 +1,271 @@ +import { erpCalculatePercentage } from '@vben/utils'; + +export function getChartOptions( + activeTabName: any, + active: boolean, + res: any, +): any { + switch (activeTabName) { + case 'businessInversionRateSummary': { + return { + color: ['#6ca2ff', '#6ac9d7', '#ff7474'], + tooltip: { + trigger: 'axis', + axisPointer: { + // 坐标轴指示器,坐标轴触发有效 + type: 'shadow', // 默认为直线,可选为:'line' | 'shadow' + }, + }, + legend: { + data: ['赢单转化率', '商机总数', '赢单商机数'], + bottom: '0px', + itemWidth: 14, + }, + grid: { + top: '40px', + left: '40px', + right: '40px', + bottom: '40px', + containLabel: true, + borderColor: '#fff', + }, + xAxis: [ + { + type: 'category', + data: res.map((s: any) => s.time), + axisTick: { + alignWithLabel: true, + lineStyle: { width: 0 }, + }, + axisLabel: { + color: '#BDBDBD', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { color: '#BDBDBD' }, + }, + splitLine: { + show: false, + }, + }, + ], + yAxis: [ + { + type: 'value', + name: '赢单转化率', + axisTick: { + alignWithLabel: true, + lineStyle: { width: 0 }, + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}%', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { color: '#BDBDBD' }, + }, + splitLine: { + show: false, + }, + }, + { + type: 'value', + name: '商机数', + axisTick: { + alignWithLabel: true, + lineStyle: { width: 0 }, + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}个', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { color: '#BDBDBD' }, + }, + splitLine: { + show: false, + }, + }, + ], + series: [ + { + name: '赢单转化率', + type: 'line', + yAxisIndex: 0, + data: res.map((s: any) => + erpCalculatePercentage(s.businessWinCount, s.businessCount), + ), + }, + { + name: '商机总数', + type: 'bar', + yAxisIndex: 1, + barWidth: 15, + data: res.map((s: any) => s.businessCount), + }, + { + name: '赢单商机数', + type: 'bar', + yAxisIndex: 1, + barWidth: 15, + data: res.map((s: any) => s.businessWinCount), + }, + ], + }; + } + case 'businessSummary': { + return { + grid: { + left: 30, + right: 30, // 让 X 轴右侧显示完整 + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '新增商机数量', + type: 'bar', + yAxisIndex: 0, + data: res.map((s: any) => s.businessCreateCount), + }, + { + name: '新增商机金额', + type: 'bar', + yAxisIndex: 1, + data: res.map((s: any) => s.totalPrice), + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '新增商机分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '新增商机数量', + min: 0, + minInterval: 1, // 显示整数刻度 + }, + { + type: 'value', + name: '新增商机金额', + min: 0, + minInterval: 1, // 显示整数刻度 + splitLine: { + lineStyle: { + type: 'dotted', // 右侧网格线虚化, 减少混乱 + opacity: 0.7, + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '日期', + data: res.map((s: any) => s.time), + }, + }; + } + case 'funnel': { + // tips:写死 value 值是为了保持漏斗顺序不变 + const list: { name: string; value: number }[] = []; + if (active) { + list.push( + { value: 60, name: `客户-${res.customerCount || 0}个` }, + { value: 40, name: `商机-${res.businessCount || 0}个` }, + { value: 20, name: `赢单-${res.businessWinCount || 0}个` }, + ); + } else { + list.push( + { + value: res.customerCount || 0, + name: `客户-${res.customerCount || 0}个`, + }, + { + value: res.businessCount || 0, + name: `商机-${res.businessCount || 0}个`, + }, + { + value: res.businessWinCount || 0, + name: `赢单-${res.businessWinCount || 0}个`, + }, + ); + } + return { + title: { + text: '销售漏斗', + }, + tooltip: { + trigger: 'item', + formatter: '{a}
{b}', + }, + toolbox: { + feature: { + dataView: { readOnly: false }, + restore: {}, + saveAsImage: {}, + }, + }, + legend: { + data: ['客户', '商机', '赢单'], + }, + series: [ + { + name: '销售漏斗', + type: 'funnel', + left: '10%', + top: 60, + bottom: 60, + width: '80%', + min: 0, + max: 100, + minSize: '0%', + maxSize: '100%', + sort: 'descending', + gap: 2, + label: { + show: true, + position: 'inside', + }, + labelLine: { + length: 10, + lineStyle: { + width: 1, + type: 'solid', + }, + }, + itemStyle: { + borderColor: '#fff', + borderWidth: 1, + }, + emphasis: { + label: { + fontSize: 20, + }, + }, + data: list, + }, + ], + }; + } + default: { + return {}; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/funnel/data.ts b/apps/web-ele/src/views/crm/statistics/funnel/data.ts new file mode 100644 index 000000000..5dd93db19 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/funnel/data.ts @@ -0,0 +1,271 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { useUserStore } from '@vben/stores'; +import { beginOfDay, endOfDay, formatDateTime, handleTree } from '@vben/utils'; + +import { getSimpleDeptList } from '#/api/system/dept'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +const userStore = useUserStore(); + +export const customerSummaryTabs = [ + { + tab: '销售漏斗分析', + key: 'funnel', + }, + { + tab: '新增商机分析', + key: 'businessSummary', + }, + { + tab: '商机转化率分析', + key: 'businessInversionRateSummary', + }, +]; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'times', + label: '时间范围', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + }, + defaultValue: [ + formatDateTime(beginOfDay(new Date(Date.now() - 3600 * 1000 * 24 * 7))), + formatDateTime(endOfDay(new Date(Date.now() - 3600 * 1000 * 24))), + ], + }, + { + fieldName: 'interval', + label: '时间间隔', + component: 'Select', + componentProps: { + allowClear: true, + placeholder: '请选择时间间隔', + options: getDictOptions(DICT_TYPE.DATE_INTERVAL, 'number'), + }, + defaultValue: 2, + }, + { + fieldName: 'deptId', + label: '归属部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getSimpleDeptList(); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + treeDefaultExpandAll: true, + placeholder: '请选择归属部门', + }, + defaultValue: userStore.userInfo?.deptId, + }, + { + fieldName: 'userId', + label: '员工', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + allowClear: true, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择员工', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + activeTabName: any, +): VxeTableGridOptions['columns'] { + switch (activeTabName) { + case 'businessInversionRateSummary': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'name', + title: '商机名称', + minWidth: 100, + }, + { + field: 'customerName', + title: '客户名称', + minWidth: 200, + }, + { + field: 'totalPrice', + title: '商机金额(元)', + minWidth: 200, + formatter: 'formatAmount2', + }, + { + field: 'dealTime', + title: '预计成交日期', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'ownerUserName', + title: '负责人', + minWidth: 200, + }, + { + field: 'ownerUserDeptName', + title: '所属部门', + minWidth: 200, + }, + { + field: 'contactLastTime', + title: '最后跟进时间', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 100, + }, + { + field: 'statusTypeName', + title: '商机状态组', + minWidth: 100, + }, + { + field: 'statusName', + title: '商机阶段', + minWidth: 100, + }, + ]; + } + case 'businessSummary': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'name', + title: '商机名称', + minWidth: 100, + }, + { + field: 'customerName', + title: '客户名称', + minWidth: 200, + }, + { + field: 'totalPrice', + title: '商机金额(元)', + minWidth: 200, + formatter: 'formatAmount2', + }, + { + field: 'dealTime', + title: '预计成交日期', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'ownerUserName', + title: '负责人', + minWidth: 200, + }, + { + field: 'ownerUserDeptName', + title: '所属部门', + minWidth: 200, + }, + { + field: 'contactLastTime', + title: '最后跟进时间', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 200, + formatter: 'formatDateTime', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 100, + }, + { + field: 'statusTypeName', + title: '商机状态组', + minWidth: 100, + }, + { + field: 'statusName', + title: '商机阶段', + minWidth: 100, + }, + ]; + } + case 'funnel': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'endStatus', + title: '阶段', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_BUSINESS_END_STATUS_TYPE }, + }, + }, + { + field: 'businessCount', + title: '商机数', + minWidth: 200, + }, + { + field: 'totalPrice', + title: '商机总金额(元)', + minWidth: 200, + formatter: 'formatAmount2', + }, + ]; + } + default: { + return []; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/funnel/index.vue b/apps/web-ele/src/views/crm/statistics/funnel/index.vue new file mode 100644 index 000000000..7c11afd29 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/funnel/index.vue @@ -0,0 +1,144 @@ + + + diff --git a/apps/web-ele/src/views/crm/statistics/performance/chartOptions.ts b/apps/web-ele/src/views/crm/statistics/performance/chartOptions.ts new file mode 100644 index 000000000..329b4d7ff --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/performance/chartOptions.ts @@ -0,0 +1,394 @@ +export function getChartOptions(activeTabName: any, res: any): any { + switch (activeTabName) { + case 'ContractCountPerformance': { + return { + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '当月合同数量(个)', + type: 'line', + data: res.map((s: any) => s.currentMonthCount), + }, + { + name: '上月合同数量(个)', + type: 'line', + data: res.map((s: any) => s.lastMonthCount), + }, + { + name: '去年同月合同数量(个)', + type: 'line', + data: res.map((s: any) => s.lastYearCount), + }, + { + name: '环比增长率(%)', + type: 'line', + yAxisIndex: 1, + data: res.map((s: any) => + s.lastMonthCount === 0 + ? 'NULL' + : ( + ((s.currentMonthCount - s.lastMonthCount) / + s.lastMonthCount) * + 100 + ).toFixed(2), + ), + }, + { + name: '同比增长率(%)', + type: 'line', + yAxisIndex: 1, + data: res.map((s: any) => + s.lastYearCount === 0 + ? 'NULL' + : ( + ((s.currentMonthCount - s.lastYearCount) / + s.lastYearCount) * + 100 + ).toFixed(2), + ), + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '客户总量分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '数量(个)', + axisTick: { + show: false, + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD', + }, + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6', + }, + }, + }, + { + type: 'value', + name: '', + axisTick: { + alignWithLabel: true, + lineStyle: { + width: 0, + }, + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}%', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD', + }, + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6', + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '日期', + data: res.map((s: any) => s.time), + }, + }; + } + case 'ContractPricePerformance': { + return { + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '当月合同金额(元)', + type: 'line', + data: res.map((s: any) => s.currentMonthCount), + }, + { + name: '上月合同金额(元)', + type: 'line', + data: res.map((s: any) => s.lastMonthCount), + }, + { + name: '去年同月合同金额(元)', + type: 'line', + data: res.map((s: any) => s.lastYearCount), + }, + { + name: '环比增长率(%)', + type: 'line', + yAxisIndex: 1, + data: res.map((s: any) => + s.lastMonthCount === 0 + ? 'NULL' + : ( + ((s.currentMonthCount - s.lastMonthCount) / + s.lastMonthCount) * + 100 + ).toFixed(2), + ), + }, + { + name: '同比增长率(%)', + type: 'line', + yAxisIndex: 1, + data: res.map((s: any) => + s.lastYearCount === 0 + ? 'NULL' + : ( + ((s.currentMonthCount - s.lastYearCount) / + s.lastYearCount) * + 100 + ).toFixed(2), + ), + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '客户总量分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '金额(元)', + axisTick: { + show: false, + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD', + }, + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6', + }, + }, + }, + { + type: 'value', + name: '', + axisTick: { + alignWithLabel: true, + lineStyle: { + width: 0, + }, + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}%', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD', + }, + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6', + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '日期', + data: res.map((s: any) => s.time), + }, + }; + } + case 'ReceivablePricePerformance': { + return { + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: {}, + series: [ + { + name: '当月回款金额(元)', + type: 'line', + data: res.map((s: any) => s.currentMonthCount), + }, + { + name: '上月回款金额(元)', + type: 'line', + data: res.map((s: any) => s.lastMonthCount), + }, + { + name: '去年同月回款金额(元)', + type: 'line', + data: res.map((s: any) => s.lastYearCount), + }, + { + name: '环比增长率(%)', + type: 'line', + yAxisIndex: 1, + data: res.map((s: any) => + s.lastMonthCount === 0 + ? 'NULL' + : ( + ((s.currentMonthCount - s.lastMonthCount) / + s.lastMonthCount) * + 100 + ).toFixed(2), + ), + }, + { + name: '同比增长率(%)', + type: 'line', + yAxisIndex: 1, + data: res.map((s: any) => + s.lastYearCount === 0 + ? 'NULL' + : ( + ((s.currentMonthCount - s.lastYearCount) / + s.lastYearCount) * + 100 + ).toFixed(2), + ), + }, + ], + toolbox: { + feature: { + dataZoom: { + xAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '客户总量分析' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + yAxis: [ + { + type: 'value', + name: '金额(元)', + axisTick: { + show: false, + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD', + }, + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6', + }, + }, + }, + { + type: 'value', + name: '', + axisTick: { + alignWithLabel: true, + lineStyle: { + width: 0, + }, + }, + axisLabel: { + color: '#BDBDBD', + formatter: '{value}%', + }, + /** 坐标轴轴线相关设置 */ + axisLine: { + lineStyle: { + color: '#BDBDBD', + }, + }, + splitLine: { + show: true, + lineStyle: { + color: '#e6e6e6', + }, + }, + }, + ], + xAxis: { + type: 'category', + name: '日期', + data: res.map((s: any) => s.time), + }, + }; + } + default: { + return {}; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/performance/data.ts b/apps/web-ele/src/views/crm/statistics/performance/data.ts new file mode 100644 index 000000000..ab86673ac --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/performance/data.ts @@ -0,0 +1,71 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { useUserStore } from '@vben/stores'; +import { handleTree } from '@vben/utils'; + +import { getSimpleDeptList } from '#/api/system/dept'; +import { getSimpleUserList } from '#/api/system/user'; + +const userStore = useUserStore(); + +export const customerSummaryTabs = [ + { + tab: '员工合同数量统计', + key: 'ContractCountPerformance', + }, + { + tab: '员工合同金额统计', + key: 'ContractPricePerformance', + }, + { + tab: '员工回款金额统计', + key: 'ReceivablePricePerformance', + }, +]; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'time', + label: '选择年份', + component: 'DatePicker', + componentProps: { + type: 'year', + format: 'YYYY', + valueFormat: 'YYYY', + placeholder: '请选择年份', + }, + defaultValue: new Date().getFullYear().toString(), + }, + { + fieldName: 'deptId', + label: '归属部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getSimpleDeptList(); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + treeDefaultExpandAll: true, + placeholder: '请选择归属部门', + }, + defaultValue: userStore.userInfo?.deptId, + }, + { + fieldName: 'userId', + label: '员工', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择员工', + allowClear: true, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/crm/statistics/performance/index.vue b/apps/web-ele/src/views/crm/statistics/performance/index.vue new file mode 100644 index 000000000..8271ed776 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/performance/index.vue @@ -0,0 +1,186 @@ + + + diff --git a/apps/web-ele/src/views/crm/statistics/portrait/chartOptions.ts b/apps/web-ele/src/views/crm/statistics/portrait/chartOptions.ts new file mode 100644 index 000000000..b63ea2513 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/portrait/chartOptions.ts @@ -0,0 +1,440 @@ +import { DICT_TYPE } from '@vben/constants'; +import { getDictLabel } from '@vben/hooks'; + +function areaReplace(areaName: string) { + if (!areaName) { + return areaName; + } + return areaName + .replace('维吾尔自治区', '') + .replace('壮族自治区', '') + .replace('回族自治区', '') + .replace('自治区', '') + .replace('省', ''); +} + +export function getChartOptions(activeTabName: any, res: any): any { + switch (activeTabName) { + case 'area': { + const data = res.map((item: any) => { + return { + ...item, + areaName: areaReplace(item.areaName), + }; + }); + let leftMin = 0; + let leftMax = 0; + let rightMin = 0; + let rightMax = 0; + data.forEach((item: any) => { + leftMin = Math.min(leftMin, item.customerCount || 0); + leftMax = Math.max(leftMax, item.customerCount || 0); + rightMin = Math.min(rightMin, item.dealCount || 0); + rightMax = Math.max(rightMax, item.dealCount || 0); + }); + return { + left: { + title: { + text: '全部客户', + left: 'center', + }, + tooltip: { + trigger: 'item', + showDelay: 0, + transitionDuration: 0.2, + }, + visualMap: { + text: ['高', '低'], + realtime: false, + calculable: true, + top: 'middle', + inRange: { + color: ['yellow', 'lightskyblue', 'orangered'], + }, + min: leftMin, + max: leftMax, + }, + series: [ + { + name: '客户地域分布', + type: 'map', + map: 'china', + roam: false, + selectedMode: false, + data: data.map((item: any) => { + return { + name: item.areaName, + value: item.customerCount || 0, + }; + }), + }, + ], + }, + right: { + title: { + text: '成交客户', + left: 'center', + }, + tooltip: { + trigger: 'item', + showDelay: 0, + transitionDuration: 0.2, + }, + visualMap: { + text: ['高', '低'], + realtime: false, + calculable: true, + top: 'middle', + inRange: { + color: ['yellow', 'lightskyblue', 'orangered'], + }, + min: rightMin, + max: rightMax, + }, + series: [ + { + name: '客户地域分布', + type: 'map', + map: 'china', + roam: false, + selectedMode: false, + data: data.map((item: any) => { + return { + name: item.areaName, + value: item.dealCount || 0, + }; + }), + }, + ], + }, + }; + } + case 'industry': { + return { + left: { + title: { + text: '全部客户', + left: 'center', + }, + tooltip: { + trigger: 'item', + }, + legend: { + orient: 'vertical', + left: 'left', + }, + toolbox: { + feature: { + saveAsImage: { show: true, name: '全部客户' }, // 保存为图片 + }, + }, + series: [ + { + name: '全部客户', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2, + }, + label: { + show: false, + position: 'center', + }, + emphasis: { + label: { + show: true, + fontSize: 40, + fontWeight: 'bold', + }, + }, + labelLine: { + show: false, + }, + data: res.map((r: any) => { + return { + name: getDictLabel( + DICT_TYPE.CRM_CUSTOMER_INDUSTRY, + r.industryId, + ), + value: r.customerCount, + }; + }), + }, + ], + }, + right: { + title: { + text: '成交客户', + left: 'center', + }, + tooltip: { + trigger: 'item', + }, + legend: { + orient: 'vertical', + left: 'left', + }, + toolbox: { + feature: { + saveAsImage: { show: true, name: '成交客户' }, // 保存为图片 + }, + }, + series: [ + { + name: '成交客户', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2, + }, + label: { + show: false, + position: 'center', + }, + emphasis: { + label: { + show: true, + fontSize: 40, + fontWeight: 'bold', + }, + }, + labelLine: { + show: false, + }, + data: res.map((r: any) => { + return { + name: getDictLabel( + DICT_TYPE.CRM_CUSTOMER_INDUSTRY, + r.industryId, + ), + value: r.dealCount, + }; + }), + }, + ], + }, + }; + } + case 'level': { + return { + left: { + title: { + text: '全部客户', + left: 'center', + }, + tooltip: { + trigger: 'item', + }, + legend: { + orient: 'vertical', + left: 'left', + }, + toolbox: { + feature: { + saveAsImage: { show: true, name: '全部客户' }, // 保存为图片 + }, + }, + series: [ + { + name: '全部客户', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2, + }, + label: { + show: false, + position: 'center', + }, + emphasis: { + label: { + show: true, + fontSize: 40, + fontWeight: 'bold', + }, + }, + labelLine: { + show: false, + }, + data: res.map((r: any) => { + return { + name: getDictLabel(DICT_TYPE.CRM_CUSTOMER_LEVEL, r.level), + value: r.customerCount, + }; + }), + }, + ], + }, + right: { + title: { + text: '成交客户', + left: 'center', + }, + tooltip: { + trigger: 'item', + }, + legend: { + orient: 'vertical', + left: 'left', + }, + toolbox: { + feature: { + saveAsImage: { show: true, name: '成交客户' }, // 保存为图片 + }, + }, + series: [ + { + name: '成交客户', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2, + }, + label: { + show: false, + position: 'center', + }, + emphasis: { + label: { + show: true, + fontSize: 40, + fontWeight: 'bold', + }, + }, + labelLine: { + show: false, + }, + data: res.map((r: any) => { + return { + name: getDictLabel(DICT_TYPE.CRM_CUSTOMER_LEVEL, r.level), + value: r.dealCount, + }; + }), + }, + ], + }, + }; + } + case 'source': { + return { + left: { + title: { + text: '全部客户', + left: 'center', + }, + tooltip: { + trigger: 'item', + }, + legend: { + orient: 'vertical', + left: 'left', + }, + toolbox: { + feature: { + saveAsImage: { show: true, name: '全部客户' }, // 保存为图片 + }, + }, + series: [ + { + name: '全部客户', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2, + }, + label: { + show: false, + position: 'center', + }, + emphasis: { + label: { + show: true, + fontSize: 40, + fontWeight: 'bold', + }, + }, + labelLine: { + show: false, + }, + data: res.map((r: any) => { + return { + name: getDictLabel(DICT_TYPE.CRM_CUSTOMER_SOURCE, r.source), + value: r.customerCount, + }; + }), + }, + ], + }, + right: { + title: { + text: '成交客户', + left: 'center', + }, + tooltip: { + trigger: 'item', + }, + legend: { + orient: 'vertical', + left: 'left', + }, + toolbox: { + feature: { + saveAsImage: { show: true, name: '成交客户' }, // 保存为图片 + }, + }, + series: [ + { + name: '成交客户', + type: 'pie', + radius: ['40%', '70%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 10, + borderColor: '#fff', + borderWidth: 2, + }, + label: { + show: false, + position: 'center', + }, + emphasis: { + label: { + show: true, + fontSize: 40, + fontWeight: 'bold', + }, + }, + labelLine: { + show: false, + }, + data: res.map((r: any) => { + return { + name: getDictLabel(DICT_TYPE.CRM_CUSTOMER_SOURCE, r.source), + value: r.dealCount, + }; + }), + }, + ], + }, + }; + } + default: { + return {}; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/portrait/data.ts b/apps/web-ele/src/views/crm/statistics/portrait/data.ts new file mode 100644 index 000000000..18e22e767 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/portrait/data.ts @@ -0,0 +1,200 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { useUserStore } from '@vben/stores'; +import { beginOfDay, endOfDay, formatDateTime, handleTree } from '@vben/utils'; + +import { getSimpleDeptList } from '#/api/system/dept'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +const userStore = useUserStore(); + +export const customerSummaryTabs = [ + { + tab: '城市分布分析', + key: 'area', + }, + { + tab: '客户级别分析', + key: 'level', + }, + { + tab: '客户来源分析', + key: 'source', + }, + { + tab: '客户行业分析', + key: 'industry', + }, +]; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'times', + label: '时间范围', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + }, + defaultValue: [ + formatDateTime(beginOfDay(new Date(Date.now() - 3600 * 1000 * 24 * 7))), + formatDateTime(endOfDay(new Date(Date.now() - 3600 * 1000 * 24))), + ], + }, + { + fieldName: 'deptId', + label: '归属部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getSimpleDeptList(); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + treeDefaultExpandAll: true, + placeholder: '请选择归属部门', + }, + defaultValue: userStore.userInfo?.deptId, + }, + { + fieldName: 'userId', + label: '员工', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择员工', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + activeTabName: any, +): VxeTableGridOptions['columns'] { + switch (activeTabName) { + case 'industry': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'industryId', + title: '客户行业', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_INDUSTRY }, + }, + }, + { + field: 'customerCount', + title: '客户个数', + minWidth: 200, + }, + { + field: 'dealCount', + title: '成交个数', + minWidth: 200, + }, + { + field: 'industryPortion', + title: '行业占比(%)', + minWidth: 200, + }, + { + field: 'dealPortion', + title: '成交占比(%)', + minWidth: 200, + }, + ]; + } + case 'level': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'level', + title: '客户级别', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_LEVEL }, + }, + }, + { + field: 'customerCount', + title: '客户个数', + minWidth: 200, + }, + { + field: 'dealCount', + title: '成交个数', + minWidth: 200, + }, + { + field: 'industryPortion', + title: '行业占比(%)', + minWidth: 200, + }, + { + field: 'dealPortion', + title: '成交占比(%)', + minWidth: 200, + }, + ]; + } + case 'source': { + return [ + { + type: 'seq', + title: '序号', + }, + { + field: 'source', + title: '客户来源', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.CRM_CUSTOMER_SOURCE }, + }, + }, + { + field: 'customerCount', + title: '客户个数', + minWidth: 200, + }, + { + field: 'dealCount', + title: '成交个数', + minWidth: 200, + }, + { + field: 'industryPortion', + title: '行业占比(%)', + minWidth: 200, + }, + { + field: 'dealPortion', + title: '成交占比(%)', + minWidth: 200, + }, + ]; + } + default: { + return []; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/portrait/index.vue b/apps/web-ele/src/views/crm/statistics/portrait/index.vue new file mode 100644 index 000000000..5eed96846 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/portrait/index.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-ele/src/views/crm/statistics/rank/chartOptions.ts b/apps/web-ele/src/views/crm/statistics/rank/chartOptions.ts new file mode 100644 index 000000000..a6c36e958 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/rank/chartOptions.ts @@ -0,0 +1,394 @@ +import { cloneDeep } from '@vben/utils'; + +export function getChartOptions(activeTabName: any, res: any): any { + switch (activeTabName) { + case 'contactCountRank': { + return { + dataset: { + dimensions: ['nickname', 'count'], + source: cloneDeep(res).toReversed(), + }, + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '新增联系人数排行', + type: 'bar', + }, + ], + toolbox: { + feature: { + dataZoom: { + yAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '新增联系人数排行' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + xAxis: { + type: 'value', + name: '新增联系人数(个)', + }, + yAxis: { + type: 'category', + name: '创建人', + }, + }; + } + case 'contractCountRank': { + return { + dataset: { + dimensions: ['nickname', 'count'], + source: cloneDeep(res).toReversed(), + }, + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '签约合同排行', + type: 'bar', + }, + ], + toolbox: { + feature: { + dataZoom: { + yAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '签约合同排行' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + xAxis: { + type: 'value', + name: '签约合同数(个)', + }, + yAxis: { + type: 'category', + name: '签订人', + }, + }; + } + case 'contractPriceRank': { + return { + dataset: { + dimensions: ['nickname', 'count'], + source: cloneDeep(res).toReversed(), + }, + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '合同金额排行', + type: 'bar', + }, + ], + toolbox: { + feature: { + dataZoom: { + yAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '合同金额排行' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + xAxis: { + type: 'value', + name: '合同金额(元)', + }, + yAxis: { + type: 'category', + name: '签订人', + }, + }; + } + case 'customerCountRank': { + return { + dataset: { + dimensions: ['nickname', 'count'], + source: cloneDeep(res).toReversed(), + }, + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '新增客户数排行', + type: 'bar', + }, + ], + toolbox: { + feature: { + dataZoom: { + yAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '新增客户数排行' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + xAxis: { + type: 'value', + name: '新增客户数(个)', + }, + yAxis: { + type: 'category', + name: '创建人', + }, + }; + } + case 'followCountRank': { + return { + dataset: { + dimensions: ['nickname', 'count'], + source: cloneDeep(res).toReversed(), + }, + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '跟进次数排行', + type: 'bar', + }, + ], + toolbox: { + feature: { + dataZoom: { + yAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '跟进次数排行' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + xAxis: { + type: 'value', + name: '跟进次数(次)', + }, + yAxis: { + type: 'category', + name: '员工', + }, + }; + } + case 'followCustomerCountRank': { + return { + dataset: { + dimensions: ['nickname', 'count'], + source: cloneDeep(res).toReversed(), + }, + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '跟进客户数排行', + type: 'bar', + }, + ], + toolbox: { + feature: { + dataZoom: { + yAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '跟进客户数排行' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + xAxis: { + type: 'value', + name: '跟进客户数(个)', + }, + yAxis: { + type: 'category', + name: '员工', + }, + }; + } + case 'productSalesRank': { + return { + dataset: { + dimensions: ['nickname', 'count'], + source: cloneDeep(res).toReversed(), + }, + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '产品销量排行', + type: 'bar', + }, + ], + toolbox: { + feature: { + dataZoom: { + yAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '产品销量排行' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + xAxis: { + type: 'value', + name: '产品销量', + }, + yAxis: { + type: 'category', + name: '员工', + }, + }; + } + case 'receivablePriceRank': { + return { + dataset: { + dimensions: ['nickname', 'count'], + source: cloneDeep(res).toReversed(), + }, + grid: { + left: 20, + right: 20, + bottom: 20, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '回款金额排行', + type: 'bar', + }, + ], + toolbox: { + feature: { + dataZoom: { + yAxisIndex: false, // 数据区域缩放:Y 轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { show: true, name: '回款金额排行' }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'shadow', + }, + }, + xAxis: { + type: 'value', + name: '回款金额(元)', + }, + yAxis: { + type: 'category', + name: '签订人', + nameGap: 30, + }, + }; + } + default: { + return {}; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/rank/data.ts b/apps/web-ele/src/views/crm/statistics/rank/data.ts new file mode 100644 index 000000000..b60d90dce --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/rank/data.ts @@ -0,0 +1,277 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { useUserStore } from '@vben/stores'; +import { beginOfDay, endOfDay, formatDateTime, handleTree } from '@vben/utils'; + +import { getSimpleDeptList } from '#/api/system/dept'; +import { getRangePickerDefaultProps } from '#/utils'; + +const userStore = useUserStore(); + +export const customerSummaryTabs = [ + { + tab: '合同金额排行', + key: 'contractPriceRank', + }, + { + tab: '回款金额排行', + key: 'receivablePriceRank', + }, + { + tab: '签约合同排行', + key: 'contractCountRank', + }, + { + tab: '产品销量排行', + key: 'productSalesRank', + }, + { + tab: '新增客户数排行', + key: 'customerCountRank', + }, + { + tab: '新增联系人数排行', + key: 'contactCountRank', + }, + { + tab: '跟进次数排行', + key: 'followCountRank', + }, + { + tab: '跟进客户数排行', + key: 'followCustomerCountRank', + }, +]; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'times', + label: '时间范围', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + }, + defaultValue: [ + formatDateTime(beginOfDay(new Date(Date.now() - 3600 * 1000 * 24 * 7))), + formatDateTime(endOfDay(new Date(Date.now() - 3600 * 1000 * 24))), + ], + }, + { + fieldName: 'deptId', + label: '归属部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getSimpleDeptList(); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + treeDefaultExpandAll: true, + placeholder: '请选择归属部门', + }, + defaultValue: userStore.userInfo?.deptId, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + activeTabName: any, +): VxeTableGridOptions['columns'] { + switch (activeTabName) { + case 'contactCountRank': { + return [ + { + type: 'seq', + title: '公司排名', + }, + { + field: 'nickname', + title: '创建人', + minWidth: 200, + }, + { + field: 'deptName', + title: '部门', + minWidth: 200, + }, + { + field: 'count', + title: '新增联系人数(个)', + minWidth: 200, + }, + ]; + } + case 'contractCountRank': { + return [ + { + type: 'seq', + title: '公司排名', + }, + { + field: 'nickname', + title: '签订人', + minWidth: 200, + }, + { + field: 'deptName', + title: '部门', + minWidth: 200, + }, + { + field: 'count', + title: '签约合同数(个)', + minWidth: 200, + }, + ]; + } + case 'contractPriceRank': { + return [ + { + type: 'seq', + title: '公司排名', + }, + { + field: 'nickname', + title: '签订人', + minWidth: 200, + }, + { + field: 'deptName', + title: '部门', + minWidth: 200, + }, + { + field: 'count', + title: '合同金额(元)', + minWidth: 200, + formatter: 'formatAmount2', + }, + ]; + } + case 'customerCountRank': { + return [ + { + type: 'seq', + title: '公司排名', + }, + { + field: 'nickname', + title: '签订人', + minWidth: 200, + }, + { + field: 'deptName', + title: '部门', + minWidth: 200, + }, + { + field: 'count', + title: '新增客户数(个)', + minWidth: 200, + }, + ]; + } + case 'followCountRank': { + return [ + { + type: 'seq', + title: '公司排名', + }, + { + field: 'nickname', + title: '签订人', + minWidth: 200, + }, + { + field: 'deptName', + title: '部门', + minWidth: 200, + }, + { + field: 'count', + title: '跟进次数(次)', + minWidth: 200, + }, + ]; + } + case 'followCustomerCountRank': { + return [ + { + type: 'seq', + title: '公司排名', + }, + { + field: 'nickname', + title: '签订人', + minWidth: 200, + }, + { + field: 'deptName', + title: '部门', + minWidth: 200, + }, + { + field: 'count', + title: '跟进客户数(个)', + minWidth: 200, + }, + ]; + } + case 'productSalesRank': { + return [ + { + type: 'seq', + title: '公司排名', + }, + { + field: 'nickname', + title: '签订人', + minWidth: 200, + }, + { + field: 'deptName', + title: '部门', + minWidth: 200, + }, + { + field: 'count', + title: '产品销量', + minWidth: 200, + }, + ]; + } + case 'receivablePriceRank': { + return [ + { + type: 'seq', + title: '公司排名', + }, + { + field: 'nickname', + title: '签订人', + minWidth: 200, + }, + { + field: 'deptName', + title: '部门', + minWidth: 200, + }, + { + field: 'count', + title: '回款金额(元)', + minWidth: 200, + formatter: 'formatAmount2', + }, + ]; + } + default: { + return []; + } + } +} diff --git a/apps/web-ele/src/views/crm/statistics/rank/index.vue b/apps/web-ele/src/views/crm/statistics/rank/index.vue new file mode 100644 index 000000000..8253b2bc7 --- /dev/null +++ b/apps/web-ele/src/views/crm/statistics/rank/index.vue @@ -0,0 +1,107 @@ + + + diff --git a/apps/web-ele/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/web-ele/src/views/dashboard/analytics/analytics-visits-sales.vue new file mode 100644 index 000000000..6ff52086f --- /dev/null +++ b/apps/web-ele/src/views/dashboard/analytics/analytics-visits-sales.vue @@ -0,0 +1,46 @@ + + + diff --git a/apps/web-ele/src/views/dashboard/workspace/index.vue b/apps/web-ele/src/views/dashboard/workspace/index.vue new file mode 100644 index 000000000..4e15e7d6c --- /dev/null +++ b/apps/web-ele/src/views/dashboard/workspace/index.vue @@ -0,0 +1,260 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/account/data.ts b/apps/web-ele/src/views/erp/finance/account/data.ts new file mode 100644 index 000000000..497d4e310 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/account/data.ts @@ -0,0 +1,188 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { ErpAccountApi } from '#/api/erp/finance/account'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '名称', + rules: 'required', + componentProps: { + placeholder: '请输入名称', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + + { + fieldName: 'sort', + label: '排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入排序', + precision: 0, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + defaultValue: 0, + }, + { + fieldName: 'defaultStatus', + label: '是否默认', + component: 'RadioGroup', + componentProps: { + options: [ + { + label: '是', + value: true, + }, + { + label: '否', + value: false, + }, + ], + }, + rules: z.boolean().default(false).optional(), + }, + { + fieldName: 'no', + label: '编码', + component: 'Input', + componentProps: { + placeholder: '请输入编码', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名称', + component: 'Input', + componentProps: { + placeholder: '请输入名称', + allowClear: true, + }, + }, + { + fieldName: 'no', + label: '编码', + component: 'Input', + componentProps: { + placeholder: '请输入编码', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onDefaultStatusChange?: ( + newStatus: boolean, + row: ErpAccountApi.Account, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '名称', + minWidth: 150, + }, + { + field: 'no', + title: '编码', + minWidth: 120, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + showOverflow: 'tooltip', + }, + { + field: 'sort', + title: '排序', + minWidth: 80, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'defaultStatus', + title: '是否默认', + minWidth: 100, + cellRender: { + attrs: { beforeChange: onDefaultStatusChange }, + name: 'CellSwitch', + props: { + activeValue: true, + inactiveValue: false, + }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/finance/account/index.vue b/apps/web-ele/src/views/erp/finance/account/index.vue new file mode 100644 index 000000000..1f5493980 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/account/index.vue @@ -0,0 +1,175 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/account/modules/form.vue b/apps/web-ele/src/views/erp/finance/account/modules/form.vue new file mode 100644 index 000000000..eced899a4 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/account/modules/form.vue @@ -0,0 +1,86 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/payment/data.ts b/apps/web-ele/src/views/erp/finance/payment/data.ts new file mode 100644 index 000000000..116172128 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/payment/data.ts @@ -0,0 +1,592 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpPriceInputFormatter } from '@vben/utils'; + +import { getAccountSimpleList } from '#/api/erp/finance/account'; +import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '付款单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'paymentTime', + label: '付款时间', + component: 'DatePicker', + componentProps: { + disabled: formType === 'detail', + placeholder: '选择付款时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + disabled: formType === 'detail', + placeholder: '请选择供应商', + clearable: true, + filterable: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'financeUserId', + label: '财务人员', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择财务人员', + clearable: true, + filterable: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autosize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '采购入库、退货单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'accountId', + label: '付款账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择付款账户', + clearable: true, + filterable: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'totalPrice', + label: '合计付款', + component: 'InputNumber', + componentProps: { + placeholder: '合计付款', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'discountPrice', + label: '优惠金额', + component: 'InputNumber', + componentProps: { + disabled: formType === 'detail', + placeholder: '请输入优惠金额', + precision: 2, + formatter: erpPriceInputFormatter, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'paymentPrice', + label: '实际付款', + component: 'InputNumber', + componentProps: { + placeholder: '实际付款', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['totalPrice', 'discountPrice'], + componentProps: (values) => { + const totalPrice = values.totalPrice || 0; + const discountPrice = values.discountPrice || 0; + values.paymentPrice = totalPrice - discountPrice; + return {}; + }, + }, + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + disabled: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'bizNo', + title: '采购单据编号', + minWidth: 200, + }, + { + field: 'totalPrice', + title: '应付金额', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'paidPrice', + title: '已付金额', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'paymentPrice', + title: '本次付款', + minWidth: 115, + fixed: 'right', + slots: { default: 'paymentPrice' }, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '付款单号', + component: 'Input', + componentProps: { + placeholder: '请输入付款单号', + clearable: true, + }, + }, + { + fieldName: 'paymentTime', + label: '付款时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + clearable: true, + filterable: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + clearable: true, + filterable: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'financeUserId', + label: '财务人员', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择财务人员', + clearable: true, + filterable: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'accountId', + label: '付款账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择付款账户', + clearable: true, + filterable: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择状态', + clearable: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + clearable: true, + }, + }, + { + fieldName: 'bizNo', + label: '采购单号', + component: 'Input', + componentProps: { + placeholder: '请输入采购单号', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '付款单号', + width: 180, + fixed: 'left', + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'paymentTime', + title: '付款时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'financeUserName', + title: '财务人员', + minWidth: 120, + }, + { + field: 'accountName', + title: '付款账户', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '合计付款', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'discountPrice', + title: '优惠金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'paymentPrice', + title: '实际付款', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 采购入库单选择表单的配置项 */ +export function usePurchaseInGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '入库单号', + component: 'Input', + componentProps: { + placeholder: '请输入入库单号', + clearable: true, + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'Input', + componentProps: { + disabled: true, + placeholder: '已自动选择供应商', + }, + }, + { + fieldName: 'paymentStatus', + label: '付款状态', + component: 'Select', + componentProps: { + options: [ + { label: '未付款', value: 0 }, + { label: '部分付款', value: 1 }, + { label: '全部付款', value: 2 }, + ], + placeholder: '请选择付款状态', + clearable: true, + }, + }, + ]; +} + +/** 采购入库单选择列表的字段 */ +export function usePurchaseInGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '入库单号', + width: 200, + fixed: 'left', + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'inTime', + title: '入库时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'totalPrice', + title: '应付金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'paymentPrice', + title: '已付金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'unPaymentPrice', + title: '未付金额', + formatter: ({ row }) => { + return erpPriceInputFormatter(row.totalPrice - row.paymentPrice || 0); + }, + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + ]; +} + +/** 采购退货单选择表单的配置项 */ +export function useSaleReturnGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '退货单号', + component: 'Input', + componentProps: { + placeholder: '请输入退货单号', + clearable: true, + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'Input', + componentProps: { + disabled: true, + placeholder: '已自动选择供应商', + }, + }, + { + fieldName: 'refundStatus', + label: '退款状态', + component: 'Select', + componentProps: { + options: [ + { label: '未退款', value: 0 }, + { label: '部分退款', value: 1 }, + { label: '全部退款', value: 2 }, + ], + placeholder: '请选择退款状态', + clearable: true, + }, + }, + ]; +} + +/** 采购退货单选择列表的字段 */ +export function useSaleReturnGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '退货单号', + width: 200, + fixed: 'left', + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'returnTime', + title: '退货时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'totalPrice', + title: '应退金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'refundPrice', + title: '已退金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'unRefundPrice', + title: '未退金额', + formatter: ({ row }) => { + return erpPriceInputFormatter(row.totalPrice - row.refundPrice || 0); + }, + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/finance/payment/index.vue b/apps/web-ele/src/views/erp/finance/payment/index.vue new file mode 100644 index 000000000..5c6750bf2 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/payment/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/payment/modules/form.vue b/apps/web-ele/src/views/erp/finance/payment/modules/form.vue new file mode 100644 index 000000000..fe8796e12 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/payment/modules/form.vue @@ -0,0 +1,195 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/payment/modules/item-form.vue b/apps/web-ele/src/views/erp/finance/payment/modules/item-form.vue new file mode 100644 index 000000000..54fd08999 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/payment/modules/item-form.vue @@ -0,0 +1,302 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/payment/modules/purchase-in-select.vue b/apps/web-ele/src/views/erp/finance/payment/modules/purchase-in-select.vue new file mode 100644 index 000000000..8169157d7 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/payment/modules/purchase-in-select.vue @@ -0,0 +1,113 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/payment/modules/sale-return-select.vue b/apps/web-ele/src/views/erp/finance/payment/modules/sale-return-select.vue new file mode 100644 index 000000000..ec57fd5cc --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/payment/modules/sale-return-select.vue @@ -0,0 +1,117 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/receipt/data.ts b/apps/web-ele/src/views/erp/finance/receipt/data.ts new file mode 100644 index 000000000..19e463c38 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/receipt/data.ts @@ -0,0 +1,592 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpPriceInputFormatter } from '@vben/utils'; + +import { getAccountSimpleList } from '#/api/erp/finance/account'; +import { getCustomerSimpleList } from '#/api/erp/sale/customer'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '收款单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'receiptTime', + label: '收款时间', + component: 'DatePicker', + componentProps: { + disabled: formType === 'detail', + placeholder: '选择收款时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + disabled: formType === 'detail', + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'financeUserId', + label: '财务人员', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择财务人员', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '销售出库、退货单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'accountId', + label: '收款账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择收款账户', + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'totalPrice', + label: '合计收款', + component: 'InputNumber', + componentProps: { + placeholder: '合计收款', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'discountPrice', + label: '优惠金额', + component: 'InputNumber', + componentProps: { + disabled: formType === 'detail', + placeholder: '请输入优惠金额', + precision: 2, + formatter: erpPriceInputFormatter, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'receiptPrice', + label: '实际收款', + component: 'InputNumber', + componentProps: { + placeholder: '实际收款', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['totalPrice', 'discountPrice'], + componentProps: (values) => { + const totalPrice = values.totalPrice || 0; + const discountPrice = values.discountPrice || 0; + values.receiptPrice = totalPrice - discountPrice; + return {}; + }, + }, + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + disabled: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'bizNo', + title: '销售单据编号', + minWidth: 200, + }, + { + field: 'totalPrice', + title: '应收金额', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'receiptedPrice', + title: '已收金额', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'receiptPrice', + title: '本次收款', + minWidth: 115, + fixed: 'right', + slots: { default: 'receiptPrice' }, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '收款单号', + component: 'Input', + componentProps: { + placeholder: '请输入收款单号', + allowClear: true, + }, + }, + { + fieldName: 'receiptTime', + label: '收款时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'financeUserId', + label: '财务人员', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择财务人员', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'accountId', + label: '收款账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择收款账户', + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + { + fieldName: 'bizNo', + label: '销售单号', + component: 'Input', + componentProps: { + placeholder: '请输入销售单号', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '收款单号', + width: 180, + fixed: 'left', + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'receiptTime', + title: '收款时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'financeUserName', + title: '财务人员', + minWidth: 120, + }, + { + field: 'accountName', + title: '收款账户', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '合计收款', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'discountPrice', + title: '优惠金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'receiptPrice', + title: '实际收款', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 90, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 销售出库单选择表单的配置项 */ +export function useSaleOutGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '出库单号', + component: 'Input', + componentProps: { + placeholder: '请输入出库单号', + allowClear: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'Input', + componentProps: { + disabled: true, + placeholder: '已自动选择客户', + }, + }, + { + fieldName: 'receiptStatus', + label: '收款状态', + component: 'Select', + componentProps: { + options: [ + { label: '未收款', value: 0 }, + { label: '部分收款', value: 1 }, + { label: '全部收款', value: 2 }, + ], + placeholder: '请选择收款状态', + allowClear: true, + }, + }, + ]; +} + +/** 销售出库单选择列表的字段 */ +export function useSaleOutGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '出库单号', + width: 200, + fixed: 'left', + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'outTime', + title: '出库时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'totalPrice', + title: '应收金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'receiptPrice', + title: '已收金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'unReceiptPrice', + title: '未收金额', + formatter: ({ row }) => { + return erpPriceInputFormatter(row.totalPrice - row.receiptPrice || 0); + }, + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + ]; +} + +/** 销售退货单选择表单的配置项 */ +export function useSaleReturnGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '退货单号', + component: 'Input', + componentProps: { + placeholder: '请输入退货单号', + allowClear: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'Input', + componentProps: { + disabled: true, + placeholder: '已自动选择客户', + }, + }, + { + fieldName: 'refundStatus', + label: '退款状态', + component: 'Select', + componentProps: { + options: [ + { label: '未退款', value: 0 }, + { label: '部分退款', value: 1 }, + { label: '全部退款', value: 2 }, + ], + placeholder: '请选择退款状态', + allowClear: true, + }, + }, + ]; +} + +/** 销售退货单选择列表的字段 */ +export function useSaleReturnGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '退货单号', + width: 200, + fixed: 'left', + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'returnTime', + title: '退货时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'totalPrice', + title: '应退金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'refundPrice', + title: '已退金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'unRefundPrice', + title: '未退金额', + formatter: ({ row }) => { + return erpPriceInputFormatter(row.totalPrice - row.refundPrice || 0); + }, + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/finance/receipt/index.vue b/apps/web-ele/src/views/erp/finance/receipt/index.vue new file mode 100644 index 000000000..53a84eecf --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/receipt/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/receipt/modules/form.vue b/apps/web-ele/src/views/erp/finance/receipt/modules/form.vue new file mode 100644 index 000000000..8464cceb4 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/receipt/modules/form.vue @@ -0,0 +1,209 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/receipt/modules/item-form.vue b/apps/web-ele/src/views/erp/finance/receipt/modules/item-form.vue new file mode 100644 index 000000000..be746dbe4 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/receipt/modules/item-form.vue @@ -0,0 +1,299 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/receipt/modules/sale-out-select.vue b/apps/web-ele/src/views/erp/finance/receipt/modules/sale-out-select.vue new file mode 100644 index 000000000..b5d2def59 --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/receipt/modules/sale-out-select.vue @@ -0,0 +1,110 @@ + + + diff --git a/apps/web-ele/src/views/erp/finance/receipt/modules/sale-return-select.vue b/apps/web-ele/src/views/erp/finance/receipt/modules/sale-return-select.vue new file mode 100644 index 000000000..8bef7177d --- /dev/null +++ b/apps/web-ele/src/views/erp/finance/receipt/modules/sale-return-select.vue @@ -0,0 +1,114 @@ + + + diff --git a/apps/web-ele/src/views/erp/home/index.vue b/apps/web-ele/src/views/erp/home/index.vue new file mode 100644 index 000000000..9f6f2ea14 --- /dev/null +++ b/apps/web-ele/src/views/erp/home/index.vue @@ -0,0 +1,51 @@ + + + diff --git a/apps/web-ele/src/views/erp/home/modules/summary-card.vue b/apps/web-ele/src/views/erp/home/modules/summary-card.vue new file mode 100644 index 000000000..ff98e556a --- /dev/null +++ b/apps/web-ele/src/views/erp/home/modules/summary-card.vue @@ -0,0 +1,69 @@ + + + diff --git a/apps/web-ele/src/views/erp/home/modules/time-summary-chart.vue b/apps/web-ele/src/views/erp/home/modules/time-summary-chart.vue new file mode 100644 index 000000000..fe164eb29 --- /dev/null +++ b/apps/web-ele/src/views/erp/home/modules/time-summary-chart.vue @@ -0,0 +1,161 @@ + + + diff --git a/apps/web-ele/src/views/erp/product/category/data.ts b/apps/web-ele/src/views/erp/product/category/data.ts new file mode 100644 index 000000000..e6b13c900 --- /dev/null +++ b/apps/web-ele/src/views/erp/product/category/data.ts @@ -0,0 +1,149 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { ErpProductCategoryApi } from '#/api/erp/product/category'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { handleTree } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getProductCategoryList } from '#/api/erp/product/category'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级分类', + component: 'ApiTreeSelect', + componentProps: { + allowClear: true, + api: async () => { + const data = await getProductCategoryList(); + data.unshift({ + id: 0, + name: '顶级分类', + }); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级分类', + treeDefaultExpandAll: true, + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '分类名称', + component: 'Input', + componentProps: { + placeholder: '请输入分类名称', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '分类编码', + component: 'Input', + componentProps: { + placeholder: '请输入分类编码', + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 查询表单 */ +export function useQueryFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'name', + label: '分类名称', + componentProps: { + placeholder: '请输入分类名称', + allowClear: true, + }, + }, + { + component: 'Select', + fieldName: 'status', + label: '开启状态', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择开启状态', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '分类名称', + align: 'left', + treeNode: true, + }, + { + field: 'code', + title: '分类编码', + }, + { + field: 'sort', + title: '显示顺序', + }, + { + field: 'status', + title: '分类状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/product/category/index.vue b/apps/web-ele/src/views/erp/product/category/index.vue new file mode 100644 index 000000000..19c20d37b --- /dev/null +++ b/apps/web-ele/src/views/erp/product/category/index.vue @@ -0,0 +1,183 @@ + + + diff --git a/apps/web-ele/src/views/erp/product/category/modules/form.vue b/apps/web-ele/src/views/erp/product/category/modules/form.vue new file mode 100644 index 000000000..981feb5cd --- /dev/null +++ b/apps/web-ele/src/views/erp/product/category/modules/form.vue @@ -0,0 +1,91 @@ + + + diff --git a/apps/web-ele/src/views/erp/product/product/data.ts b/apps/web-ele/src/views/erp/product/product/data.ts new file mode 100644 index 000000000..8bfa9ebfe --- /dev/null +++ b/apps/web-ele/src/views/erp/product/product/data.ts @@ -0,0 +1,258 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { handleTree } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getProductCategorySimpleList } from '#/api/erp/product/category'; +import { getProductUnitSimpleList } from '#/api/erp/product/unit'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '名称', + rules: 'required', + componentProps: { + placeholder: '请输入名称', + }, + }, + { + fieldName: 'barCode', + label: '条码', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入条码', + }, + }, + { + fieldName: 'categoryId', + label: '分类', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getProductCategorySimpleList(); + return handleTree(data); + }, + + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择分类', + treeDefaultExpandAll: true, + }, + rules: 'required', + }, + { + fieldName: 'unitId', + label: '单位', + component: 'ApiSelect', + componentProps: { + api: getProductUnitSimpleList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择单位', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'standard', + label: '规格', + component: 'Input', + componentProps: { + placeholder: '请输入规格', + }, + }, + { + fieldName: 'expiryDay', + label: '保质期天数', + component: 'InputNumber', + componentProps: { + placeholder: '请输入保质期天数', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'weight', + label: '重量(kg)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入重量(kg)', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'purchasePrice', + label: '采购价格', + component: 'InputNumber', + componentProps: { + placeholder: '请输入采购价格,单位:元', + precision: 2, + min: 0, + step: 0.01, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'salePrice', + label: '销售价格', + component: 'InputNumber', + componentProps: { + placeholder: '请输入销售价格,单位:元', + precision: 2, + min: 0, + step: 0.01, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'minPrice', + label: '最低价格', + component: 'InputNumber', + componentProps: { + placeholder: '请输入最低价格,单位:元', + precision: 2, + min: 0, + step: 0.01, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名称', + component: 'Input', + componentProps: { + placeholder: '请输入名称', + allowClear: true, + }, + }, + { + fieldName: 'categoryId', + label: '分类', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getProductCategorySimpleList(); + return handleTree(data); + }, + + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择分类', + treeDefaultExpandAll: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'barCode', + title: '条码', + minWidth: 120, + }, + { + field: 'name', + title: '名称', + minWidth: 200, + }, + { + field: 'standard', + title: '规格', + minWidth: 100, + }, + { + field: 'categoryName', + title: '分类', + minWidth: 120, + }, + { + field: 'unitName', + title: '单位', + minWidth: 100, + }, + { + field: 'purchasePrice', + title: '采购价格', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'salePrice', + title: '销售价格', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'minPrice', + title: '最低价格', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/product/product/index.vue b/apps/web-ele/src/views/erp/product/product/index.vue new file mode 100644 index 000000000..f6bce71be --- /dev/null +++ b/apps/web-ele/src/views/erp/product/product/index.vue @@ -0,0 +1,150 @@ + + + diff --git a/apps/web-ele/src/views/erp/product/product/modules/form.vue b/apps/web-ele/src/views/erp/product/product/modules/form.vue new file mode 100644 index 000000000..baed5a71e --- /dev/null +++ b/apps/web-ele/src/views/erp/product/product/modules/form.vue @@ -0,0 +1,85 @@ + + + diff --git a/apps/web-ele/src/views/erp/product/unit/data.ts b/apps/web-ele/src/views/erp/product/unit/data.ts new file mode 100644 index 000000000..0914528aa --- /dev/null +++ b/apps/web-ele/src/views/erp/product/unit/data.ts @@ -0,0 +1,101 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '单位名称', + rules: 'required', + componentProps: { + placeholder: '请输入单位名称', + }, + }, + { + fieldName: 'status', + label: '单位状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '单位名称', + component: 'Input', + componentProps: { + placeholder: '请输入单位名称', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '单位状态', + component: 'Select', + componentProps: { + placeholder: '请选择单位状态', + allowClear: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '单位编号', + minWidth: 100, + }, + { + field: 'name', + title: '单位名称', + minWidth: 200, + }, + { + field: 'status', + title: '单位状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/product/unit/index.vue b/apps/web-ele/src/views/erp/product/unit/index.vue new file mode 100644 index 000000000..19972401c --- /dev/null +++ b/apps/web-ele/src/views/erp/product/unit/index.vue @@ -0,0 +1,149 @@ + + + diff --git a/apps/web-ele/src/views/erp/product/unit/modules/form.vue b/apps/web-ele/src/views/erp/product/unit/modules/form.vue new file mode 100644 index 000000000..70b3a9821 --- /dev/null +++ b/apps/web-ele/src/views/erp/product/unit/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/in/data.ts b/apps/web-ele/src/views/erp/purchase/in/data.ts new file mode 100644 index 000000000..4b4233c68 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/in/data.ts @@ -0,0 +1,624 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpNumberFormatter, erpPriceInputFormatter } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getAccountSimpleList } from '#/api/erp/finance/account'; +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '入库单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'inTime', + label: '入库时间', + component: 'DatePicker', + componentProps: { + disabled: formType === 'detail', + placeholder: '选择入库时间', + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'orderNo', + label: '关联订单', + component: 'Input', + formItemClass: 'col-span-1', + rules: 'required', + componentProps: { + placeholder: '请选择关联订单', + disabled: formType === 'detail', + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + disabled: true, + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '入库产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'discountPercent', + label: '优惠率(%)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入优惠率', + min: 0, + max: 100, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + { + fieldName: 'discountPrice', + label: '付款优惠', + component: 'InputNumber', + componentProps: { + placeholder: '付款优惠', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'discountedPrice', + label: '优惠后金额', + component: 'InputNumber', + componentProps: { + placeholder: '优惠后金额', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['totalPrice', 'otherPrice'], + componentProps: (values) => { + const totalPrice = values.totalPrice || 0; + const otherPrice = values.otherPrice || 0; + values.discountedPrice = totalPrice - otherPrice; + return {}; + }, + }, + }, + { + fieldName: 'otherPrice', + label: '其他费用', + component: 'InputNumber', + componentProps: { + disabled: formType === 'detail', + placeholder: '请输入其他费用', + precision: 2, + formatter: erpPriceInputFormatter, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'accountId', + label: '结算账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择结算账户', + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'totalPrice', + label: '应付金额', + component: 'InputNumber', + componentProps: { + precision: 2, + min: 0, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + formData?: any[], + disabled?: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'warehouseId', + title: '仓库名称', + minWidth: 200, + slots: { default: 'warehouseId' }, + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'totalCount', + title: '原数量', + formatter: 'formatAmount3', + minWidth: 120, + fixed: 'right', + visible: formData && formData[0]?.inCount !== undefined, + }, + { + field: 'inCount', + title: '已入库', + formatter: 'formatAmount3', + minWidth: 120, + fixed: 'right', + visible: formData && formData[0]?.returnCount !== undefined, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + fixed: 'right', + minWidth: 120, + slots: { default: 'productPrice' }, + }, + { + field: 'totalProductPrice', + fixed: 'right', + title: '产品金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + fixed: 'right', + field: 'taxPercent', + title: '税率(%)', + minWidth: 105, + slots: { default: 'taxPercent' }, + }, + { + fixed: 'right', + field: 'taxPrice', + title: '税额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'totalPrice', + fixed: 'right', + title: '合计金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '入库单号', + component: 'Input', + componentProps: { + placeholder: '请输入入库单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'inTime', + label: '入库时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'orderNo', + label: '关联订单', + component: 'Input', + componentProps: { + placeholder: '请输入关联订单号', + allowClear: true, + }, + }, + { + fieldName: 'accountId', + label: '结算账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择结算账户', + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'paymentStatus', + label: '付款状态', + component: 'Select', + componentProps: { + options: [ + { label: '未付款', value: 0 }, + { label: '部分付款', value: 1 }, + { label: '全部付款', value: 2 }, + ], + placeholder: '请选择付款状态', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '审批状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择审批状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '入库单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'inTime', + title: '入库时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '应付金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'paymentPrice', + title: '已付金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'unPaymentPrice', + title: '未付金额', + formatter: ({ row }) => { + return `${erpNumberFormatter(row.totalPrice - row.paymentPrice, 2)}元`; + }, + minWidth: 120, + }, + { + field: 'status', + title: '审批状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useOrderGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '订单单号', + component: 'Input', + componentProps: { + placeholder: '请输入订单单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'orderTime', + label: '订单时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useOrderGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'radio', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '订单单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'orderTime', + title: '订单时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'inCount', + title: '入库数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalProductPrice', + title: '金额合计', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '含税金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/purchase/in/index.vue b/apps/web-ele/src/views/erp/purchase/in/index.vue new file mode 100644 index 000000000..2b5c1cbd6 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/in/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/in/modules/form.vue b/apps/web-ele/src/views/erp/purchase/in/modules/form.vue new file mode 100644 index 000000000..21cfadfae --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/in/modules/form.vue @@ -0,0 +1,231 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/in/modules/item-form.vue b/apps/web-ele/src/views/erp/purchase/in/modules/item-form.vue new file mode 100644 index 000000000..6ec8a500c --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/in/modules/item-form.vue @@ -0,0 +1,310 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/in/modules/purchase-order-select.vue b/apps/web-ele/src/views/erp/purchase/in/modules/purchase-order-select.vue new file mode 100644 index 000000000..0f1815fe5 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/in/modules/purchase-order-select.vue @@ -0,0 +1,124 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/order/data.ts b/apps/web-ele/src/views/erp/purchase/order/data.ts new file mode 100644 index 000000000..489af22cc --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/order/data.ts @@ -0,0 +1,454 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpPriceInputFormatter } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getAccountSimpleList } from '#/api/erp/finance/account'; +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '订单单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'orderTime', + label: '订单时间', + component: 'DatePicker', + componentProps: { + placeholder: '选择订单时间', + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + label: '供应商', + fieldName: 'supplierId', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '采购产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'discountPercent', + label: '优惠率(%)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入优惠率', + min: 0, + max: 100, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + { + fieldName: 'discountPrice', + label: '付款优惠', + component: 'InputNumber', + componentProps: { + placeholder: '付款优惠', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'totalPrice', + label: '优惠后金额', + component: 'InputNumber', + componentProps: { + placeholder: '优惠后金额', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'accountId', + label: '结算账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择结算账户', + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + component: 'InputNumber', + componentProps: { + placeholder: '请输入支付订金', + precision: 2, + min: 0, + controlsPosition: 'right', + class: '!w-full', + }, + fieldName: 'depositPrice', + label: '支付订金', + rules: z.number().min(0).optional(), + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + disabled: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + minWidth: 120, + fixed: 'right', + slots: { default: 'productPrice' }, + }, + { + field: 'totalProductPrice', + title: '金额', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + field: 'taxPercent', + title: '税率(%)', + minWidth: 105, + fixed: 'right', + slots: { default: 'taxPercent' }, + }, + { + field: 'taxPrice', + title: '税额', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + field: 'totalPrice', + title: '税额合计', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '订单单号', + component: 'Input', + componentProps: { + placeholder: '请输入订单单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'orderTime', + label: '订单时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + { + fieldName: 'inStatus', + label: '入库状态', + component: 'Select', + componentProps: { + options: [ + { label: '未入库', value: 0 }, + { label: '部分入库', value: 1 }, + { label: '全部入库', value: 2 }, + ], + placeholder: '请选择入库状态', + allowClear: true, + }, + }, + { + fieldName: 'returnStatus', + label: '退货状态', + component: 'Select', + componentProps: { + options: [ + { label: '未退货', value: 0 }, + { label: '部分退货', value: 1 }, + { label: '全部退货', value: 2 }, + ], + placeholder: '请选择退货状态', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '订单单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'orderTime', + title: '订单时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'inCount', + title: '入库数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'returnCount', + title: '退货数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalProductPrice', + title: '金额合计', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '含税金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'depositPrice', + title: '支付订金', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/purchase/order/index.vue b/apps/web-ele/src/views/erp/purchase/order/index.vue new file mode 100644 index 000000000..024e3c2a5 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/order/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/order/modules/form.vue b/apps/web-ele/src/views/erp/purchase/order/modules/form.vue new file mode 100644 index 000000000..03e9129c3 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/order/modules/form.vue @@ -0,0 +1,172 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/order/modules/item-form.vue b/apps/web-ele/src/views/erp/purchase/order/modules/item-form.vue new file mode 100644 index 000000000..0d32f3a62 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/order/modules/item-form.vue @@ -0,0 +1,329 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/return/data.ts b/apps/web-ele/src/views/erp/purchase/return/data.ts new file mode 100644 index 000000000..6a2686946 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/return/data.ts @@ -0,0 +1,617 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpNumberFormatter, erpPriceInputFormatter } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getAccountSimpleList } from '#/api/erp/finance/account'; +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '退货单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'returnTime', + label: '退货时间', + component: 'DatePicker', + componentProps: { + disabled: formType === 'detail', + placeholder: '选择退货时间', + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'orderNo', + label: '关联订单', + component: 'Input', + formItemClass: 'col-span-1', + rules: 'required', + componentProps: { + placeholder: '请选择关联订单', + disabled: formType === 'detail', + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + disabled: true, + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '退货产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'discountPercent', + label: '优惠率(%)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入优惠率', + min: 0, + max: 100, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + { + fieldName: 'discountPrice', + label: '退款优惠', + component: 'InputNumber', + componentProps: { + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'discountedPrice', + label: '优惠后金额', + component: 'InputNumber', + componentProps: { + placeholder: '优惠后金额', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['totalPrice', 'otherPrice'], + componentProps: (values) => { + const totalPrice = values.totalPrice || 0; + const otherPrice = values.otherPrice || 0; + values.discountedPrice = totalPrice - otherPrice; + return {}; + }, + }, + }, + { + fieldName: 'otherPrice', + label: '其他费用', + component: 'InputNumber', + componentProps: { + disabled: formType === 'detail', + placeholder: '请输入其他费用', + precision: 2, + formatter: erpPriceInputFormatter, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'accountId', + label: '结算账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择结算账户', + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'totalPrice', + label: '应退金额', + component: 'InputNumber', + componentProps: { + precision: 2, + min: 0, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + formData?: any[], + disabled?: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'warehouseId', + title: '仓库名称', + minWidth: 200, + slots: { default: 'warehouseId' }, + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + formatter: 'formatAmount3', + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'inCount', + title: '已入库', + formatter: 'formatAmount3', + minWidth: 120, + fixed: 'right', + visible: formData && formData[0]?.inCount !== undefined, + }, + { + field: 'returnCount', + title: '已退货', + formatter: 'formatAmount3', + minWidth: 120, + fixed: 'right', + visible: formData && formData[0]?.returnCount !== undefined, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + fixed: 'right', + minWidth: 120, + slots: { default: 'productPrice' }, + }, + { + field: 'totalProductPrice', + fixed: 'right', + title: '产品金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + fixed: 'right', + field: 'taxPercent', + title: '税率(%)', + minWidth: 105, + slots: { default: 'taxPercent' }, + }, + { + fixed: 'right', + field: 'taxPrice', + title: '税额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'totalPrice', + fixed: 'right', + title: '合计金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '退货单号', + component: 'Input', + componentProps: { + placeholder: '请输入退货单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'returnTime', + label: '退货时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'orderNo', + label: '关联订单', + component: 'Input', + componentProps: { + placeholder: '请输入关联订单号', + allowClear: true, + }, + }, + { + fieldName: 'refundStatus', + label: '退款状态', + component: 'Select', + componentProps: { + options: [ + { label: '未退款', value: 0 }, + { label: '部分退款', value: 1 }, + { label: '全部退款', value: 2 }, + ], + placeholder: '请选择退款状态', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '审批状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择审批状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '退货单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '退货产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'returnTime', + title: '退货时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '应退金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'refundPrice', + title: '已退金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'unRefundPrice', + title: '未退金额', + formatter: ({ row }) => { + return `${erpNumberFormatter(row.totalPrice - row.refundPrice, 2)}元`; + }, + minWidth: 120, + }, + { + field: 'status', + title: '审批状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useOrderGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '订单单号', + component: 'Input', + componentProps: { + placeholder: '请输入订单单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'orderTime', + label: '订单时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useOrderGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'radio', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '订单单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'orderTime', + title: '订单时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'inCount', + title: '已入库数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'returnCount', + title: '已退货数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalProductPrice', + title: '金额合计', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '含税金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/purchase/return/index.vue b/apps/web-ele/src/views/erp/purchase/return/index.vue new file mode 100644 index 000000000..91c9cfbfc --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/return/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/return/modules/form.vue b/apps/web-ele/src/views/erp/purchase/return/modules/form.vue new file mode 100644 index 000000000..f496ae03e --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/return/modules/form.vue @@ -0,0 +1,231 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/return/modules/item-form.vue b/apps/web-ele/src/views/erp/purchase/return/modules/item-form.vue new file mode 100644 index 000000000..93e24edd9 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/return/modules/item-form.vue @@ -0,0 +1,312 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/return/modules/purchase-order-select.vue b/apps/web-ele/src/views/erp/purchase/return/modules/purchase-order-select.vue new file mode 100644 index 000000000..8b6ae1072 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/return/modules/purchase-order-select.vue @@ -0,0 +1,124 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/supplier/data.ts b/apps/web-ele/src/views/erp/purchase/supplier/data.ts new file mode 100644 index 000000000..d433c09d5 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/supplier/data.ts @@ -0,0 +1,234 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '供应商名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入供应商名称', + }, + }, + { + fieldName: 'contact', + label: '联系人', + component: 'Input', + componentProps: { + placeholder: '请输入联系人', + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + }, + }, + { + fieldName: 'telephone', + label: '联系电话', + component: 'Input', + componentProps: { + placeholder: '请输入联系电话', + }, + }, + { + fieldName: 'email', + label: '电子邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入电子邮箱', + }, + }, + { + fieldName: 'fax', + label: '传真', + component: 'Input', + componentProps: { + placeholder: '请输入传真', + }, + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'sort', + label: '排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入排序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'taxNo', + label: '纳税人识别号', + component: 'Input', + componentProps: { + placeholder: '请输入纳税人识别号', + }, + }, + { + fieldName: 'taxPercent', + label: '税率(%)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入税率', + min: 0, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'bankName', + label: '开户行', + component: 'Input', + componentProps: { + placeholder: '请输入开户行', + }, + }, + { + fieldName: 'bankAccount', + label: '开户账号', + component: 'Input', + componentProps: { + placeholder: '请输入开户账号', + }, + }, + { + fieldName: 'bankAddress', + label: '开户地址', + component: 'Input', + componentProps: { + placeholder: '请输入开户地址', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + formItemClass: 'col-span-2', + }, + ]; +} + +/** 搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '供应商名称', + component: 'Input', + componentProps: { + placeholder: '请输入供应商名称', + allowClear: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + allowClear: true, + }, + }, + { + fieldName: 'telephone', + label: '联系电话', + component: 'Input', + componentProps: { + placeholder: '请输入联系电话', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '供应商名称', + minWidth: 150, + }, + { + field: 'contact', + title: '联系人', + minWidth: 120, + }, + { + field: 'mobile', + title: '手机号码', + minWidth: 130, + }, + { + field: 'telephone', + title: '联系电话', + minWidth: 130, + }, + { + field: 'email', + title: '电子邮箱', + minWidth: 180, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'sort', + title: '排序', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + showOverflow: 'tooltip', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/purchase/supplier/index.vue b/apps/web-ele/src/views/erp/purchase/supplier/index.vue new file mode 100644 index 000000000..915adb7bb --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/supplier/index.vue @@ -0,0 +1,153 @@ + + + diff --git a/apps/web-ele/src/views/erp/purchase/supplier/modules/form.vue b/apps/web-ele/src/views/erp/purchase/supplier/modules/form.vue new file mode 100644 index 000000000..653dd5cb9 --- /dev/null +++ b/apps/web-ele/src/views/erp/purchase/supplier/modules/form.vue @@ -0,0 +1,90 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/customer/data.ts b/apps/web-ele/src/views/erp/sale/customer/data.ts new file mode 100644 index 000000000..adfdd5675 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/customer/data.ts @@ -0,0 +1,241 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '客户名称', + rules: 'required', + componentProps: { + placeholder: '请输入客户名称', + }, + }, + { + fieldName: 'contact', + label: '联系人', + component: 'Input', + componentProps: { + placeholder: '请输入联系人', + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + }, + }, + { + fieldName: 'telephone', + label: '联系电话', + component: 'Input', + componentProps: { + placeholder: '请输入联系电话', + }, + }, + { + fieldName: 'email', + label: '电子邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入电子邮箱', + }, + }, + { + fieldName: 'fax', + label: '传真', + component: 'Input', + componentProps: { + placeholder: '请输入传真', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'sort', + label: '排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入排序', + precision: 0, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'taxNo', + label: '纳税人识别号', + component: 'Input', + componentProps: { + placeholder: '请输入纳税人识别号', + }, + }, + { + fieldName: 'taxPercent', + label: '税率(%)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入税率', + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).max(100).optional(), + }, + { + fieldName: 'bankName', + label: '开户行名称', + component: 'Input', + componentProps: { + placeholder: '请输入开户行名称', + }, + }, + { + fieldName: 'bankAccount', + label: '开户行账号', + component: 'Input', + componentProps: { + placeholder: '请输入开户行账号', + }, + }, + { + fieldName: 'bankAddress', + label: '开户行地址', + component: 'Input', + componentProps: { + placeholder: '请输入开户行地址', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 3, + }, + formItemClass: 'col-span-2', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '客户名称', + component: 'Input', + componentProps: { + placeholder: '请输入客户名称', + allowClear: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + allowClear: true, + }, + }, + { + fieldName: 'email', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '客户名称', + minWidth: 150, + }, + { + field: 'contact', + title: '联系人', + minWidth: 120, + }, + { + field: 'mobile', + title: '手机号码', + minWidth: 130, + }, + { + field: 'telephone', + title: '联系电话', + minWidth: 130, + }, + { + field: 'email', + title: '电子邮箱', + minWidth: 180, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'sort', + title: '排序', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + showOverflow: 'tooltip', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/sale/customer/index.vue b/apps/web-ele/src/views/erp/sale/customer/index.vue new file mode 100644 index 000000000..c2e3bc90a --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/customer/index.vue @@ -0,0 +1,149 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/customer/modules/form.vue b/apps/web-ele/src/views/erp/sale/customer/modules/form.vue new file mode 100644 index 000000000..d7445569d --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/customer/modules/form.vue @@ -0,0 +1,86 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/order/data.ts b/apps/web-ele/src/views/erp/sale/order/data.ts new file mode 100644 index 000000000..b1537508c --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/order/data.ts @@ -0,0 +1,467 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpPriceInputFormatter } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getAccountSimpleList } from '#/api/erp/finance/account'; +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getCustomerSimpleList } from '#/api/erp/sale/customer'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '订单单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'orderTime', + label: '订单时间', + component: 'DatePicker', + componentProps: { + placeholder: '选择订单时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + label: '客户', + fieldName: 'customerId', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'saleUserId', + label: '销售人员', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择销售人员', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '销售产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'discountPercent', + label: '优惠率(%)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入优惠率', + min: 0, + max: 100, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + { + fieldName: 'discountPrice', + label: '付款优惠', + component: 'InputNumber', + componentProps: { + placeholder: '收款优惠', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'totalPrice', + label: '优惠后金额', + component: 'InputNumber', + componentProps: { + placeholder: '优惠后金额', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'accountId', + label: '结算账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择结算账户', + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + component: 'InputNumber', + componentProps: { + placeholder: '请输入收取订金', + precision: 2, + min: 0, + controlsPosition: 'right', + class: '!w-full', + }, + fieldName: 'depositPrice', + label: '收取订金', + rules: z.number().min(0).optional(), + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + disabled: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + formatter: 'formatAmount3', + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + minWidth: 120, + fixed: 'right', + slots: { default: 'productPrice' }, + }, + { + field: 'totalProductPrice', + title: '金额', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + field: 'taxPercent', + title: '税率(%)', + minWidth: 105, + fixed: 'right', + slots: { default: 'taxPercent' }, + }, + { + field: 'taxPrice', + title: '税额', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + field: 'totalPrice', + title: '税额合计', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '订单单号', + component: 'Input', + componentProps: { + placeholder: '请输入订单单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'orderTime', + label: '订单时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'supplierId', + label: '客户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + { + fieldName: 'outStatus', + label: '出库状态', + component: 'Select', + componentProps: { + options: [ + { label: '未出库', value: 0 }, + { label: '部分出库', value: 1 }, + { label: '全部出库', value: 2 }, + ], + placeholder: '请选择出库状态', + allowClear: true, + }, + }, + { + fieldName: 'returnStatus', + label: '退货状态', + component: 'Select', + componentProps: { + options: [ + { label: '未退货', value: 0 }, + { label: '部分退货', value: 1 }, + { label: '全部退货', value: 2 }, + ], + placeholder: '请选择退货状态', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '订单单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'orderTime', + title: '订单时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'outCount', + title: '出库数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'returnCount', + title: '退货数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalProductPrice', + title: '金额合计', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '含税金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'depositPrice', + title: '收取订金', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/sale/order/index.vue b/apps/web-ele/src/views/erp/sale/order/index.vue new file mode 100644 index 000000000..e681b8696 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/order/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/order/modules/form.vue b/apps/web-ele/src/views/erp/sale/order/modules/form.vue new file mode 100644 index 000000000..53dd591fe --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/order/modules/form.vue @@ -0,0 +1,162 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/order/modules/item-form.vue b/apps/web-ele/src/views/erp/sale/order/modules/item-form.vue new file mode 100644 index 000000000..0a0aef785 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/order/modules/item-form.vue @@ -0,0 +1,328 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/out/data.ts b/apps/web-ele/src/views/erp/sale/out/data.ts new file mode 100644 index 000000000..76f8e2288 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/out/data.ts @@ -0,0 +1,637 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpNumberFormatter, erpPriceInputFormatter } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getAccountSimpleList } from '#/api/erp/finance/account'; +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getCustomerSimpleList } from '#/api/erp/sale/customer'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '出库单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'outTime', + label: '出库时间', + component: 'DatePicker', + componentProps: { + disabled: formType === 'detail', + placeholder: '选择出库时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'orderNo', + label: '关联订单', + component: 'Input', + formItemClass: 'col-span-1', + rules: 'required', + componentProps: { + placeholder: '请选择关联订单', + disabled: formType === 'detail', + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + disabled: true, + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + fieldNames: { + label: 'name', + value: 'id', + }, + }, + rules: 'required', + }, + { + fieldName: 'saleUserId', + label: '销售人员', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择销售人员', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + fieldNames: { + label: 'nickname', + value: 'id', + }, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '出库产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'discountPercent', + label: '优惠率(%)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入优惠率', + min: 0, + max: 100, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + { + fieldName: 'discountPrice', + label: '收款优惠', + component: 'InputNumber', + componentProps: { + placeholder: '付款优惠', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'discountedPrice', + label: '优惠后金额', + component: 'InputNumber', + componentProps: { + placeholder: '优惠后金额', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['totalPrice', 'otherPrice'], + componentProps: (values) => { + const totalPrice = values.totalPrice || 0; + const otherPrice = values.otherPrice || 0; + values.discountedPrice = totalPrice - otherPrice; + return {}; + }, + }, + }, + { + fieldName: 'otherPrice', + label: '其他费用', + component: 'InputNumber', + componentProps: { + disabled: formType === 'detail', + placeholder: '请输入其他费用', + precision: 2, + formatter: erpPriceInputFormatter, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'accountId', + label: '结算账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择结算账户', + disabled: true, + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + fieldNames: { + label: 'name', + value: 'id', + }, + }, + }, + { + fieldName: 'totalPrice', + label: '应收金额', + component: 'InputNumber', + componentProps: { + precision: 2, + min: 0, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + formData?: any[], + disabled?: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'warehouseId', + title: '仓库名称', + minWidth: 200, + slots: { default: 'warehouseId' }, + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + formatter: 'formatAmount3', + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'totalCount', + title: '原数量', + formatter: 'formatAmount3', + minWidth: 120, + fixed: 'right', + visible: formData && formData[0]?.outCount !== undefined, + }, + { + field: 'outCount', + title: '已出库', + formatter: 'formatAmount3', + minWidth: 120, + fixed: 'right', + visible: formData && formData[0]?.returnCount !== undefined, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + fixed: 'right', + minWidth: 120, + slots: { default: 'productPrice' }, + }, + { + field: 'totalProductPrice', + fixed: 'right', + title: '产品金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + fixed: 'right', + field: 'taxPercent', + title: '税率(%)', + minWidth: 105, + slots: { default: 'taxPercent' }, + }, + { + fixed: 'right', + field: 'taxPrice', + title: '税额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'totalPrice', + fixed: 'right', + title: '合计金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '出库单号', + component: 'Input', + componentProps: { + placeholder: '请输入出库单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + fieldNames: { + label: 'name', + value: 'id', + }, + }, + }, + { + fieldName: 'outTime', + label: '出库时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + fieldNames: { + label: 'name', + value: 'id', + }, + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + fieldNames: { + label: 'nickname', + value: 'id', + }, + }, + }, + { + fieldName: 'orderNo', + label: '关联订单', + component: 'Input', + componentProps: { + placeholder: '请输入关联订单号', + allowClear: true, + }, + }, + { + fieldName: 'receiptStatus', + label: '收款状态', + component: 'Select', + componentProps: { + options: [ + { label: '未收款', value: 0 }, + { label: '部分收款', value: 1 }, + { label: '全部收款', value: 2 }, + ], + placeholder: '请选择收款状态', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '审批状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择审批状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '出库单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'outTime', + title: '出库时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '应收金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'receiptPrice', + title: '已收金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'unReceiptPrice', + title: '未收金额', + formatter: ({ row }) => { + return `${erpNumberFormatter(row.totalPrice - row.receiptPrice, 2)}元`; + }, + minWidth: 120, + }, + { + field: 'status', + title: '审批状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useOrderGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '订单单号', + component: 'Input', + componentProps: { + placeholder: '请输入订单单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'orderTime', + label: '订单时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useOrderGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'radio', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '订单单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'orderTime', + title: '订单时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'outCount', + title: '出库数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalProductPrice', + title: '金额合计', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '含税金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/sale/out/index.vue b/apps/web-ele/src/views/erp/sale/out/index.vue new file mode 100644 index 000000000..528e7f5b3 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/out/index.vue @@ -0,0 +1,223 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/out/modules/form.vue b/apps/web-ele/src/views/erp/sale/out/modules/form.vue new file mode 100644 index 000000000..123f2f38b --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/out/modules/form.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/out/modules/item-form.vue b/apps/web-ele/src/views/erp/sale/out/modules/item-form.vue new file mode 100644 index 000000000..3ec5996b3 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/out/modules/item-form.vue @@ -0,0 +1,310 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/out/modules/sale-order-select.vue b/apps/web-ele/src/views/erp/sale/out/modules/sale-order-select.vue new file mode 100644 index 000000000..de1912674 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/out/modules/sale-order-select.vue @@ -0,0 +1,124 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/return/data.ts b/apps/web-ele/src/views/erp/sale/return/data.ts new file mode 100644 index 000000000..9138f35a6 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/return/data.ts @@ -0,0 +1,624 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpNumberFormatter, erpPriceInputFormatter } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getAccountSimpleList } from '#/api/erp/finance/account'; +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getCustomerSimpleList } from '#/api/erp/sale/customer'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '退货单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'returnTime', + label: '退货时间', + component: 'DatePicker', + componentProps: { + disabled: formType === 'detail', + placeholder: '选择退货时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'orderNo', + label: '关联订单', + component: 'Input', + formItemClass: 'col-span-1', + rules: 'required', + componentProps: { + placeholder: '请选择关联订单', + disabled: formType === 'detail', + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + disabled: true, + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'saleUserId', + label: '销售人员', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择销售人员', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '退货产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'discountPercent', + label: '优惠率(%)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入优惠率', + min: 0, + max: 100, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + { + fieldName: 'discountPrice', + label: '退款优惠', + component: 'InputNumber', + componentProps: { + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'discountedPrice', + label: '优惠后金额', + component: 'InputNumber', + componentProps: { + placeholder: '优惠后金额', + precision: 2, + formatter: erpPriceInputFormatter, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['totalPrice', 'otherPrice'], + componentProps: (values) => { + const totalPrice = values.totalPrice || 0; + const otherPrice = values.otherPrice || 0; + values.discountedPrice = totalPrice - otherPrice; + return {}; + }, + }, + }, + { + fieldName: 'otherPrice', + label: '其他费用', + component: 'InputNumber', + componentProps: { + disabled: formType === 'detail', + placeholder: '请输入其他费用', + precision: 2, + formatter: erpPriceInputFormatter, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'accountId', + label: '结算账户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择结算账户', + disabled: true, + allowClear: true, + showSearch: true, + api: getAccountSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'totalPrice', + label: '应收金额', + component: 'InputNumber', + componentProps: { + precision: 2, + min: 0, + disabled: true, + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0).optional(), + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + formData?: any[], + disabled?: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'warehouseId', + title: '仓库名称', + minWidth: 200, + slots: { default: 'warehouseId' }, + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + formatter: 'formatAmount3', + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'totalCount', + title: '已出库', + formatter: 'formatAmount3', + minWidth: 120, + fixed: 'right', + visible: formData && formData[0]?.outCount !== undefined, + }, + { + field: 'returnCount', + title: '已退货', + formatter: 'formatAmount3', + minWidth: 120, + fixed: 'right', + visible: formData && formData[0]?.returnCount !== undefined, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + fixed: 'right', + minWidth: 120, + slots: { default: 'productPrice' }, + }, + { + field: 'totalProductPrice', + fixed: 'right', + title: '产品金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + fixed: 'right', + field: 'taxPercent', + title: '税率(%)', + minWidth: 105, + slots: { default: 'taxPercent' }, + }, + { + fixed: 'right', + field: 'taxPrice', + title: '税额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'totalPrice', + fixed: 'right', + title: '合计金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '退货单号', + component: 'Input', + componentProps: { + placeholder: '请输入退货单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'returnTime', + label: '退货时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'orderNo', + label: '关联订单', + component: 'Input', + componentProps: { + placeholder: '请输入关联订单号', + allowClear: true, + }, + }, + { + fieldName: 'refundStatus', + label: '退款状态', + component: 'Select', + componentProps: { + options: [ + { label: '未退款', value: 0 }, + { label: '部分退款', value: 1 }, + { label: '全部退款', value: 2 }, + ], + placeholder: '请选择退款状态', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '审批状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择审批状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '退货单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '退货产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'returnTime', + title: '退货时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '应收金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'refundPrice', + title: '已退金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'unRefundPrice', + title: '未退金额', + formatter: ({ row }) => { + return `${erpNumberFormatter(row.totalPrice - row.refundPrice, 2)}元`; + }, + minWidth: 120, + }, + { + field: 'status', + title: '审批状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useOrderGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '订单单号', + component: 'Input', + componentProps: { + placeholder: '请输入订单单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'orderTime', + label: '订单时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useOrderGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'radio', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '订单单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'orderTime', + title: '订单时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'returnCount', + title: '已退货数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalProductPrice', + title: '金额合计', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '含税金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/sale/return/index.vue b/apps/web-ele/src/views/erp/sale/return/index.vue new file mode 100644 index 000000000..5a76a96b3 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/return/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/return/modules/form.vue b/apps/web-ele/src/views/erp/sale/return/modules/form.vue new file mode 100644 index 000000000..35f59a95d --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/return/modules/form.vue @@ -0,0 +1,231 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/return/modules/item-form.vue b/apps/web-ele/src/views/erp/sale/return/modules/item-form.vue new file mode 100644 index 000000000..3e169a983 --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/return/modules/item-form.vue @@ -0,0 +1,310 @@ + + + diff --git a/apps/web-ele/src/views/erp/sale/return/modules/sale-order-select.vue b/apps/web-ele/src/views/erp/sale/return/modules/sale-order-select.vue new file mode 100644 index 000000000..05d4746cd --- /dev/null +++ b/apps/web-ele/src/views/erp/sale/return/modules/sale-order-select.vue @@ -0,0 +1,124 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/check/data.ts b/apps/web-ele/src/views/erp/stock/check/data.ts new file mode 100644 index 000000000..c4beb2c5c --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/check/data.ts @@ -0,0 +1,307 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '盘点单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'checkTime', + label: '盘点时间', + component: 'DatePicker', + componentProps: { + placeholder: '选择盘点时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + disabled: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'warehouseId', + title: '仓库名称', + minWidth: 150, + slots: { default: 'warehouseId' }, + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '账面库存', + minWidth: 80, + formatter: 'formatAmount3', + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'actualCount', + title: '实际库存', + minWidth: 120, + fixed: 'right', + slots: { default: 'actualCount' }, + formatter: 'formatAmount3', + }, + { + field: 'count', + title: '盈亏数量', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount3', + }, + { + field: 'productPrice', + title: '产品单价', + minWidth: 120, + fixed: 'right', + slots: { default: 'productPrice' }, + }, + { + field: 'totalPrice', + title: '金额', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '盘点单号', + component: 'Input', + componentProps: { + placeholder: '请输入盘点单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'checkTime', + label: '盘点时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '盘点单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'checkTime', + title: '盘点时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '总金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/stock/check/index.vue b/apps/web-ele/src/views/erp/stock/check/index.vue new file mode 100644 index 000000000..144ab7619 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/check/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/check/modules/form.vue b/apps/web-ele/src/views/erp/stock/check/modules/form.vue new file mode 100644 index 000000000..72e504a73 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/check/modules/form.vue @@ -0,0 +1,132 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/check/modules/item-form.vue b/apps/web-ele/src/views/erp/stock/check/modules/item-form.vue new file mode 100644 index 000000000..545c1a6f3 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/check/modules/item-form.vue @@ -0,0 +1,312 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/in/data.ts b/apps/web-ele/src/views/erp/stock/in/data.ts new file mode 100644 index 000000000..9b59ce9cc --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/in/data.ts @@ -0,0 +1,332 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getSupplierSimpleList } from '#/api/erp/purchase/supplier'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '入库单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'inTime', + label: '入库时间', + component: 'DatePicker', + componentProps: { + placeholder: '选择入库时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + label: '供应商', + fieldName: 'supplierId', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '入库产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + disabled: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'warehouseId', + title: '仓库名称', + minWidth: 150, + slots: { default: 'warehouseId' }, + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + formatter: 'formatAmount3', + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + minWidth: 120, + fixed: 'right', + slots: { default: 'productPrice' }, + }, + { + field: 'totalPrice', + title: '金额', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '入库单号', + component: 'Input', + componentProps: { + placeholder: '请输入入库单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'inTime', + label: '入库时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'supplierId', + label: '供应商', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择供应商', + allowClear: true, + showSearch: true, + api: getSupplierSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '入库单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'supplierName', + title: '供应商', + minWidth: 120, + }, + { + field: 'inTime', + title: '入库时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '总金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/stock/in/index.vue b/apps/web-ele/src/views/erp/stock/in/index.vue new file mode 100644 index 000000000..4f6e1da26 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/in/index.vue @@ -0,0 +1,223 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/in/modules/form.vue b/apps/web-ele/src/views/erp/stock/in/modules/form.vue new file mode 100644 index 000000000..8d1e42091 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/in/modules/form.vue @@ -0,0 +1,128 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/in/modules/item-form.vue b/apps/web-ele/src/views/erp/stock/in/modules/item-form.vue new file mode 100644 index 000000000..2fa34310e --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/in/modules/item-form.vue @@ -0,0 +1,301 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/move/data.ts b/apps/web-ele/src/views/erp/stock/move/data.ts new file mode 100644 index 000000000..0b4ffea43 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/move/data.ts @@ -0,0 +1,318 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '调度单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'moveTime', + label: '调度时间', + component: 'DatePicker', + componentProps: { + placeholder: '选择调度时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + disabled: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'fromWarehouseId', + title: '调出仓库', + minWidth: 150, + slots: { default: 'fromWarehouseId' }, + }, + { + field: 'toWarehouseId', + title: '调入仓库', + minWidth: 150, + slots: { default: 'toWarehouseId' }, + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + formatter: 'formatAmount3', + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + minWidth: 120, + fixed: 'right', + slots: { default: 'productPrice' }, + }, + { + field: 'totalPrice', + title: '金额', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '调度单号', + component: 'Input', + componentProps: { + placeholder: '请输入调度单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'moveTime', + label: '调度时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'fromWarehouseId', + label: '调出仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择调出仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'toWarehouseId', + label: '调入仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择调入仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '调度单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'moveTime', + title: '调度时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '总金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/stock/move/index.vue b/apps/web-ele/src/views/erp/stock/move/index.vue new file mode 100644 index 000000000..c0a64812b --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/move/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/move/modules/form.vue b/apps/web-ele/src/views/erp/stock/move/modules/form.vue new file mode 100644 index 000000000..94c879b99 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/move/modules/form.vue @@ -0,0 +1,132 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/move/modules/item-form.vue b/apps/web-ele/src/views/erp/stock/move/modules/item-form.vue new file mode 100644 index 000000000..8b3764c62 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/move/modules/item-form.vue @@ -0,0 +1,321 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/out/data.ts b/apps/web-ele/src/views/erp/stock/out/data.ts new file mode 100644 index 000000000..eb11d6849 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/out/data.ts @@ -0,0 +1,332 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getCustomerSimpleList } from '#/api/erp/sale/customer'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单的配置项 */ +export function useFormSchema(formType: string): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'no', + label: '出库单号', + component: 'Input', + componentProps: { + placeholder: '系统自动生成', + disabled: true, + }, + }, + { + fieldName: 'outTime', + label: '出库时间', + component: 'DatePicker', + componentProps: { + placeholder: '选择出库时间', + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + rules: 'required', + }, + { + label: '客户', + fieldName: 'customerId', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + autoSize: { minRows: 1, maxRows: 1 }, + disabled: formType === 'detail', + }, + formItemClass: 'col-span-2', + }, + { + fieldName: 'fileUrl', + label: '附件', + component: 'FileUpload', + componentProps: { + maxNumber: 1, + maxSize: 10, + accept: [ + 'pdf', + 'doc', + 'docx', + 'xls', + 'xlsx', + 'txt', + 'jpg', + 'jpeg', + 'png', + ], + showDescription: formType !== 'detail', + disabled: formType === 'detail', + }, + formItemClass: 'col-span-3', + }, + { + fieldName: 'items', + label: '出库产品清单', + component: 'Input', + formItemClass: 'col-span-3', + }, + ]; +} + +/** 表单的明细表格列 */ +export function useFormItemColumns( + disabled: boolean, +): VxeTableGridOptions['columns'] { + return [ + { type: 'seq', title: '序号', minWidth: 50, fixed: 'left' }, + { + field: 'warehouseId', + title: '仓库名称', + minWidth: 150, + slots: { default: 'warehouseId' }, + }, + { + field: 'productId', + title: '产品名称', + minWidth: 200, + slots: { default: 'productId' }, + }, + { + field: 'stockCount', + title: '库存', + minWidth: 80, + formatter: 'formatAmount3', + }, + { + field: 'productBarCode', + title: '条码', + minWidth: 120, + }, + { + field: 'productUnitName', + title: '单位', + minWidth: 80, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + slots: { default: 'remark' }, + }, + { + field: 'count', + title: '数量', + minWidth: 120, + fixed: 'right', + slots: { default: 'count' }, + }, + { + field: 'productPrice', + title: '产品单价', + minWidth: 120, + fixed: 'right', + slots: { default: 'productPrice' }, + }, + { + field: 'totalPrice', + title: '金额', + minWidth: 120, + fixed: 'right', + formatter: 'formatAmount2', + }, + { + title: '操作', + width: 50, + fixed: 'right', + slots: { default: 'actions' }, + visible: !disabled, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '出库单号', + component: 'Input', + componentProps: { + placeholder: '请输入出库单号', + allowClear: true, + }, + }, + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'outTime', + label: '出库时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + { + fieldName: 'customerId', + label: '客户', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择客户', + allowClear: true, + showSearch: true, + api: getCustomerSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'creator', + label: '创建人', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择创建人', + allowClear: true, + showSearch: true, + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.ERP_AUDIT_STATUS, 'number'), + placeholder: '请选择状态', + allowClear: true, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + fixed: 'left', + }, + { + field: 'no', + title: '出库单号', + width: 200, + fixed: 'left', + }, + { + field: 'productNames', + title: '产品信息', + showOverflow: 'tooltip', + minWidth: 120, + }, + { + field: 'customerName', + title: '客户', + minWidth: 120, + }, + { + field: 'outTime', + title: '出库时间', + width: 160, + formatter: 'formatDate', + }, + { + field: 'creatorName', + title: '创建人', + minWidth: 120, + }, + { + field: 'totalCount', + title: '总数量', + formatter: 'formatAmount3', + minWidth: 120, + }, + { + field: 'totalPrice', + title: '总金额', + formatter: 'formatAmount2', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_AUDIT_STATUS }, + }, + }, + { + title: '操作', + width: 260, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/stock/out/index.vue b/apps/web-ele/src/views/erp/stock/out/index.vue new file mode 100644 index 000000000..96c244e09 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/out/index.vue @@ -0,0 +1,226 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/out/modules/form.vue b/apps/web-ele/src/views/erp/stock/out/modules/form.vue new file mode 100644 index 000000000..4d84dd331 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/out/modules/form.vue @@ -0,0 +1,132 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/out/modules/item-form.vue b/apps/web-ele/src/views/erp/stock/out/modules/item-form.vue new file mode 100644 index 000000000..5aa4f9afe --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/out/modules/item-form.vue @@ -0,0 +1,299 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/record/data.ts b/apps/web-ele/src/views/erp/stock/record/data.ts new file mode 100644 index 000000000..e4e3d0ecd --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/record/data.ts @@ -0,0 +1,133 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'bizType', + label: '类型', + component: 'Select', + componentProps: { + placeholder: '请选择类型', + allowClear: true, + options: getDictOptions(DICT_TYPE.ERP_STOCK_RECORD_BIZ_TYPE, 'number'), + }, + }, + { + fieldName: 'bizNo', + label: '业务单号', + component: 'Input', + componentProps: { + placeholder: '请输入业务单号', + allowClear: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'productName', + title: '产品名称', + minWidth: 150, + }, + { + field: 'categoryName', + title: '产品分类', + width: 120, + }, + { + field: 'unitName', + title: '产品单位', + width: 100, + }, + { + field: 'warehouseName', + title: '仓库', + width: 120, + }, + { + field: 'bizType', + title: '类型', + width: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.ERP_STOCK_RECORD_BIZ_TYPE }, + }, + }, + { + field: 'bizNo', + title: '出入库单号', + width: 200, + showOverflow: 'tooltip', + }, + { + field: 'createTime', + title: '出入库日期', + width: 180, + formatter: 'formatDateTime', + }, + { + field: 'count', + title: '出入库数量', + width: 120, + formatter: 'formatAmount3', + }, + { + field: 'totalCount', + title: '库存量', + width: 100, + formatter: 'formatAmount3', + }, + { + field: 'creatorName', + title: '操作人', + width: 100, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/stock/record/index.vue b/apps/web-ele/src/views/erp/stock/record/index.vue new file mode 100644 index 000000000..8c975031a --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/record/index.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/stock/data.ts b/apps/web-ele/src/views/erp/stock/stock/data.ts new file mode 100644 index 000000000..037bb9c45 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/stock/data.ts @@ -0,0 +1,69 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { getProductSimpleList } from '#/api/erp/product/product'; +import { getWarehouseSimpleList } from '#/api/erp/stock/warehouse'; + +/** 搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'productId', + label: '产品', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择产品', + allowClear: true, + showSearch: true, + api: getProductSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + { + fieldName: 'warehouseId', + label: '仓库', + component: 'ApiSelect', + componentProps: { + placeholder: '请选择仓库', + allowClear: true, + showSearch: true, + api: getWarehouseSimpleList, + labelField: 'name', + valueField: 'id', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'productName', + title: '产品名称', + minWidth: 150, + }, + { + field: 'unitName', + title: '产品单位', + minWidth: 100, + }, + { + field: 'categoryName', + title: '产品分类', + minWidth: 120, + }, + { + field: 'count', + title: '库存量', + minWidth: 100, + formatter: 'formatAmount3', + }, + { + field: 'warehouseName', + title: '仓库', + minWidth: 120, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/stock/stock/index.vue b/apps/web-ele/src/views/erp/stock/stock/index.vue new file mode 100644 index 000000000..2b20d7475 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/stock/index.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/warehouse/data.ts b/apps/web-ele/src/views/erp/stock/warehouse/data.ts new file mode 100644 index 000000000..0f38019e0 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/warehouse/data.ts @@ -0,0 +1,209 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { ErpWarehouseApi } from '#/api/erp/stock/warehouse'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '仓库名称', + component: 'Input', + componentProps: { + placeholder: '请输入仓库名称', + }, + rules: 'required', + }, + { + fieldName: 'address', + label: '仓库地址', + component: 'Input', + componentProps: { + placeholder: '请输入仓库地址', + }, + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'warehousePrice', + label: '仓储费(元)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入仓储费,单位:元/天/KG', + min: 0, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'truckagePrice', + label: '搬运费(元)', + component: 'InputNumber', + componentProps: { + placeholder: '请输入搬运费,单位:元', + min: 0, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'principal', + label: '负责人', + component: 'Input', + componentProps: { + placeholder: '请输入负责人', + }, + }, + { + fieldName: 'sort', + label: '排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入排序', + precision: 0, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '仓库名称', + component: 'Input', + componentProps: { + placeholder: '请输入仓库名称', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '仓库状态', + component: 'Select', + componentProps: { + placeholder: '请选择仓库状态', + allowClear: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onDefaultStatusChange?: ( + newStatus: boolean, + row: ErpWarehouseApi.Warehouse, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '仓库名称', + minWidth: 150, + }, + { + field: 'address', + title: '仓库地址', + minWidth: 200, + showOverflow: 'tooltip', + }, + { + field: 'warehousePrice', + title: '仓储费', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'truckagePrice', + title: '搬运费', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'principal', + title: '负责人', + minWidth: 100, + }, + { + field: 'sort', + title: '排序', + minWidth: 80, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'defaultStatus', + title: '是否默认', + minWidth: 100, + cellRender: { + attrs: { beforeChange: onDefaultStatusChange }, + name: 'CellSwitch', + props: { + activeValue: true, + inactiveValue: false, + }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + showOverflow: 'tooltip', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/erp/stock/warehouse/index.vue b/apps/web-ele/src/views/erp/stock/warehouse/index.vue new file mode 100644 index 000000000..48669e1f8 --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/warehouse/index.vue @@ -0,0 +1,179 @@ + + + diff --git a/apps/web-ele/src/views/erp/stock/warehouse/modules/form.vue b/apps/web-ele/src/views/erp/stock/warehouse/modules/form.vue new file mode 100644 index 000000000..5bd04fded --- /dev/null +++ b/apps/web-ele/src/views/erp/stock/warehouse/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/infra/apiAccessLog/data.ts b/apps/web-ele/src/views/infra/apiAccessLog/data.ts new file mode 100644 index 000000000..fb59f59b6 --- /dev/null +++ b/apps/web-ele/src/views/infra/apiAccessLog/data.ts @@ -0,0 +1,273 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { JsonViewer } from '@vben/common-ui'; +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + clearable: true, + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'applicationName', + label: '应用名', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入应用名', + }, + }, + { + fieldName: 'beginTime', + label: '请求时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'duration', + label: '执行时长', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入执行时长', + }, + }, + { + fieldName: 'resultCode', + label: '结果码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入结果码', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'applicationName', + title: '应用名', + minWidth: 150, + }, + { + field: 'requestMethod', + title: '请求方法', + minWidth: 80, + }, + { + field: 'requestUrl', + title: '请求地址', + minWidth: 300, + }, + { + field: 'beginTime', + title: '请求时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'duration', + title: '执行时长', + minWidth: 120, + formatter: ({ row }) => `${row.duration} ms`, + }, + { + field: 'resultCode', + title: '操作结果', + minWidth: 150, + formatter: ({ row }) => { + return row.resultCode === 0 ? '成功' : `失败(${row.resultMsg})`; + }, + }, + { + field: 'operateModule', + title: '操作模块', + minWidth: 150, + }, + { + field: 'operateName', + title: '操作名', + minWidth: 220, + }, + { + field: 'operateType', + title: '操作类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_OPERATE_TYPE }, + }, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'traceId', + label: '链路追踪', + }, + { + field: 'applicationName', + label: '应用名', + }, + { + field: 'userId', + label: '用户Id', + }, + { + field: 'userType', + label: '用户类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: val, + }); + }, + }, + { + field: 'userIp', + label: '用户 IP', + }, + { + field: 'userAgent', + label: '用户 UA', + }, + { + field: 'requestMethod', + label: '请求信息', + render: (val, data) => { + if (val && data?.requestUrl) { + return `${val} ${data.requestUrl}`; + } + return ''; + }, + }, + { + field: 'requestParams', + label: '请求参数', + render: (val) => { + if (val) { + return h(JsonViewer, { + value: JSON.parse(val), + previewMode: true, + }); + } + return ''; + }, + }, + { + field: 'responseBody', + label: '请求结果', + }, + { + label: '请求时间', + field: 'beginTime', + render: (val, data) => { + if (val && data?.endTime) { + return `${formatDateTime(val)} ~ ${formatDateTime(data.endTime)}`; + } + return ''; + }, + }, + { + label: '请求耗时', + field: 'duration', + render: (val) => { + return val ? `${val} ms` : ''; + }, + }, + { + label: '操作结果', + field: 'resultCode', + render: (val, data) => { + if (val === 0) { + return '正常'; + } else if (val > 0 && data?.resultMsg) { + return `失败 | ${val} | ${data.resultMsg}`; + } + return ''; + }, + }, + { + field: 'operateModule', + label: '操作模块', + }, + { + field: 'operateName', + label: '操作名', + }, + { + field: 'operateType', + label: '操作类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_OPERATE_TYPE, + value: val, + }); + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/apiAccessLog/index.vue b/apps/web-ele/src/views/infra/apiAccessLog/index.vue new file mode 100644 index 000000000..6ba4dad99 --- /dev/null +++ b/apps/web-ele/src/views/infra/apiAccessLog/index.vue @@ -0,0 +1,107 @@ + + + diff --git a/apps/web-ele/src/views/infra/apiAccessLog/modules/detail.vue b/apps/web-ele/src/views/infra/apiAccessLog/modules/detail.vue new file mode 100644 index 000000000..0b3c4c7fa --- /dev/null +++ b/apps/web-ele/src/views/infra/apiAccessLog/modules/detail.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/infra/apiErrorLog/data.ts b/apps/web-ele/src/views/infra/apiErrorLog/data.ts new file mode 100644 index 000000000..e669a77f1 --- /dev/null +++ b/apps/web-ele/src/views/infra/apiErrorLog/data.ts @@ -0,0 +1,249 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { JsonViewer } from '@vben/common-ui'; +import { DICT_TYPE, InfraApiErrorLogProcessStatusEnum } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + clearable: true, + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'applicationName', + label: '应用名', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入应用名', + }, + }, + { + fieldName: 'exceptionTime', + label: '异常时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'processStatus', + label: '处理状态', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS, + 'number', + ), + clearable: true, + placeholder: '请选择处理状态', + }, + defaultValue: InfraApiErrorLogProcessStatusEnum.INIT, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'applicationName', + title: '应用名', + minWidth: 150, + }, + { + field: 'requestMethod', + title: '请求方法', + minWidth: 80, + }, + { + field: 'requestUrl', + title: '请求地址', + minWidth: 200, + }, + { + field: 'exceptionTime', + title: '异常发生时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'exceptionName', + title: '异常名', + minWidth: 180, + }, + { + field: 'processStatus', + title: '处理状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS }, + }, + }, + { + title: '操作', + minWidth: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'traceId', + label: '链路追踪', + }, + { + field: 'applicationName', + label: '应用名', + }, + { + field: 'userId', + label: '用户Id', + }, + { + field: 'userType', + label: '用户类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: val, + }); + }, + }, + { + field: 'userIp', + label: '用户 IP', + }, + { + field: 'userAgent', + label: '用户 UA', + }, + { + field: 'requestMethod', + label: '请求信息', + render: (val, data) => { + if (val && data?.requestUrl) { + return `${val} ${data.requestUrl}`; + } + return ''; + }, + }, + { + field: 'requestParams', + label: '请求参数', + render: (val) => { + if (val) { + return h(JsonViewer, { + value: JSON.parse(val), + previewMode: true, + }); + } + return ''; + }, + }, + { + field: 'exceptionTime', + label: '异常时间', + render: (val) => { + return formatDateTime(val) as string; + }, + }, + { + field: 'exceptionName', + label: '异常名', + }, + { + field: 'exceptionStackTrace', + label: '异常堆栈', + show: (val) => !val, + render: (val) => { + if (val) { + return h('textarea', { + value: val, + style: + 'width: 100%; min-height: 200px; max-height: 400px; resize: vertical;', + readonly: true, + }); + } + return ''; + }, + }, + { + field: 'processStatus', + label: '处理状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS, + value: val, + }); + }, + }, + { + field: 'processUserId', + label: '处理人', + show: (val) => !val, + }, + { + field: 'processTime', + label: '处理时间', + show: (val) => !val, + render: (val) => { + return formatDateTime(val) as string; + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/apiErrorLog/index.vue b/apps/web-ele/src/views/infra/apiErrorLog/index.vue new file mode 100644 index 000000000..964217c5d --- /dev/null +++ b/apps/web-ele/src/views/infra/apiErrorLog/index.vue @@ -0,0 +1,156 @@ + + + diff --git a/apps/web-ele/src/views/infra/apiErrorLog/modules/detail.vue b/apps/web-ele/src/views/infra/apiErrorLog/modules/detail.vue new file mode 100644 index 000000000..705cfdcd0 --- /dev/null +++ b/apps/web-ele/src/views/infra/apiErrorLog/modules/detail.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/infra/build/index.vue b/apps/web-ele/src/views/infra/build/index.vue new file mode 100644 index 000000000..53ec087b9 --- /dev/null +++ b/apps/web-ele/src/views/infra/build/index.vue @@ -0,0 +1,181 @@ + + + + diff --git a/apps/web-ele/src/views/infra/codegen/data.ts b/apps/web-ele/src/views/infra/codegen/data.ts new file mode 100644 index 000000000..a02a6e191 --- /dev/null +++ b/apps/web-ele/src/views/infra/codegen/data.ts @@ -0,0 +1,552 @@ +import type { Recordable } from '@vben/types'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { InfraCodegenApi } from '#/api/infra/codegen'; +import type { SystemMenuApi } from '#/api/system/menu'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { IconifyIcon } from '@vben/icons'; +import { handleTree } from '@vben/utils'; + +import { getDataSourceConfigList } from '#/api/infra/data-source-config'; +import { getMenuList } from '#/api/system/menu'; +import { $t } from '#/locales'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 导入数据库表的表单 */ +export function useImportTableFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'dataSourceConfigId', + label: '数据源', + component: 'ApiSelect', + componentProps: { + api: getDataSourceConfigList, + labelField: 'name', + valueField: 'id', + autoSelect: 'first', + placeholder: '请选择数据源', + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '表名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入表名称', + }, + }, + { + fieldName: 'comment', + label: '表描述', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入表描述', + }, + }, + ]; +} + +/** 导入数据库表表格列定义 */ +export function useImportTableColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { field: 'name', title: '表名称', minWidth: 200 }, + { field: 'comment', title: '表描述', minWidth: 200 }, + ]; +} + +/** 基本信息表单的 schema */ +export function useBasicInfoFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'tableName', + label: '表名称', + component: 'Input', + componentProps: { + placeholder: '请输入仓库名称', + }, + rules: 'required', + }, + { + fieldName: 'tableComment', + label: '表描述', + component: 'Input', + componentProps: { + placeholder: '请输入表描述', + }, + rules: 'required', + }, + { + fieldName: 'className', + label: '实体类名称', + component: 'Input', + componentProps: { + placeholder: '请输入实体类名称', + }, + rules: 'required', + help: '默认去除表名的前缀。如果存在重复,则需要手动添加前缀,避免 MyBatis 报 Alias 重复的问题。', + }, + { + fieldName: 'author', + label: '作者', + component: 'Input', + componentProps: { + placeholder: '请输入作者', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + rows: 3, + placeholder: '请输入备注', + }, + formItemClass: 'md:col-span-2', + }, + ]; +} + +/** 生成信息表单基础 schema */ +export function useGenerationInfoBaseFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Select', + fieldName: 'templateType', + label: '生成模板', + componentProps: { + options: getDictOptions( + DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE, + 'number', + ), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'frontType', + label: '前端类型', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_FRONT_TYPE, 'number'), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'scene', + label: '生成场景', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE, 'number'), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + fieldName: 'parentMenuId', + label: '上级菜单', + help: '分配到指定菜单下,例如 系统管理', + component: 'ApiTreeSelect', + componentProps: { + clearable: true, + api: async () => { + const data = await getMenuList(); + data.unshift({ + id: 0, + name: '顶级菜单', + } as SystemMenuApi.Menu); + return handleTree(data); + }, + class: 'w-full', + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级菜单', + filterTreeNode(input: string, node: Recordable) { + if (!input || input.length === 0) { + return true; + } + const name: string = node.label ?? ''; + if (!name) return false; + return name.includes(input) || $t(name).includes(input); + }, + showSearch: true, + treeDefaultExpandedKeys: [0], + }, + rules: 'selectRequired', + renderComponentContent() { + return { + title({ label, icon }: { icon: string; label: string }) { + const components = []; + if (!label) return ''; + if (icon) { + components.push(h(IconifyIcon, { class: 'size-4', icon })); + } + components.push(h('span', { class: '' }, $t(label || ''))); + return h('div', { class: 'flex items-center gap-1' }, components); + }, + }; + }, + }, + { + component: 'Input', + fieldName: 'moduleName', + label: '模块名', + help: '模块名,即一级目录,例如 system、infra、tool 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'businessName', + label: '业务名', + help: '业务名,即二级目录,例如 user、permission、dict 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'className', + label: '类名称', + help: '类名称(首字母大写),例如SysUser、SysMenu、SysDictData 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'classComment', + label: '类描述', + help: '用作类描述,例如 用户', + rules: 'required', + }, + ]; +} + +/** 树表信息 schema */ +export function useGenerationInfoTreeFormSchema( + columns: InfraCodegenApi.CodegenColumn[] = [], +): VbenFormSchema[] { + return [ + { + component: 'Divider', + fieldName: 'treeDivider', + label: '', + renderComponentContent: () => { + return { + default: () => ['树表信息'], + }; + }, + formItemClass: 'md:col-span-2', + }, + { + component: 'Select', + fieldName: 'treeParentColumnId', + label: '父编号字段', + help: '树显示的父编码字段名,例如 parent_Id', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择', + options: columns.map((column) => ({ + label: column.columnName, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'treeNameColumnId', + label: '名称字段', + help: '树节点显示的名称字段,一般是 name', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择名称字段', + options: columns.map((column) => ({ + label: column.columnName, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + ]; +} + +/** 主子表信息 schema */ +export function useGenerationInfoSubTableFormSchema( + columns: InfraCodegenApi.CodegenColumn[] = [], + tables: InfraCodegenApi.CodegenTable[] = [], +): VbenFormSchema[] { + return [ + { + component: 'Divider', + fieldName: 'subDivider', + label: '', + renderComponentContent: () => { + return { + default: () => ['主子表信息'], + }; + }, + formItemClass: 'md:col-span-2', + }, + { + component: 'Select', + fieldName: 'masterTableId', + label: '关联的主表', + help: '关联主表(父表)的表名, 如:system_user', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择', + options: tables.map((table) => ({ + label: `${table.tableName}:${table.tableComment}`, + value: table.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'subJoinColumnId', + label: '子表关联的字段', + help: '子表关联的字段, 如:user_id', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择', + options: columns.map((column) => ({ + label: `${column.columnName}:${column.columnComment}`, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'RadioGroup', + fieldName: 'subJoinMany', + label: '关联关系', + help: '主表与子表的关联关系', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择', + options: [ + { + label: '一对多', + value: true, + }, + { + label: '一对一', + value: false, + }, + ], + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'tableName', + label: '表名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入表名称', + }, + }, + { + fieldName: 'tableComment', + label: '表描述', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入表描述', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + getDataSourceConfigName?: (dataSourceConfigId: number) => string | undefined, +): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'dataSourceConfigId', + title: '数据源', + minWidth: 120, + formatter: ({ cellValue }) => getDataSourceConfigName?.(cellValue) || '-', + }, + { + field: 'tableName', + title: '表名称', + minWidth: 200, + }, + { + field: 'tableComment', + title: '表描述', + minWidth: 200, + }, + { + field: 'className', + title: '实体', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 代码生成表格列定义 */ +export function useCodegenColumnTableColumns(): VxeTableGridOptions['columns'] { + return [ + { field: 'columnName', title: '字段列名', minWidth: 130 }, + { + field: 'columnComment', + title: '字段描述', + minWidth: 100, + slots: { default: 'columnComment' }, + }, + { field: 'dataType', title: '物理类型', minWidth: 100 }, + { + field: 'javaType', + title: 'Java 类型', + minWidth: 130, + slots: { default: 'javaType' }, + params: { + options: [ + { label: 'Long', value: 'Long' }, + { label: 'String', value: 'String' }, + { label: 'Integer', value: 'Integer' }, + { label: 'Double', value: 'Double' }, + { label: 'BigDecimal', value: 'BigDecimal' }, + { label: 'LocalDateTime', value: 'LocalDateTime' }, + { label: 'Boolean', value: 'Boolean' }, + ], + }, + }, + { + field: 'javaField', + title: 'Java 属性', + minWidth: 100, + slots: { default: 'javaField' }, + }, + { + field: 'createOperation', + title: '插入', + width: 40, + slots: { default: 'createOperation' }, + }, + { + field: 'updateOperation', + title: '编辑', + width: 40, + slots: { default: 'updateOperation' }, + }, + { + field: 'listOperationResult', + title: '列表', + width: 40, + slots: { default: 'listOperationResult' }, + }, + { + field: 'listOperation', + title: '查询', + width: 40, + slots: { default: 'listOperation' }, + }, + { + field: 'listOperationCondition', + title: '查询方式', + minWidth: 100, + slots: { default: 'listOperationCondition' }, + params: { + options: [ + { label: '=', value: '=' }, + { label: '!=', value: '!=' }, + { label: '>', value: '>' }, + { label: '>=', value: '>=' }, + { label: '<', value: '<' }, + { label: '<=', value: '<=' }, + { label: 'LIKE', value: 'LIKE' }, + { label: 'BETWEEN', value: 'BETWEEN' }, + ], + }, + }, + { + field: 'nullable', + title: '允许空', + width: 60, + slots: { default: 'nullable' }, + }, + { + field: 'htmlType', + title: '显示类型', + width: 130, + slots: { default: 'htmlType' }, + params: { + options: [ + { label: '文本框', value: 'input' }, + { label: '文本域', value: 'textarea' }, + { label: '下拉框', value: 'select' }, + { label: '单选框', value: 'radio' }, + { label: '复选框', value: 'checkbox' }, + { label: '日期控件', value: 'datetime' }, + { label: '图片上传', value: 'imageUpload' }, + { label: '文件上传', value: 'fileUpload' }, + { label: '富文本控件', value: 'editor' }, + ], + }, + }, + { + field: 'dictType', + title: '字典类型', + width: 120, + slots: { default: 'dictType' }, + }, + { + field: 'example', + title: '示例', + minWidth: 100, + slots: { default: 'example' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/codegen/edit/index.vue b/apps/web-ele/src/views/infra/codegen/edit/index.vue new file mode 100644 index 000000000..63161ed74 --- /dev/null +++ b/apps/web-ele/src/views/infra/codegen/edit/index.vue @@ -0,0 +1,169 @@ + + + diff --git a/apps/web-ele/src/views/infra/codegen/index.vue b/apps/web-ele/src/views/infra/codegen/index.vue new file mode 100644 index 000000000..cab2e64ac --- /dev/null +++ b/apps/web-ele/src/views/infra/codegen/index.vue @@ -0,0 +1,283 @@ + + diff --git a/apps/web-ele/src/views/infra/codegen/modules/column-info.vue b/apps/web-ele/src/views/infra/codegen/modules/column-info.vue new file mode 100644 index 000000000..493366978 --- /dev/null +++ b/apps/web-ele/src/views/infra/codegen/modules/column-info.vue @@ -0,0 +1,152 @@ + + + diff --git a/apps/web-ele/src/views/infra/codegen/modules/generation-info.vue b/apps/web-ele/src/views/infra/codegen/modules/generation-info.vue new file mode 100644 index 000000000..56ffe616f --- /dev/null +++ b/apps/web-ele/src/views/infra/codegen/modules/generation-info.vue @@ -0,0 +1,176 @@ + + + diff --git a/apps/web-ele/src/views/infra/codegen/modules/import-table.vue b/apps/web-ele/src/views/infra/codegen/modules/import-table.vue new file mode 100644 index 000000000..2225c932f --- /dev/null +++ b/apps/web-ele/src/views/infra/codegen/modules/import-table.vue @@ -0,0 +1,119 @@ + + + diff --git a/apps/web-ele/src/views/infra/codegen/modules/preview-code.vue b/apps/web-ele/src/views/infra/codegen/modules/preview-code.vue new file mode 100644 index 000000000..4eb1104a0 --- /dev/null +++ b/apps/web-ele/src/views/infra/codegen/modules/preview-code.vue @@ -0,0 +1,279 @@ + + + diff --git a/apps/web-ele/src/views/infra/config/data.ts b/apps/web-ele/src/views/infra/config/data.ts new file mode 100644 index 000000000..c5fd28c7c --- /dev/null +++ b/apps/web-ele/src/views/infra/config/data.ts @@ -0,0 +1,185 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'category', + label: '参数分类', + component: 'Input', + componentProps: { + placeholder: '请输入参数分类', + }, + rules: 'required', + }, + { + fieldName: 'name', + label: '参数名称', + component: 'Input', + componentProps: { + placeholder: '请输入参数名称', + }, + rules: 'required', + }, + { + fieldName: 'key', + label: '参数键名', + component: 'Input', + componentProps: { + placeholder: '请输入参数键名', + }, + rules: 'required', + }, + { + fieldName: 'value', + label: '参数键值', + component: 'Input', + componentProps: { + placeholder: '请输入参数键值', + }, + rules: 'required', + }, + { + fieldName: 'visible', + label: '是否可见', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + }, + defaultValue: true, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '参数名称', + component: 'Input', + componentProps: { + placeholder: '请输入参数名称', + clearable: true, + }, + }, + { + fieldName: 'key', + label: '参数键名', + component: 'Input', + componentProps: { + placeholder: '请输入参数键名', + clearable: true, + }, + }, + { + fieldName: 'type', + label: '系统内置', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CONFIG_TYPE, 'number'), + placeholder: '请选择系统内置', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '参数主键', + minWidth: 100, + }, + { + field: 'category', + title: '参数分类', + minWidth: 120, + }, + { + field: 'name', + title: '参数名称', + minWidth: 200, + }, + { + field: 'key', + title: '参数键名', + minWidth: 200, + }, + { + field: 'value', + title: '参数键值', + minWidth: 150, + }, + { + field: 'visible', + title: '是否可见', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'type', + title: '系统内置', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_CONFIG_TYPE }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/config/index.vue b/apps/web-ele/src/views/infra/config/index.vue new file mode 100644 index 000000000..18699040a --- /dev/null +++ b/apps/web-ele/src/views/infra/config/index.vue @@ -0,0 +1,183 @@ + + + diff --git a/apps/web-ele/src/views/infra/config/modules/form.vue b/apps/web-ele/src/views/infra/config/modules/form.vue new file mode 100644 index 000000000..777f29dab --- /dev/null +++ b/apps/web-ele/src/views/infra/config/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/infra/dataSourceConfig/data.ts b/apps/web-ele/src/views/infra/dataSourceConfig/data.ts new file mode 100644 index 000000000..6f4c6027f --- /dev/null +++ b/apps/web-ele/src/views/infra/dataSourceConfig/data.ts @@ -0,0 +1,92 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '数据源名称', + component: 'Input', + componentProps: { + placeholder: '请输入数据源名称', + }, + rules: 'required', + }, + { + fieldName: 'url', + label: '数据源连接', + component: 'Input', + componentProps: { + placeholder: '请输入数据源连接', + }, + rules: 'required', + }, + { + fieldName: 'username', + label: '用户名', + component: 'Input', + componentProps: { + placeholder: '请输入用户名', + }, + rules: 'required', + }, + { + fieldName: 'password', + label: '密码', + component: 'Input', + componentProps: { + placeholder: '请输入密码', + type: 'password', + }, + rules: 'required', + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '主键编号', + minWidth: 100, + }, + { + field: 'name', + title: '数据源名称', + minWidth: 150, + }, + { + field: 'url', + title: '数据源连接', + minWidth: 300, + }, + { + field: 'username', + title: '用户名', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/dataSourceConfig/index.vue b/apps/web-ele/src/views/infra/dataSourceConfig/index.vue new file mode 100644 index 000000000..c885bf455 --- /dev/null +++ b/apps/web-ele/src/views/infra/dataSourceConfig/index.vue @@ -0,0 +1,159 @@ + + + diff --git a/apps/web-ele/src/views/infra/dataSourceConfig/modules/form.vue b/apps/web-ele/src/views/infra/dataSourceConfig/modules/form.vue new file mode 100644 index 000000000..6291c7406 --- /dev/null +++ b/apps/web-ele/src/views/infra/dataSourceConfig/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo01/data.ts b/apps/web-ele/src/views/infra/demo/demo01/data.ts new file mode 100644 index 000000000..d2242a83a --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo01/data.ts @@ -0,0 +1,152 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo01ContactApi } from '#/api/infra/demo/demo01'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生年', + rules: 'required', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + placeholder: '请选择出生年', + class: '!w-full', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + { + fieldName: 'avatar', + label: '头像', + component: 'ImageUpload', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生年', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'avatar', + title: '头像', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/demo/demo01/index.vue b/apps/web-ele/src/views/infra/demo/demo01/index.vue new file mode 100644 index 000000000..4b5455abe --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo01/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo01/modules/form.vue b/apps/web-ele/src/views/infra/demo/demo01/modules/form.vue new file mode 100644 index 000000000..d97dd9c89 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo01/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo02/data.ts b/apps/web-ele/src/views/infra/demo/demo02/data.ts new file mode 100644 index 000000000..6fb82a8ca --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo02/data.ts @@ -0,0 +1,120 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo02CategoryApi } from '#/api/infra/demo/demo02'; + +import { handleTree } from '@vben/utils'; + +import { getDemo02CategoryList } from '#/api/infra/demo/demo02'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级示例分类', + component: 'ApiTreeSelect', + componentProps: { + clearable: true, + api: async () => { + const data = await getDemo02CategoryList({}); + data.unshift({ + id: 0, + name: '顶级示例分类', + }); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级示例分类', + treeDefaultExpandAll: true, + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'parentId', + label: '父级编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入父级编号', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + treeNode: true, + }, + { + field: 'parentId', + title: '父级编号', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/demo/demo02/index.vue b/apps/web-ele/src/views/infra/demo/demo02/index.vue new file mode 100644 index 000000000..b5eab45bf --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo02/index.vue @@ -0,0 +1,175 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo02/modules/form.vue b/apps/web-ele/src/views/infra/demo/demo02/modules/form.vue new file mode 100644 index 000000000..2f4a58124 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo02/modules/form.vue @@ -0,0 +1,91 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/erp/data.ts b/apps/web-ele/src/views/infra/demo/demo03/erp/data.ts new file mode 100644 index 000000000..63580aa17 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/erp/data.ts @@ -0,0 +1,381 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + placeholder: '请选择出生日期', + class: '!w-full', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改的表单 */ +export function useDemo03CourseFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'score', + label: '分数', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入分数', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useDemo03CourseGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'studentId', + label: '学生编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入学生编号', + }, + }, + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'score', + label: '分数', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入分数', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03CourseGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useDemo03GradeGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'studentId', + label: '学生编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入学生编号', + }, + }, + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入班主任', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03GradeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'teacher', + title: '班主任', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/demo/demo03/erp/index.vue b/apps/web-ele/src/views/infra/demo/demo03/erp/index.vue new file mode 100644 index 000000000..043dd98c8 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/erp/index.vue @@ -0,0 +1,207 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue new file mode 100644 index 000000000..dfb34f1e7 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue @@ -0,0 +1,92 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue new file mode 100644 index 000000000..076e33a58 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue @@ -0,0 +1,197 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue new file mode 100644 index 000000000..0b41e208a --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue @@ -0,0 +1,92 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue new file mode 100644 index 000000000..c4b42faf2 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue @@ -0,0 +1,197 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/erp/modules/form.vue b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/form.vue new file mode 100644 index 000000000..b0ad481d1 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/erp/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/inner/data.ts b/apps/web-ele/src/views/infra/demo/demo03/inner/data.ts new file mode 100644 index 000000000..e79cf78b1 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/inner/data.ts @@ -0,0 +1,275 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + class: '!w-full', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { type: 'expand', width: 80, slots: { content: 'expand_content' } }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改列表的字段 */ +export function useDemo03CourseGridEditColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '名字', + minWidth: 120, + slots: { default: 'name' }, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + slots: { default: 'score' }, + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03CourseGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03GradeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'teacher', + title: '班主任', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/demo/demo03/inner/index.vue b/apps/web-ele/src/views/infra/demo/demo03/inner/index.vue new file mode 100644 index 000000000..eadfc8201 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/inner/index.vue @@ -0,0 +1,201 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue b/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue new file mode 100644 index 000000000..0402bf441 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue @@ -0,0 +1,119 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue b/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue new file mode 100644 index 000000000..50297db48 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue @@ -0,0 +1,56 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue b/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue new file mode 100644 index 000000000..1ad2db740 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue @@ -0,0 +1,56 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/inner/modules/form.vue b/apps/web-ele/src/views/infra/demo/demo03/inner/modules/form.vue new file mode 100644 index 000000000..753a6899b --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/inner/modules/form.vue @@ -0,0 +1,119 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/normal/data.ts b/apps/web-ele/src/views/infra/demo/demo03/normal/data.ts new file mode 100644 index 000000000..ef76bdd2e --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/normal/data.ts @@ -0,0 +1,211 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + showTime: true, + format: 'YYYY-MM-DD HH:mm:ss', + valueFormat: 'x', + placeholder: '请选择出生日期', + class: '!w-full', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改列表的字段 */ +export function useDemo03CourseGridEditColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '名字', + minWidth: 120, + slots: { default: 'name' }, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + slots: { default: 'score' }, + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/infra/demo/demo03/normal/index.vue b/apps/web-ele/src/views/infra/demo/demo03/normal/index.vue new file mode 100644 index 000000000..7c8c07d51 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/normal/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue b/apps/web-ele/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue new file mode 100644 index 000000000..f460aee2d --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue @@ -0,0 +1,118 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/demo03/normal/modules/form.vue b/apps/web-ele/src/views/infra/demo/demo03/normal/modules/form.vue new file mode 100644 index 000000000..7b9d1b0e8 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/demo03/normal/modules/form.vue @@ -0,0 +1,119 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo01/index.vue b/apps/web-ele/src/views/infra/demo/general/demo01/index.vue new file mode 100644 index 000000000..0da2e0ab6 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo01/index.vue @@ -0,0 +1,314 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo01/modules/form.vue b/apps/web-ele/src/views/infra/demo/general/demo01/modules/form.vue new file mode 100644 index 000000000..05c2cf721 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo01/modules/form.vue @@ -0,0 +1,149 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo02/index.vue b/apps/web-ele/src/views/infra/demo/general/demo02/index.vue new file mode 100644 index 000000000..500cbe2da --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo02/index.vue @@ -0,0 +1,264 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/erp/index.vue b/apps/web-ele/src/views/infra/demo/general/demo03/erp/index.vue new file mode 100644 index 000000000..9c62a60fe --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/erp/index.vue @@ -0,0 +1,342 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/demo03-course-list.vue b/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/demo03-course-list.vue new file mode 100644 index 000000000..32abd3846 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/demo03-course-list.vue @@ -0,0 +1,294 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-list.vue b/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-list.vue new file mode 100644 index 000000000..adc374d18 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/demo03-grade-list.vue @@ -0,0 +1,294 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/form.vue b/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/form.vue new file mode 100644 index 000000000..d9cc4154d --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/erp/modules/form.vue @@ -0,0 +1,140 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/inner/index.vue b/apps/web-ele/src/views/infra/demo/general/demo03/inner/index.vue new file mode 100644 index 000000000..7fca75c75 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/inner/index.vue @@ -0,0 +1,335 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/demo03-course-list.vue b/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/demo03-course-list.vue new file mode 100644 index 000000000..678042aa2 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/demo03-course-list.vue @@ -0,0 +1,59 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-list.vue b/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-list.vue new file mode 100644 index 000000000..4272b63e9 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/demo03-grade-list.vue @@ -0,0 +1,59 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/form.vue b/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/form.vue new file mode 100644 index 000000000..63ee76c8a --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/inner/modules/form.vue @@ -0,0 +1,176 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/normal/index.vue b/apps/web-ele/src/views/infra/demo/general/demo03/normal/index.vue new file mode 100644 index 000000000..38244937c --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/normal/index.vue @@ -0,0 +1,314 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/normal/modules/demo03-course-form.vue b/apps/web-ele/src/views/infra/demo/general/demo03/normal/modules/demo03-course-form.vue new file mode 100644 index 000000000..738283919 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/normal/modules/demo03-course-form.vue @@ -0,0 +1,95 @@ + + + diff --git a/apps/web-ele/src/views/infra/demo/general/demo03/normal/modules/form.vue b/apps/web-ele/src/views/infra/demo/general/demo03/normal/modules/form.vue new file mode 100644 index 000000000..9dd6a5fb0 --- /dev/null +++ b/apps/web-ele/src/views/infra/demo/general/demo03/normal/modules/form.vue @@ -0,0 +1,176 @@ + + + diff --git a/apps/web-ele/src/views/infra/druid/index.vue b/apps/web-ele/src/views/infra/druid/index.vue new file mode 100644 index 000000000..01ec58d5f --- /dev/null +++ b/apps/web-ele/src/views/infra/druid/index.vue @@ -0,0 +1,36 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/components/diy-editor/util.ts b/apps/web-ele/src/views/mall/promotion/components/diy-editor/util.ts new file mode 100644 index 000000000..8926ca581 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/components/diy-editor/util.ts @@ -0,0 +1,115 @@ +import type { NavigationBarProperty } from './components/mobile/navigation-bar/config'; +import type { PageConfigProperty } from './components/mobile/page-config/config'; +import type { TabBarProperty } from './components/mobile/tab-bar/config'; + +/** 页面装修组件 */ +export interface DiyComponent { + uid?: number; // 用于区分同一种组件的不同实例 + id: string; // 组件唯一标识 + name: string; // 组件名称 + icon: string; // 组件图标 + /* + 组件位置: + top: 固定于手机顶部,例如 顶部的导航栏 + bottom: 固定于手机底部,例如 底部的菜单导航栏 + center: 位于手机中心,每个组件占一行,顺序向下排列 + 空:同 center + fixed: 由组件自己决定位置,如弹窗位于手机中心、浮动按钮一般位于手机右下角 + */ + position?: '' | 'bottom' | 'center' | 'fixed' | 'top'; + property: T; // 组件属性 +} + +/** 页面装修组件库 */ +export interface DiyComponentLibrary { + name: string; // 组件库名称 + extended: boolean; // 是否展开 + components: string[]; // 组件列表 +} + +/** 组件样式 */ +export interface ComponentStyle { + bgType: 'color' | 'img'; // 背景类型 + bgColor: string; // 背景颜色 + bgImg: string; // 背景图片 + // 外边距 + margin: number; + marginTop: number; + marginRight: number; + marginBottom: number; + marginLeft: number; + // 内边距 + padding: number; + paddingTop: number; + paddingRight: number; + paddingBottom: number; + paddingLeft: number; + // 边框圆角 + borderRadius: number; + borderTopLeftRadius: number; + borderTopRightRadius: number; + borderBottomRightRadius: number; + borderBottomLeftRadius: number; +} + +/** 页面配置 */ +export interface PageConfig { + page: PageConfigProperty; // 页面属性 + navigationBar: NavigationBarProperty; // 顶部导航栏属性 + tabBar?: TabBarProperty; // 底部导航菜单属性 + + components: PageComponent[]; // 页面组件列表 +} + +export type PageComponent = Pick, 'id' | 'property'>; // 页面组件,只保留组件 ID,组件属性 + +/** 页面组件库 */ +export const PAGE_LIBS = [ + { + name: '基础组件', + extended: true, + components: [ + 'SearchBar', + 'NoticeBar', + 'MenuSwiper', + 'MenuGrid', + 'MenuList', + 'Popover', + 'FloatingActionButton', + ], + }, + { + name: '图文组件', + extended: true, + components: [ + 'ImageBar', + 'Carousel', + 'TitleBar', + 'VideoPlayer', + 'Divider', + 'MagicCube', + 'HotZone', + ], + }, + { + name: '商品组件', + extended: true, + components: ['ProductCard', 'ProductList'], + }, + { + name: '用户组件', + extended: true, + components: ['UserCard', 'UserOrder', 'UserWallet', 'UserCoupon'], + }, + { + name: '营销组件', + extended: true, + components: [ + 'PromotionCombination', + 'PromotionSeckill', + 'PromotionPoint', + 'CouponCard', + 'PromotionArticle', + ], + }, +] as DiyComponentLibrary[]; diff --git a/apps/web-ele/src/views/mall/promotion/components/draggable/index.vue b/apps/web-ele/src/views/mall/promotion/components/draggable/index.vue new file mode 100644 index 000000000..3c673a709 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/components/draggable/index.vue @@ -0,0 +1,95 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/components/index.ts b/apps/web-ele/src/views/mall/promotion/components/index.ts new file mode 100644 index 000000000..6dbe001fe --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/components/index.ts @@ -0,0 +1,10 @@ +export { default as AppLinkInput } from './app-link-input/index.vue'; +export { default as AppLinkSelectDialog } from './app-link-input/select-dialog.vue'; +export { default as ColorInput } from './color-input/index.vue'; +export { default as DiyEditor } from './diy-editor/index.vue'; +export { type DiyComponentLibrary, PAGE_LIBS } from './diy-editor/util'; +export { default as Draggable } from './draggable/index.vue'; +export { default as InputWithColor } from './input-with-color/index.vue'; + +export { default as MagicCubeEditor } from './magic-cube-editor/index.vue'; +export { default as VerticalButtonGroup } from './vertical-button-group/index.vue'; diff --git a/apps/web-ele/src/views/mall/promotion/components/input-with-color/index.vue b/apps/web-ele/src/views/mall/promotion/components/input-with-color/index.vue new file mode 100644 index 000000000..d82567808 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/components/input-with-color/index.vue @@ -0,0 +1,44 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/components/magic-cube-editor/index.vue b/apps/web-ele/src/views/mall/promotion/components/magic-cube-editor/index.vue new file mode 100644 index 000000000..5f45238b8 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/components/magic-cube-editor/index.vue @@ -0,0 +1,254 @@ + + diff --git a/apps/web-ele/src/views/mall/promotion/components/magic-cube-editor/util.ts b/apps/web-ele/src/views/mall/promotion/components/magic-cube-editor/util.ts new file mode 100644 index 000000000..d55cd4b65 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/components/magic-cube-editor/util.ts @@ -0,0 +1,71 @@ +/** 坐标点 */ +export interface Point { + x: number; + y: number; +} + +/** 矩形 */ +export interface Rect { + left: number; // 左上角 X 轴坐标 + top: number; // 左上角 Y 轴坐标 + right: number; // 右下角 X 轴坐标 + bottom: number; // 右下角 Y 轴坐标 + width: number; // 矩形宽度 + height: number; // 矩形高度 +} + +/** + * 判断两个矩形是否重叠 + * + * @param a 矩形 A + * @param b 矩形 B + */ +export function isOverlap(a: Rect, b: Rect): boolean { + return ( + a.left < b.left + b.width && + a.left + a.width > b.left && + a.top < b.top + b.height && + a.height + a.top > b.top + ); +} + +/** + * 检查坐标点是否在矩形内 + * @param hotArea 矩形 + * @param point 坐标 + */ +export function isContains(hotArea: Rect, point: Point): boolean { + return ( + point.x >= hotArea.left && + point.x < hotArea.right && + point.y >= hotArea.top && + point.y < hotArea.bottom + ); +} + +/** + * 在两个坐标点中间,创建一个矩形 + * + * 存在以下情况: + * 1. 两个坐标点是同一个位置,只占一个位置的正方形,宽高都为 1 + * 2. X 轴坐标相同,只占一行的矩形,高度为 1 + * 3. Y 轴坐标相同,只占一列的矩形,宽度为 1 + * 4. 多行多列的矩形 + * + * @param a 坐标点一 + * @param b 坐标点二 + */ +export function createRect(a: Point, b: Point): Rect { + // 计算矩形的范围 + let [left, left2] = [a.x, b.x].toSorted(); + left = left ?? 0; + left2 = left2 ?? 0; + let [top, top2] = [a.y, b.y].toSorted(); + top = top ?? 0; + top2 = top2 ?? 0; + const right = left2 + 1; + const bottom = top2 + 1; + const height = bottom - top; + const width = right - left; + return { left, right, top, bottom, height, width }; +} diff --git a/apps/web-ele/src/views/mall/promotion/components/vertical-button-group/index.vue b/apps/web-ele/src/views/mall/promotion/components/vertical-button-group/index.vue new file mode 100644 index 000000000..16f4b33d0 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/components/vertical-button-group/index.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/apps/web-ele/src/views/mall/promotion/coupon/components/index.ts b/apps/web-ele/src/views/mall/promotion/coupon/components/index.ts new file mode 100644 index 000000000..24cb4e274 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/components/index.ts @@ -0,0 +1,2 @@ +export { default as CouponSelect } from './select.vue'; +export { default as CouponSendForm } from './send-form.vue'; diff --git a/apps/web-ele/src/views/mall/promotion/coupon/components/select-data.ts b/apps/web-ele/src/views/mall/promotion/coupon/components/select-data.ts new file mode 100644 index 000000000..4820881d0 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/components/select-data.ts @@ -0,0 +1,119 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { + discountFormat, + remainedCountFormat, + takeLimitCountFormat, + validityTypeFormat, +} from '../formatter'; + +/** 优惠券选择的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '优惠券名称', + component: 'Input', + componentProps: { + placeholder: '请输入优惠券名称', + clearable: true, + }, + }, + { + fieldName: 'discountType', + label: '优惠类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.PROMOTION_DISCOUNT_TYPE, 'number'), + placeholder: '请选择优惠类型', + clearable: true, + }, + }, + ]; +} + +/** 优惠券选择的表格列 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 55 }, + { + field: 'name', + title: '优惠券名称', + minWidth: 140, + }, + { + field: 'productScope', + title: '类型', + minWidth: 80, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE }, + }, + }, + { + field: 'discountType', + title: '优惠类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE }, + }, + }, + { + field: 'discountPrice', + title: '优惠力度', + minWidth: 100, + formatter: ({ row }) => discountFormat(row), + }, + { + field: 'takeType', + title: '领取方式', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE }, + }, + }, + { + field: 'validityType', + title: '使用时间', + minWidth: 185, + align: 'center', + formatter: ({ row }) => validityTypeFormat(row), + }, + { + field: 'totalCount', + title: '发放数量', + minWidth: 100, + align: 'center', + }, + { + field: 'remainedCount', + title: '剩余数量', + minWidth: 100, + align: 'center', + formatter: ({ row }) => remainedCountFormat(row), + }, + { + field: 'takeLimitCount', + title: '领取上限', + minWidth: 100, + align: 'center', + formatter: ({ row }) => takeLimitCountFormat(row), + }, + { + field: 'status', + title: '状态', + minWidth: 80, + align: 'center', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/coupon/components/select.vue b/apps/web-ele/src/views/mall/promotion/coupon/components/select.vue new file mode 100644 index 000000000..073206aff --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/components/select.vue @@ -0,0 +1,66 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/coupon/components/send-form-data.ts b/apps/web-ele/src/views/mall/promotion/coupon/components/send-form-data.ts new file mode 100644 index 000000000..6961bfa74 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/components/send-form-data.ts @@ -0,0 +1,64 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { + discountFormat, + remainedCountFormat, + usePriceFormat, + validityTypeFormat, +} from '../formatter'; + +/** 搜索表单的 schema */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'name', + label: '优惠券名称', + componentProps: { + placeholder: '请输入优惠券名称', + clearable: true, + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeGridProps['columns'] { + return [ + { + title: '优惠券名称', + field: 'name', + minWidth: 120, + }, + { + title: '优惠金额 / 折扣', + field: 'discount', + minWidth: 120, + formatter: ({ row }) => discountFormat(row), + }, + { + title: '最低消费', + field: 'usePrice', + minWidth: 100, + formatter: ({ row }) => usePriceFormat(row), + }, + { + title: '有效期限', + field: 'validityType', + minWidth: 140, + formatter: ({ row }) => validityTypeFormat(row), + }, + { + title: '剩余数量', + minWidth: 100, + formatter: ({ row }) => remainedCountFormat(row), + }, + { + title: '操作', + width: 100, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/coupon/components/send-form.vue b/apps/web-ele/src/views/mall/promotion/coupon/components/send-form.vue new file mode 100644 index 000000000..ce609628a --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/components/send-form.vue @@ -0,0 +1,86 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/coupon/data.ts b/apps/web-ele/src/views/mall/promotion/coupon/data.ts new file mode 100644 index 000000000..2c5dc8b40 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/data.ts @@ -0,0 +1,111 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +import { getRangePickerDefaultProps } from '#/utils'; + +import { discountFormat } from './formatter'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'nickname', + label: '会员昵称', + component: 'Input', + componentProps: { + placeholder: '请输入会员昵称', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '领取时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'nickname', + title: '会员昵称', + minWidth: 100, + }, + { + field: 'name', + title: '优惠券名称', + minWidth: 140, + }, + { + field: 'productScope', + title: '类型', + minWidth: 110, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE }, + }, + }, + { + field: 'discountType', + title: '优惠', + minWidth: 110, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE }, + }, + }, + { + field: 'discountPrice', + title: '优惠力度', + minWidth: 110, + formatter: ({ row }) => { + return discountFormat(row); + }, + }, + { + field: 'takeType', + title: '领取方式', + minWidth: 110, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE }, + }, + }, + { + field: 'status', + title: '状态', + minWidth: 110, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_COUPON_STATUS }, + }, + }, + { + field: 'createTime', + title: '领取时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'useTime', + title: '使用时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'actions', + title: '操作', + width: 100, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/coupon/formatter.ts b/apps/web-ele/src/views/mall/promotion/coupon/formatter.ts new file mode 100644 index 000000000..2f34863ff --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/formatter.ts @@ -0,0 +1,64 @@ +import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate'; + +import { + CouponTemplateValidityTypeEnum, + PromotionDiscountTypeEnum, +} from '@vben/constants'; +import { floatToFixed2, formatDate } from '@vben/utils'; + +/** 格式化【优惠金额/折扣】 */ +export function discountFormat(row: MallCouponTemplateApi.CouponTemplate) { + if (row.discountType === PromotionDiscountTypeEnum.PRICE.type) { + return `¥${floatToFixed2(row.discountPrice)}`; + } + if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) { + return `${row.discountPercent}%`; + } + return `未知【${row.discountType}】`; +} + +/** 格式化【领取上限】 */ +export function takeLimitCountFormat( + row: MallCouponTemplateApi.CouponTemplate, +) { + if (row.takeLimitCount) { + if (row.takeLimitCount === -1) { + return '无领取限制'; + } + return `${row.takeLimitCount} 张/人`; + } else { + return ' '; + } +} + +/** 格式化【有效期限】 */ +export function validityTypeFormat(row: MallCouponTemplateApi.CouponTemplate) { + if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) { + return `${formatDate(row.validStartTime)} 至 ${formatDate(row.validEndTime)}`; + } + if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) { + return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`; + } + return `未知【${row.validityType}】`; +} + +/** 格式化【totalCount】 */ +export function totalCountFormat(row: MallCouponTemplateApi.CouponTemplate) { + if (row.totalCount === -1) { + return '不限制'; + } + return row.totalCount; +} + +/** 格式化【剩余数量】 */ +export function remainedCountFormat(row: MallCouponTemplateApi.CouponTemplate) { + if (row.totalCount === -1) { + return '不限制'; + } + return row.totalCount - row.takeCount; +} + +/** 格式化【最低消费】 */ +export function usePriceFormat(row: MallCouponTemplateApi.CouponTemplate) { + return `¥${floatToFixed2(row.usePrice)}`; +} diff --git a/apps/web-ele/src/views/mall/promotion/coupon/index.vue b/apps/web-ele/src/views/mall/promotion/coupon/index.vue new file mode 100644 index 000000000..17580fcfd --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/index.vue @@ -0,0 +1,148 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/coupon/template/data.ts b/apps/web-ele/src/views/mall/promotion/coupon/template/data.ts new file mode 100644 index 000000000..035259be5 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/template/data.ts @@ -0,0 +1,487 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MallCouponTemplateApi } from '#/api/mall/promotion/coupon/couponTemplate'; + +import { + CommonStatusEnum, + CouponTemplateTakeTypeEnum, + CouponTemplateValidityTypeEnum, + DICT_TYPE, + PromotionDiscountTypeEnum, + PromotionProductScopeEnum, +} from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +import { + discountFormat, + remainedCountFormat, + takeLimitCountFormat, + totalCountFormat, + validityTypeFormat, +} from '../formatter'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '优惠券名称', + component: 'Input', + componentProps: { + placeholder: '请输入优惠券名称', + }, + rules: 'required', + }, + { + fieldName: 'description', + label: '优惠券描述', + component: 'Textarea', + componentProps: { + placeholder: '请输入优惠券描述', + }, + }, + { + fieldName: 'productScope', + label: '优惠劵类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE, 'number'), + }, + rules: 'required', + defaultValue: PromotionProductScopeEnum.ALL.scope, + }, + // TODO @puhui999: 商品选择器优化 + { + fieldName: 'productSpuIds', + label: '商品', + component: 'Input', + componentProps: { + placeholder: '请选择商品', + }, + dependencies: { + triggerFields: ['productScope', 'productScopeValues'], + show: (model) => + model.productScope === PromotionProductScopeEnum.SPU.scope, + trigger(values, form) { + // 当加载已有数据时,根据 productScopeValues 设置 productSpuIds + if ( + values.productScope === PromotionProductScopeEnum.SPU.scope && + values.productScopeValues + ) { + form.setFieldValue('productSpuIds', values.productScopeValues); + } + }, + }, + rules: 'required', + }, + // TODO @puhui999: 商品分类选择器优化 + { + fieldName: 'productCategoryIds', + label: '商品分类', + component: 'Input', + componentProps: { + placeholder: '请选择商品分类', + }, + dependencies: { + triggerFields: ['productScope', 'productScopeValues'], + show: (model) => + model.productScope === PromotionProductScopeEnum.CATEGORY.scope, + trigger(values, form) { + // 当加载已有数据时,根据 productScopeValues 设置 productCategoryIds + if ( + values.productScope === PromotionProductScopeEnum.CATEGORY.scope && + values.productScopeValues + ) { + const categoryIds = values.productScopeValues; + // 单选时使用数组不能反显,取第一个元素 + form.setFieldValue( + 'productCategoryIds', + Array.isArray(categoryIds) && categoryIds.length > 0 + ? categoryIds[0] + : categoryIds, + ); + } + }, + }, + rules: 'required', + }, + { + fieldName: 'discountType', + label: '优惠类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.PROMOTION_DISCOUNT_TYPE, 'number'), + }, + rules: 'required', + defaultValue: PromotionDiscountTypeEnum.PRICE.type, + }, + { + fieldName: 'discountPrice', + label: '优惠券面额', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + placeholder: '请输入优惠金额,单位:元', + addonAfter: '元', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['discountType'], + show: (model) => + model.discountType === PromotionDiscountTypeEnum.PRICE.type, + }, + rules: 'required', + }, + { + fieldName: 'discountPercent', + label: '优惠券折扣', + component: 'InputNumber', + componentProps: { + min: 1, + max: 9.9, + precision: 1, + placeholder: '优惠券折扣不能小于 1 折,且不可大于 9.9 折', + addonAfter: '折', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['discountType'], + show: (model) => + model.discountType === PromotionDiscountTypeEnum.PERCENT.type, + }, + rules: 'required', + }, + { + fieldName: 'discountLimitPrice', + label: '最多优惠', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + placeholder: '请输入最多优惠', + addonAfter: '元', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['discountType'], + show: (model) => + model.discountType === PromotionDiscountTypeEnum.PERCENT.type, + }, + rules: 'required', + }, + { + fieldName: 'usePrice', + label: '满多少元可以使用', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + placeholder: '无门槛请设为 0', + addonAfter: '元', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'takeType', + label: '领取方式', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE, 'number'), + }, + rules: 'required', + defaultValue: CouponTemplateTakeTypeEnum.USER.type, + }, + { + fieldName: 'totalCount', + label: '发放数量', + component: 'InputNumber', + componentProps: { + min: -1, + placeholder: '发放数量,没有之后不能领取或发放,-1 为不限制', + addonAfter: '张', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['takeType'], + show: (model) => + model.takeType === CouponTemplateTakeTypeEnum.USER.type, + }, + rules: 'required', + }, + { + fieldName: 'takeLimitCount', + label: '每人限领个数', + component: 'InputNumber', + componentProps: { + min: -1, + placeholder: '设置为 -1 时,可无限领取', + addonAfter: '张', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['takeType'], + show: (model) => model.takeType === 1, + }, + rules: 'required', + }, + { + fieldName: 'validityType', + label: '有效期类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions( + DICT_TYPE.PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE, + 'number', + ), + }, + defaultValue: CouponTemplateValidityTypeEnum.DATE.type, + rules: 'required', + }, + { + fieldName: 'validTimes', + label: '固定日期', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + valueFormat: 'x', + }, + dependencies: { + triggerFields: ['validityType'], + show: (model) => + model.validityType === CouponTemplateValidityTypeEnum.DATE.type, + }, + rules: 'required', + }, + { + fieldName: 'fixedStartTerm', + label: '领取日期', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '第 0 为今天生效', + addonBefore: '第', + addonAfter: '天', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['validityType'], + show: (model) => + model.validityType === CouponTemplateValidityTypeEnum.TERM.type, + }, + rules: 'required', + }, + { + fieldName: 'fixedEndTerm', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入结束天数', + addonBefore: '至', + addonAfter: '天有效', + controlsPosition: 'right', + class: '!w-full', + }, + dependencies: { + triggerFields: ['validityType'], + show: (model) => + model.validityType === CouponTemplateValidityTypeEnum.TERM.type, + }, + rules: 'required', + }, + { + fieldName: 'productScopeValues', + component: 'Input', + dependencies: { + triggerFields: ['productScope', 'productSpuIds', 'productCategoryIds'], + show: () => false, + trigger(values, form) { + switch (values.productScope) { + case PromotionProductScopeEnum.CATEGORY.scope: { + const categoryIds = Array.isArray(values.productCategoryIds) + ? values.productCategoryIds + : [values.productCategoryIds]; + form.setFieldValue('productScopeValues', categoryIds); + break; + } + case PromotionProductScopeEnum.SPU.scope: { + form.setFieldValue('productScopeValues', values.productSpuIds); + break; + } + } + }, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '优惠券名称', + component: 'Input', + componentProps: { + placeholder: '请输入优惠劵名', + clearable: true, + }, + }, + { + fieldName: 'discountType', + label: '优惠类型', + component: 'Select', + componentProps: { + placeholder: '请选择优惠类型', + clearable: true, + options: getDictOptions(DICT_TYPE.PROMOTION_DISCOUNT_TYPE, 'number'), + }, + }, + { + fieldName: 'status', + label: '优惠券状态', + component: 'Select', + componentProps: { + placeholder: '请选择优惠券状态', + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onStatusChange?: ( + newStatus: number, + row: MallCouponTemplateApi.CouponTemplate, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '优惠券名称', + minWidth: 140, + }, + { + field: 'productScope', + title: '类型', + minWidth: 130, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE }, + }, + }, + { + field: 'discountType', + title: '优惠', + minWidth: 110, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_DISCOUNT_TYPE }, + }, + }, + { + field: 'discountPrice', + title: '优惠力度', + minWidth: 110, + formatter: ({ row }) => { + return discountFormat(row); + }, + }, + { + field: 'takeType', + title: '领取方式', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE }, + }, + }, + { + field: 'validityType', + title: '使用时间', + minWidth: 180, + formatter: ({ row }) => { + return validityTypeFormat(row); + }, + }, + { + field: 'totalCount', + title: '发放数量', + minWidth: 100, + formatter: ({ row }) => { + return totalCountFormat(row); + }, + }, + { + field: 'remainedCount', + title: '剩余数量', + minWidth: 100, + formatter: ({ row }) => { + return remainedCountFormat(row); + }, + }, + { + field: 'takeLimitCount', + title: '领取上限', + minWidth: 100, + formatter: ({ row }) => { + return takeLimitCountFormat(row); + }, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + align: 'center', + cellRender: { + attrs: { beforeChange: onStatusChange }, + name: 'CellSwitch', + props: { + activeValue: CommonStatusEnum.ENABLE, + inactiveValue: CommonStatusEnum.DISABLE, + }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/coupon/template/index.vue b/apps/web-ele/src/views/mall/promotion/coupon/template/index.vue new file mode 100644 index 000000000..cbe999341 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/template/index.vue @@ -0,0 +1,161 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/coupon/template/modules/form.vue b/apps/web-ele/src/views/mall/promotion/coupon/template/modules/form.vue new file mode 100644 index 000000000..c8b735ccc --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/coupon/template/modules/form.vue @@ -0,0 +1,149 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/discountActivity/data.ts b/apps/web-ele/src/views/mall/promotion/discountActivity/data.ts new file mode 100644 index 000000000..8de164a6a --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/discountActivity/data.ts @@ -0,0 +1,161 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDate } from '@vben/utils'; + +/** 表单配置 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '活动名称', + component: 'Input', + componentProps: { + placeholder: '请输入活动名称', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '活动状态', + component: 'Select', + componentProps: { + placeholder: '请选择活动状态', + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: 'required', + }, + { + fieldName: 'startTime', + label: '开始时间', + component: 'DatePicker', + componentProps: { + placeholder: '请选择开始时间', + showTime: false, + valueFormat: 'x', + format: 'YYYY-MM-DD', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'endTime', + label: '结束时间', + component: 'DatePicker', + componentProps: { + placeholder: '请选择结束时间', + showTime: false, + valueFormat: 'x', + format: 'YYYY-MM-DD', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + }, + // TODO + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '活动名称', + component: 'Input', + componentProps: { + placeholder: '请输入活动名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '活动状态', + component: 'Select', + componentProps: { + placeholder: '请选择活动状态', + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'activeTime', + label: '活动时间', + component: 'RangePicker', + componentProps: { + placeholder: ['开始时间', '结束时间'], + clearable: true, + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '活动编号', + minWidth: 80, + }, + { + field: 'name', + title: '活动名称', + minWidth: 140, + }, + { + field: 'activityTime', + title: '活动时间', + minWidth: 210, + formatter: ({ row }) => { + if (!row.startTime || !row.endTime) return ''; + return `${formatDate(row.startTime, 'YYYY-MM-DD')} ~ ${formatDate(row.endTime, 'YYYY-MM-DD')}`; + }, + }, + { + field: 'status', + title: '活动状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/discountActivity/index.vue b/apps/web-ele/src/views/mall/promotion/discountActivity/index.vue new file mode 100644 index 000000000..6d8d15850 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/discountActivity/index.vue @@ -0,0 +1,157 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/discountActivity/modules/form.vue b/apps/web-ele/src/views/mall/promotion/discountActivity/modules/form.vue new file mode 100644 index 000000000..c1b67fe6f --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/discountActivity/modules/form.vue @@ -0,0 +1,96 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/diy/page/data.ts b/apps/web-ele/src/views/mall/promotion/diy/page/data.ts new file mode 100644 index 000000000..cec26ffcf --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/diy/page/data.ts @@ -0,0 +1,110 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单配置 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '页面名称', + component: 'Input', + componentProps: { + placeholder: '请输入页面名称', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + }, + { + fieldName: 'previewPicUrls', + component: 'ImageUpload', + label: '预览图', + componentProps: { + maxNumber: 10, + multiple: true, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '页面名称', + component: 'Input', + componentProps: { + placeholder: '请输入页面名称', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 80, + }, + { + field: 'previewPicUrls', + title: '预览图', + minWidth: 120, + cellRender: { + name: 'CellImages', + }, + }, + { + field: 'name', + title: '页面名称', + minWidth: 150, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/diy/page/decorate/index.vue b/apps/web-ele/src/views/mall/promotion/diy/page/decorate/index.vue new file mode 100644 index 000000000..2dddd5941 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/diy/page/decorate/index.vue @@ -0,0 +1,65 @@ + + diff --git a/apps/web-ele/src/views/mall/promotion/diy/page/index.vue b/apps/web-ele/src/views/mall/promotion/diy/page/index.vue new file mode 100644 index 000000000..af3b0cf18 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/diy/page/index.vue @@ -0,0 +1,152 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/diy/page/modules/form.vue b/apps/web-ele/src/views/mall/promotion/diy/page/modules/form.vue new file mode 100644 index 000000000..b44b4a614 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/diy/page/modules/form.vue @@ -0,0 +1,84 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/diy/template/data.ts b/apps/web-ele/src/views/mall/promotion/diy/template/data.ts new file mode 100644 index 000000000..e3c8f1421 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/diy/template/data.ts @@ -0,0 +1,121 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 表单配置 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + }, + { + fieldName: 'previewPicUrls', + component: 'ImageUpload', + label: '预览图', + componentProps: { + maxNumber: 10, + multiple: true, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 80, + }, + { + field: 'previewPicUrls', + title: '预览图', + minWidth: 120, + cellRender: { + name: 'CellImages', + }, + }, + { + field: 'name', + title: '模板名称', + minWidth: 150, + }, + { + field: 'used', + title: '是否使用', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 250, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/diy/template/decorate/index.vue b/apps/web-ele/src/views/mall/promotion/diy/template/decorate/index.vue new file mode 100644 index 000000000..3f655becf --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/diy/template/decorate/index.vue @@ -0,0 +1,213 @@ + + diff --git a/apps/web-ele/src/views/mall/promotion/diy/template/index.vue b/apps/web-ele/src/views/mall/promotion/diy/template/index.vue new file mode 100644 index 000000000..df68ff883 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/diy/template/index.vue @@ -0,0 +1,180 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/diy/template/modules/form.vue b/apps/web-ele/src/views/mall/promotion/diy/template/modules/form.vue new file mode 100644 index 000000000..8e088d7cf --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/diy/template/modules/form.vue @@ -0,0 +1,86 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/a.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/a.png new file mode 100644 index 000000000..32939004d Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/a.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/aini.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/aini.png new file mode 100644 index 000000000..02cf5c498 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/aini.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/aixin.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/aixin.png new file mode 100644 index 000000000..25e642234 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/aixin.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/baiyan.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/baiyan.png new file mode 100644 index 000000000..d16260afd Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/baiyan.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/bizui.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/bizui.png new file mode 100644 index 000000000..a3b18002e Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/bizui.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/buhaoyisi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/buhaoyisi.png new file mode 100644 index 000000000..54c4b3f71 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/buhaoyisi.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/bukesiyi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/bukesiyi.png new file mode 100644 index 000000000..5f272e3e4 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/bukesiyi.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/dajing.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/dajing.png new file mode 100644 index 000000000..8649727ec Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/dajing.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/danao.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/danao.png new file mode 100644 index 000000000..aa85a2947 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/danao.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/daxiao.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/daxiao.png new file mode 100644 index 000000000..26206bc05 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/daxiao.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/dianzan.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/dianzan.png new file mode 100644 index 000000000..2e7f00eba Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/dianzan.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/emo.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/emo.png new file mode 100644 index 000000000..9c8455165 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/emo.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/esi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/esi.png new file mode 100644 index 000000000..84e9726f1 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/esi.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/fadai.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fadai.png new file mode 100644 index 000000000..0772de262 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fadai.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/fankun.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fankun.png new file mode 100644 index 000000000..6e18dac3e Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fankun.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/feiwen.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/feiwen.png new file mode 100644 index 000000000..be9761658 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/feiwen.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/fennu.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fennu.png new file mode 100644 index 000000000..20c57338c Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fennu.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganga.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganga.png new file mode 100644 index 000000000..30ec329d2 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganga.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganmao.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganmao.png new file mode 100644 index 000000000..35bbb89f3 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganmao.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/hanyan.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/hanyan.png new file mode 100644 index 000000000..a0bc838b1 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/hanyan.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/haochi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/haochi.png new file mode 100644 index 000000000..2e52b6bee Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/haochi.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/hongxin.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/hongxin.png new file mode 100644 index 000000000..65b5de8f0 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/hongxin.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/huaixiao.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/huaixiao.png new file mode 100644 index 000000000..bc0e76c4c Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/huaixiao.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingkong.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingkong.png new file mode 100644 index 000000000..7aa65845f Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingkong.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingshu.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingshu.png new file mode 100644 index 000000000..0e984d68d Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingshu.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingya.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingya.png new file mode 100644 index 000000000..9ba6bab32 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingya.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/kaixin.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/kaixin.png new file mode 100644 index 000000000..29c9f5ddb Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/kaixin.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/keai.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/keai.png new file mode 100644 index 000000000..d3b582c69 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/keai.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/keshui.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/keshui.png new file mode 100644 index 000000000..cef489ea4 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/keshui.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/kun.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/kun.png new file mode 100644 index 000000000..1ddc388a6 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/kun.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/lengku.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/lengku.png new file mode 100644 index 000000000..c5c6feebb Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/lengku.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/liuhan.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liuhan.png new file mode 100644 index 000000000..e6ddc6f4d Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liuhan.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/liukoushui.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liukoushui.png new file mode 100644 index 000000000..3e2fba656 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liukoushui.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/liulei.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liulei.png new file mode 100644 index 000000000..dbf820404 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liulei.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/mengbi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/mengbi.png new file mode 100644 index 000000000..a4206eefb Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/mengbi.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/mianwubiaoqing.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/mianwubiaoqing.png new file mode 100644 index 000000000..6f315b98e Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/mianwubiaoqing.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/nanguo.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/nanguo.png new file mode 100644 index 000000000..19b9fb94a Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/nanguo.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/outu.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/outu.png new file mode 100644 index 000000000..2f9a06d63 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/outu.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/picture.svg b/apps/web-ele/src/views/mall/promotion/kefu/asserts/picture.svg new file mode 100644 index 000000000..8811d4957 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/asserts/picture.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/shengqi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/shengqi.png new file mode 100644 index 000000000..7dce41dc9 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/shengqi.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/shuizhuo.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/shuizhuo.png new file mode 100644 index 000000000..97d0f0a67 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/shuizhuo.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/tianshi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/tianshi.png new file mode 100644 index 000000000..eb922dd7a Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/tianshi.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaodiaoya.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaodiaoya.png new file mode 100644 index 000000000..29fbc0e19 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaodiaoya.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaoku.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaoku.png new file mode 100644 index 000000000..88a169d4f Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaoku.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/xinsui.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xinsui.png new file mode 100644 index 000000000..a0f572a11 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xinsui.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiong.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiong.png new file mode 100644 index 000000000..43dfd7090 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiong.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/yiwen.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/yiwen.png new file mode 100644 index 000000000..4c0da7095 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/yiwen.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/yun.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/yun.png new file mode 100644 index 000000000..56e5d0218 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/yun.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/ziya.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ziya.png new file mode 100644 index 000000000..593ef5e68 Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ziya.png differ diff --git a/apps/web-ele/src/views/mall/promotion/kefu/index.vue b/apps/web-ele/src/views/mall/promotion/kefu/index.vue new file mode 100644 index 000000000..096cb0cd2 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/index.vue @@ -0,0 +1,122 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/conversation-list.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/conversation-list.vue new file mode 100644 index 000000000..9f22f84b6 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/conversation-list.vue @@ -0,0 +1,250 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/member/member-info.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/member-info.vue new file mode 100644 index 000000000..4b87f377a --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/member-info.vue @@ -0,0 +1,224 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/member/order-browsing-history.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/order-browsing-history.vue new file mode 100644 index 000000000..a7a5fa65c --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/order-browsing-history.vue @@ -0,0 +1,47 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/member/product-browsing-history.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/product-browsing-history.vue new file mode 100644 index 000000000..c35d87bd9 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/product-browsing-history.vue @@ -0,0 +1,58 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/message-list.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/message-list.vue new file mode 100644 index 000000000..633187084 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/message-list.vue @@ -0,0 +1,429 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/message/message-item.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/message-item.vue new file mode 100644 index 000000000..7c8e3153b --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/message-item.vue @@ -0,0 +1,25 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/message/order-item.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/order-item.vue new file mode 100644 index 000000000..ff0ecd986 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/order-item.vue @@ -0,0 +1,121 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/message/product-item.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/product-item.vue new file mode 100644 index 000000000..da580820a --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/product-item.vue @@ -0,0 +1,73 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/constants.ts b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/constants.ts new file mode 100644 index 000000000..266a6cf02 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/constants.ts @@ -0,0 +1,17 @@ +/** 客服消息类型枚举类 */ +export const KeFuMessageContentTypeEnum = { + TEXT: 1, // 文本消息 + IMAGE: 2, // 图片消息 + VOICE: 3, // 语音消息 + VIDEO: 4, // 视频消息 + SYSTEM: 5, // 系统消息 + // ========== 商城特殊消息 ========== + PRODUCT: 10, // 商品消息 + ORDER: 11, // 订单消息" +}; + +/** Promotion 的 WebSocket 消息类型枚举类 */ +export const WebSocketMessageTypeConstants = { + KEFU_MESSAGE_TYPE: 'kefu_message_type', // 客服消息类型 + KEFU_MESSAGE_ADMIN_READ: 'kefu_message_read_status_change', // 客服消息管理员已读 +}; diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji-select-popover.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji-select-popover.vue new file mode 100644 index 000000000..2d14ec189 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji-select-popover.vue @@ -0,0 +1,53 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji.ts b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji.ts new file mode 100644 index 000000000..f1bd19319 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji.ts @@ -0,0 +1,126 @@ +import { onMounted, ref } from 'vue'; + +import { isEmpty } from '@vben/utils'; + +const emojiList = [ + { name: '[笑掉牙]', file: 'xiaodiaoya.png' }, + { name: '[可爱]', file: 'keai.png' }, + { name: '[冷酷]', file: 'lengku.png' }, + { name: '[闭嘴]', file: 'bizui.png' }, + { name: '[生气]', file: 'shengqi.png' }, + { name: '[惊恐]', file: 'jingkong.png' }, + { name: '[瞌睡]', file: 'keshui.png' }, + { name: '[大笑]', file: 'daxiao.png' }, + { name: '[爱心]', file: 'aixin.png' }, + { name: '[坏笑]', file: 'huaixiao.png' }, + { name: '[飞吻]', file: 'feiwen.png' }, + { name: '[疑问]', file: 'yiwen.png' }, + { name: '[开心]', file: 'kaixin.png' }, + { name: '[发呆]', file: 'fadai.png' }, + { name: '[流泪]', file: 'liulei.png' }, + { name: '[汗颜]', file: 'hanyan.png' }, + { name: '[惊悚]', file: 'jingshu.png' }, + { name: '[困~]', file: 'kun.png' }, + { name: '[心碎]', file: 'xinsui.png' }, + { name: '[天使]', file: 'tianshi.png' }, + { name: '[晕]', file: 'yun.png' }, + { name: '[啊]', file: 'a.png' }, + { name: '[愤怒]', file: 'fennu.png' }, + { name: '[睡着]', file: 'shuizhuo.png' }, + { name: '[面无表情]', file: 'mianwubiaoqing.png' }, + { name: '[难过]', file: 'nanguo.png' }, + { name: '[犯困]', file: 'fankun.png' }, + { name: '[好吃]', file: 'haochi.png' }, + { name: '[呕吐]', file: 'outu.png' }, + { name: '[龇牙]', file: 'ziya.png' }, + { name: '[懵比]', file: 'mengbi.png' }, + { name: '[白眼]', file: 'baiyan.png' }, + { name: '[饿死]', file: 'esi.png' }, + { name: '[凶]', file: 'xiong.png' }, + { name: '[感冒]', file: 'ganmao.png' }, + { name: '[流汗]', file: 'liuhan.png' }, + { name: '[笑哭]', file: 'xiaoku.png' }, + { name: '[流口水]', file: 'liukoushui.png' }, + { name: '[尴尬]', file: 'ganga.png' }, + { name: '[惊讶]', file: 'jingya.png' }, + { name: '[大惊]', file: 'dajing.png' }, + { name: '[不好意思]', file: 'buhaoyisi.png' }, + { name: '[大闹]', file: 'danao.png' }, + { name: '[不可思议]', file: 'bukesiyi.png' }, + { name: '[爱你]', file: 'aini.png' }, + { name: '[红心]', file: 'hongxin.png' }, + { name: '[点赞]', file: 'dianzan.png' }, + { name: '[恶魔]', file: 'emo.png' }, +]; + +export interface Emoji { + name: string; + url: string; +} + +export function useEmoji() { + const emojiPathList = ref([]); + + /** 加载本地图片 */ + async function initStaticEmoji() { + const pathList = import.meta.glob('../../asserts/*.{png,jpg,jpeg,svg}'); + for (const path in pathList) { + const imageModule: any = await pathList[path]?.(); + emojiPathList.value.push({ path, src: imageModule.default }); + } + } + + /** 初始化 */ + onMounted(async () => { + if (isEmpty(emojiPathList.value)) { + await initStaticEmoji(); + } + }); + + /** + * 将文本中的表情替换成图片 + * + * @return 替换后的文本 + * @param content 消息内容 + */ + function replaceEmoji(content: string) { + let newData = content; + if (typeof newData !== 'object') { + const reg = /\[(.+?)\]/g; // [] 中括号 + const zhEmojiName = newData.match(reg); + if (zhEmojiName) { + zhEmojiName.forEach((item) => { + const emojiFile = getEmojiFileByName(item); + newData = newData.replace( + item, + ``, + ); + }); + } + } + return newData; + } + + /** 获得所有表情 */ + function getEmojiList(): Emoji[] { + return emojiList.map((item) => ({ + url: getEmojiFileByName(item.name), + name: item.name, + })) as Emoji[]; + } + + function getEmojiFileByName(name: string) { + for (const emoji of emojiList) { + if (emoji.name === name) { + const emojiPath = emojiPathList.value.find( + (item: { path: string; src: string }) => + item.path.includes(emoji.file), + ); + return emojiPath ? emojiPath.src : undefined; + } + } + return false; + } + + return { replaceEmoji, getEmojiList }; +} diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/picture-select-upload.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/picture-select-upload.vue new file mode 100644 index 000000000..b991f04a8 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/picture-select-upload.vue @@ -0,0 +1,90 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/point/activity/data.ts b/apps/web-ele/src/views/mall/promotion/point/activity/data.ts new file mode 100644 index 000000000..dcb750cbb --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/point/activity/data.ts @@ -0,0 +1,143 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +/** 表单配置 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'spuId', + label: '积分商城活动商品', + component: 'Input', + componentProps: { + placeholder: '请选择商品', + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入排序', + min: 0, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'status', + label: '活动状态', + component: 'Select', + componentProps: { + placeholder: '请选择活动状态', + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '活动编号', + minWidth: 80, + }, + { + field: 'picUrl', + title: '商品图片', + minWidth: 80, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'spuName', + title: '商品标题', + minWidth: 300, + }, + { + field: 'marketPrice', + title: '原价', + minWidth: 100, + formatter: 'formatFenToYuanAmount', + }, + { + field: 'point', + title: '兑换积分', + minWidth: 100, + }, + { + field: 'price', + title: '兑换金额', + minWidth: 100, + formatter: 'formatFenToYuanAmount', + }, + { + field: 'status', + title: '活动状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'stock', + title: '库存', + minWidth: 80, + }, + { + field: 'totalStock', + title: '总库存', + minWidth: 80, + }, + { + field: 'redeemedQuantity', + title: '已兑换数量', + minWidth: 100, + slots: { default: 'redeemedQuantity' }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/point/activity/index.vue b/apps/web-ele/src/views/mall/promotion/point/activity/index.vue new file mode 100644 index 000000000..4fcebfad2 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/point/activity/index.vue @@ -0,0 +1,166 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/point/activity/modules/form.vue b/apps/web-ele/src/views/mall/promotion/point/activity/modules/form.vue new file mode 100644 index 000000000..5bded7559 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/point/activity/modules/form.vue @@ -0,0 +1,105 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/point/components/index.ts b/apps/web-ele/src/views/mall/promotion/point/components/index.ts new file mode 100644 index 000000000..bb0194057 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/point/components/index.ts @@ -0,0 +1 @@ +export { default as PointShowcase } from './showcase.vue'; diff --git a/apps/web-ele/src/views/mall/promotion/point/components/showcase.vue b/apps/web-ele/src/views/mall/promotion/point/components/showcase.vue new file mode 100644 index 000000000..f1bbe2a59 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/point/components/showcase.vue @@ -0,0 +1,149 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/point/components/table-select.vue b/apps/web-ele/src/views/mall/promotion/point/components/table-select.vue new file mode 100644 index 000000000..eff545cba --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/point/components/table-select.vue @@ -0,0 +1,239 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts b/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts new file mode 100644 index 000000000..410941928 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/rewardActivity/data.ts @@ -0,0 +1,169 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +/** 表单配置 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '活动名称', + component: 'Input', + componentProps: { + placeholder: '请输入活动名称', + }, + rules: 'required', + }, + { + fieldName: 'startTime', + label: '开始时间', + component: 'DatePicker', + componentProps: { + placeholder: '请选择开始时间', + showTime: true, + valueFormat: 'x', + format: 'YYYY-MM-DD HH:mm:ss', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'endTime', + label: '结束时间', + component: 'DatePicker', + componentProps: { + placeholder: '请选择结束时间', + showTime: true, + valueFormat: 'x', + format: 'YYYY-MM-DD HH:mm:ss', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'conditionType', + label: '条件类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.PROMOTION_CONDITION_TYPE, 'number'), + }, + rules: 'required', + }, + { + fieldName: 'productScope', + label: '商品范围', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.PROMOTION_PRODUCT_SCOPE, 'number'), + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + rows: 4, + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '活动名称', + component: 'Input', + componentProps: { + placeholder: '请输入活动名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '活动状态', + component: 'Select', + componentProps: { + placeholder: '请选择活动状态', + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '活动时间', + component: 'RangePicker', + componentProps: { + placeholder: ['活动开始日期', '活动结束日期'], + clearable: true, + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '活动名称', + minWidth: 140, + }, + { + field: 'productScope', + title: '活动范围', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PROMOTION_PRODUCT_SCOPE }, + }, + }, + { + field: 'startTime', + title: '活动开始时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'endTime', + title: '活动结束时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/rewardActivity/index.vue b/apps/web-ele/src/views/mall/promotion/rewardActivity/index.vue new file mode 100644 index 000000000..3a148d167 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/rewardActivity/index.vue @@ -0,0 +1,157 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue b/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue new file mode 100644 index 000000000..e1c74b702 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/rewardActivity/modules/form.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/seckill/activity/data.ts b/apps/web-ele/src/views/mall/promotion/seckill/activity/data.ts new file mode 100644 index 000000000..b27413519 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/activity/data.ts @@ -0,0 +1,127 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '活动名称', + component: 'Input', + componentProps: { + placeholder: '请输入活动名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '活动状态', + component: 'Select', + componentProps: { + placeholder: '请选择活动状态', + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '活动编号', + minWidth: 80, + }, + { + field: 'name', + title: '活动名称', + minWidth: 140, + }, + { + field: 'configIds', + title: '秒杀时段', + minWidth: 220, + slots: { default: 'configIds' }, + }, + { + field: 'startTime', + title: '活动时间', + minWidth: 210, + slots: { default: 'timeRange' }, + }, + { + field: 'picUrl', + title: '商品图片', + minWidth: 80, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'spuName', + title: '商品标题', + minWidth: 300, + }, + { + field: 'marketPrice', + title: '原价', + minWidth: 100, + formatter: ({ row }) => `¥${(row.marketPrice / 100).toFixed(2)}`, + }, + { + field: 'seckillPrice', + title: '秒杀价', + minWidth: 100, + formatter: ({ row }) => { + if (!(row.products || row.products.length === 0)) { + return '¥0.00'; + } + const seckillPrice = Math.min( + ...row.products.map((item: any) => item.seckillPrice), + ); + return `¥${(seckillPrice / 100).toFixed(2)}`; + }, + }, + { + field: 'status', + title: '活动状态', + align: 'center', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'stock', + title: '库存', + align: 'center', + minWidth: 80, + }, + { + field: 'totalStock', + title: '总库存', + align: 'center', + minWidth: 80, + }, + { + field: 'createTime', + title: '创建时间', + align: 'center', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + align: 'center', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/seckill/activity/index.vue b/apps/web-ele/src/views/mall/promotion/seckill/activity/index.vue new file mode 100644 index 000000000..ea9d01761 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/activity/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/seckill/activity/modules/form.vue b/apps/web-ele/src/views/mall/promotion/seckill/activity/modules/form.vue new file mode 100644 index 000000000..3de767ce9 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/activity/modules/form.vue @@ -0,0 +1,121 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/seckill/components/index.ts b/apps/web-ele/src/views/mall/promotion/seckill/components/index.ts new file mode 100644 index 000000000..dd452161c --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/components/index.ts @@ -0,0 +1 @@ +export { default as SeckillShowcase } from './showcase.vue'; diff --git a/apps/web-ele/src/views/mall/promotion/seckill/components/showcase.vue b/apps/web-ele/src/views/mall/promotion/seckill/components/showcase.vue new file mode 100644 index 000000000..5ee5fe6d3 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/components/showcase.vue @@ -0,0 +1,148 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/seckill/components/table-select.vue b/apps/web-ele/src/views/mall/promotion/seckill/components/table-select.vue new file mode 100644 index 000000000..ec3315b1a --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/components/table-select.vue @@ -0,0 +1,277 @@ + + + + diff --git a/apps/web-ele/src/views/mall/promotion/seckill/config/data.ts b/apps/web-ele/src/views/mall/promotion/seckill/config/data.ts new file mode 100644 index 000000000..85a7bf4cc --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/config/data.ts @@ -0,0 +1,153 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MallSeckillConfigApi } from '#/api/mall/promotion/seckill/seckillConfig'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '秒杀时段名称', + component: 'Input', + componentProps: { + placeholder: '请输入秒杀时段名称', + }, + rules: 'required', + }, + { + fieldName: 'startTime', + label: '开始时间点', + component: 'TimePicker', + componentProps: { + format: 'HH:mm', + valueFormat: 'HH:mm', + placeholder: '请选择开始时间点', + }, + rules: 'required', + }, + { + fieldName: 'endTime', + label: '结束时间点', + component: 'TimePicker', + componentProps: { + format: 'HH:mm', + valueFormat: 'HH:mm', + placeholder: '请选择结束时间点', + }, + rules: 'required', + }, + { + fieldName: 'sliderPicUrls', + label: '秒杀轮播图', + component: 'ImageUpload', + componentProps: { + multiple: true, + maxNumber: 5, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '秒杀时段名称', + component: 'Input', + componentProps: { + placeholder: '请输入秒杀时段名称', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns( + onStatusChange?: ( + newStatus: number, + row: MallSeckillConfigApi.SeckillConfig, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + title: '秒杀时段名称', + field: 'name', + minWidth: 200, + }, + { + title: '开始时间点', + field: 'startTime', + minWidth: 120, + }, + { + title: '结束时间点', + field: 'endTime', + minWidth: 120, + }, + { + title: '秒杀轮播图', + field: 'sliderPicUrls', + minWidth: 100, + cellRender: { + name: 'CellImages', + }, + }, + { + title: '活动状态', + field: 'status', + minWidth: 100, + cellRender: { + attrs: { beforeChange: onStatusChange }, + name: 'CellSwitch', + props: { + checkedValue: 1, + checkedChildren: '启用', + unCheckedValue: 0, + unCheckedChildren: '禁用', + }, + }, + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/promotion/seckill/config/index.vue b/apps/web-ele/src/views/mall/promotion/seckill/config/index.vue new file mode 100644 index 000000000..073c763c2 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/config/index.vue @@ -0,0 +1,152 @@ + + + diff --git a/apps/web-ele/src/views/mall/promotion/seckill/config/modules/form.vue b/apps/web-ele/src/views/mall/promotion/seckill/config/modules/form.vue new file mode 100644 index 000000000..36aaf29b9 --- /dev/null +++ b/apps/web-ele/src/views/mall/promotion/seckill/config/modules/form.vue @@ -0,0 +1,90 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/member/index.vue b/apps/web-ele/src/views/mall/statistics/member/index.vue new file mode 100644 index 000000000..1e3f9ddbe --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/member/index.vue @@ -0,0 +1,113 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/area-card.vue b/apps/web-ele/src/views/mall/statistics/member/modules/area-card.vue new file mode 100644 index 000000000..7c713851a --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/member/modules/area-card.vue @@ -0,0 +1,101 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/area-chart-options.ts b/apps/web-ele/src/views/mall/statistics/member/modules/area-chart-options.ts new file mode 100644 index 000000000..b09f492f0 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/member/modules/area-chart-options.ts @@ -0,0 +1,130 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MallMemberStatisticsApi } from '#/api/mall/statistics/member'; + +import { fenToYuan } from '@vben/utils'; + +/** 会员地域分布图表配置 */ +export function getAreaChartOptions( + data: MallMemberStatisticsApi.AreaStatisticsRespVO[], +): any { + if (!data || data.length === 0) { + return { + title: { + text: '暂无数据', + left: 'center', + top: 'center', + textStyle: { + color: '#999', + fontSize: 14, + }, + }, + }; + } + + // 计算 min 和 max 值 + let min = Number.POSITIVE_INFINITY; + let max = Number.NEGATIVE_INFINITY; + const mapData = data.map((item) => { + const payUserCount = item.orderPayUserCount || 0; + min = Math.min(min, payUserCount); + max = Math.max(max, payUserCount); + return { + ...item, + name: item.areaName, + value: payUserCount, + }; + }); + // 如果所有值都为 0,设置合理的 min 和 max 值 + if (min === max && min === 0) { + min = 0; + max = 10; + } + + // 返回图表配置 + return { + tooltip: { + trigger: 'item', + formatter: (params: any) => { + const itemData = params?.data; + if (!itemData) { + return `${params?.name || ''}
暂无数据`; + } + return `${itemData.areaName || params.name}
+会员数量:${itemData.userCount || 0}
+订单创建数量:${itemData.orderCreateUserCount || 0}
+订单支付数量:${itemData.orderPayUserCount || 0}
+订单支付金额:¥${Number(fenToYuan(itemData.orderPayPrice || 0)).toFixed(2)}`; + }, + }, + visualMap: { + text: ['高', '低'], + realtime: false, + calculable: true, + top: 'middle', + left: 10, + min, + max, + inRange: { + color: ['#e6f3ff', '#1890ff', '#0050b3'], + }, + }, + series: [ + { + name: '会员地域分布', + type: 'map', + map: 'china', + roam: false, + selectedMode: false, + itemStyle: { + borderColor: '#389e0d', + borderWidth: 0.5, + }, + emphasis: { + itemStyle: { + areaColor: '#ffec3d', + borderWidth: 1, + }, + }, + data: mapData, + }, + ], + }; +} + +/** VXE Grid 表格列配置 */ +export function getAreaTableColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'areaName', + title: '省份', + minWidth: 80, + sortable: true, + showOverflow: 'tooltip', + }, + { + field: 'userCount', + title: '会员数量', + minWidth: 100, + sortable: true, + }, + { + field: 'orderCreateUserCount', + title: '订单创建数量', + minWidth: 120, + sortable: true, + }, + { + field: 'orderPayUserCount', + title: '订单支付数量', + minWidth: 120, + sortable: true, + }, + { + field: 'orderPayPrice', + title: '订单支付金额', + minWidth: 120, + sortable: true, + formatter: 'formatFenToYuanAmount', + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/funnel-card.vue b/apps/web-ele/src/views/mall/statistics/member/modules/funnel-card.vue new file mode 100644 index 000000000..845fd8502 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/member/modules/funnel-card.vue @@ -0,0 +1,147 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/sex-card.vue b/apps/web-ele/src/views/mall/statistics/member/modules/sex-card.vue new file mode 100644 index 000000000..b646619c6 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/member/modules/sex-card.vue @@ -0,0 +1,65 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/sex-chart-options.ts b/apps/web-ele/src/views/mall/statistics/member/modules/sex-chart-options.ts new file mode 100644 index 000000000..81a7faf87 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/member/modules/sex-chart-options.ts @@ -0,0 +1,28 @@ +/** 会员性别比例图表配置 */ +export function getSexChartOptions(data: any[]): any { + return { + tooltip: { + trigger: 'item', + confine: true, + formatter: '{a}
{b} : {c} ({d}%)', + }, + legend: { + orient: 'vertical', + left: 'right', + }, + series: [ + { + name: '会员性别', + type: 'pie', + roseType: 'area', + label: { + show: false, + }, + labelLine: { + show: false, + }, + data, + }, + ], + }; +} diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/terminal-card.vue b/apps/web-ele/src/views/mall/statistics/member/modules/terminal-card.vue new file mode 100644 index 000000000..50c088c19 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/member/modules/terminal-card.vue @@ -0,0 +1,59 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/member/modules/terminal-chart-options.ts b/apps/web-ele/src/views/mall/statistics/member/modules/terminal-chart-options.ts new file mode 100644 index 000000000..c3233bdd1 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/member/modules/terminal-chart-options.ts @@ -0,0 +1,27 @@ +/** 会员终端统计图配置 */ +export function getTerminalChartOptions(data: any[]): any { + return { + tooltip: { + trigger: 'item', + confine: true, + formatter: '{a}
{b} : {c} ({d}%)', + }, + legend: { + orient: 'vertical', + left: 'right', + }, + series: [ + { + name: '会员终端', + type: 'pie', + label: { + show: false, + }, + labelLine: { + show: false, + }, + data, + }, + ], + }; +} diff --git a/apps/web-ele/src/views/mall/statistics/product/index.vue b/apps/web-ele/src/views/mall/statistics/product/index.vue new file mode 100644 index 000000000..2e3834059 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/product/index.vue @@ -0,0 +1,27 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/product/modules/rank-card.vue b/apps/web-ele/src/views/mall/statistics/product/modules/rank-card.vue new file mode 100644 index 000000000..0a7e34e74 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/product/modules/rank-card.vue @@ -0,0 +1,138 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/product/modules/summary-card.vue b/apps/web-ele/src/views/mall/statistics/product/modules/summary-card.vue new file mode 100644 index 000000000..5d6b9bffe --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/product/modules/summary-card.vue @@ -0,0 +1,261 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/product/modules/summary-chart-options.ts b/apps/web-ele/src/views/mall/statistics/product/modules/summary-chart-options.ts new file mode 100644 index 000000000..ec6e474e1 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/product/modules/summary-chart-options.ts @@ -0,0 +1,129 @@ +/** 商品统计折线图配置 */ +export function getProductSummaryChartOptions(data: any[]): any { + // 处理数据:将金额从分转换为元 + const processedData = data.map((item) => ({ + ...item, + orderPayPrice: Number((item.orderPayPrice / 100).toFixed(2)), + afterSaleRefundPrice: Number((item.afterSaleRefundPrice / 100).toFixed(2)), + })); + + return { + dataset: { + dimensions: [ + 'time', + 'browseCount', + 'browseUserCount', + 'orderPayPrice', + 'afterSaleRefundPrice', + ], + source: processedData, + }, + grid: { + left: 20, + right: 20, + bottom: 20, + top: 80, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '商品浏览量', + type: 'line', + smooth: true, + itemStyle: { color: '#B37FEB' }, + }, + { + name: '商品访客数', + type: 'line', + smooth: true, + itemStyle: { color: '#FFAB2B' }, + }, + { + name: '支付金额', + type: 'bar', + smooth: true, + yAxisIndex: 1, + itemStyle: { color: '#1890FF' }, + }, + { + name: '退款金额', + type: 'bar', + smooth: true, + yAxisIndex: 1, + itemStyle: { color: '#00C050' }, + }, + ], + toolbox: { + feature: { + // 数据区域缩放 + dataZoom: { + yAxisIndex: false, // Y轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { + show: true, + name: '商品状况', + }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + }, + padding: [5, 10], + }, + xAxis: { + type: 'category', + boundaryGap: true, + axisTick: { + show: false, + }, + }, + yAxis: [ + { + type: 'value', + name: '金额', + axisLine: { + show: false, + }, + axisTick: { + show: false, + }, + axisLabel: { + color: '#7F8B9C', + }, + splitLine: { + show: true, + lineStyle: { + color: '#F5F7F9', + }, + }, + }, + { + type: 'value', + name: '数量', + axisLine: { + show: false, + }, + axisTick: { + show: false, + }, + axisLabel: { + color: '#7F8B9C', + }, + splitLine: { + show: true, + lineStyle: { + color: '#F5F7F9', + }, + }, + }, + ], + }; +} diff --git a/apps/web-ele/src/views/mall/statistics/trade/index.vue b/apps/web-ele/src/views/mall/statistics/trade/index.vue new file mode 100644 index 000000000..0207bde48 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/trade/index.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/trade/modules/trend-card.vue b/apps/web-ele/src/views/mall/statistics/trade/modules/trend-card.vue new file mode 100644 index 000000000..8dd3f2759 --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/trade/modules/trend-card.vue @@ -0,0 +1,293 @@ + + + diff --git a/apps/web-ele/src/views/mall/statistics/trade/modules/trend-chart-options.ts b/apps/web-ele/src/views/mall/statistics/trade/modules/trend-chart-options.ts new file mode 100644 index 000000000..899a6a32a --- /dev/null +++ b/apps/web-ele/src/views/mall/statistics/trade/modules/trend-chart-options.ts @@ -0,0 +1,124 @@ +import type { MallTradeStatisticsApi } from '#/api/mall/statistics/trade'; + +import { fenToYuan } from '@vben/utils'; + +/** 交易趋势折线图配置 */ +export function getTradeTrendChartOptions( + data: MallTradeStatisticsApi.TradeTrendSummaryRespVO[], +): any { + // 处理数据:将分转换为元 + const processedData = data.map((item) => ({ + ...item, + turnoverPrice: Number(fenToYuan(item.turnoverPrice)), + orderPayPrice: Number(fenToYuan(item.orderPayPrice)), + rechargePrice: Number(fenToYuan(item.rechargePrice)), + expensePrice: Number(fenToYuan(item.expensePrice)), + })); + + return { + dataset: { + dimensions: [ + 'date', + 'turnoverPrice', + 'orderPayPrice', + 'rechargePrice', + 'expensePrice', + ], + source: processedData, + }, + grid: { + left: 20, + right: 20, + bottom: 20, + top: 80, + containLabel: true, + }, + legend: { + top: 50, + }, + series: [ + { + name: '营业额', + type: 'line', + smooth: true, + itemStyle: { color: '#1890FF' }, + }, + { + name: '商品支付金额', + type: 'line', + smooth: true, + itemStyle: { color: '#722ED1' }, + }, + { + name: '充值金额', + type: 'line', + smooth: true, + itemStyle: { color: '#FAAD14' }, + }, + { + name: '支出金额', + type: 'line', + smooth: true, + itemStyle: { color: '#52C41A' }, + }, + ], + toolbox: { + feature: { + // 数据区域缩放 + dataZoom: { + yAxisIndex: false, // Y轴不缩放 + }, + brush: { + type: ['lineX', 'clear'], // 区域缩放按钮、还原按钮 + }, + saveAsImage: { + show: true, + name: '交易状况', + }, // 保存为图片 + }, + }, + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + }, + padding: [5, 10], + formatter(params: any) { + let result = `
${params[0].data.time}
`; + params.forEach((item: any) => { + result += `
+ + ${item.seriesName}: ¥${item.data[item.dimensionNames[item.encode.y[0]]]} +
`; + }); + return result; + }, + }, + xAxis: { + type: 'category', + boundaryGap: false, + axisTick: { + show: false, + }, + }, + yAxis: { + type: 'value', + axisLine: { + show: false, + }, + axisTick: { + show: false, + }, + axisLabel: { + formatter: '¥{value}', + color: '#7F8B9C', + }, + splitLine: { + show: true, + lineStyle: { + color: '#F5F7F9', + }, + }, + }, + }; +} diff --git a/apps/web-ele/src/views/mall/trade/afterSale/data.ts b/apps/web-ele/src/views/mall/trade/afterSale/data.ts new file mode 100644 index 000000000..2aa4ecc69 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/afterSale/data.ts @@ -0,0 +1,172 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 拒绝售后表单的 schema 配置 */ +export function useDisagreeFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Textarea', + fieldName: 'reason', + label: '拒绝原因', + componentProps: { + placeholder: '请输入拒绝原因', + rows: 4, + }, + rules: z.string().min(2, { message: '拒绝原因不能少于 2 个字符' }), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'spuName', + label: '商品名称', + component: 'Input', + componentProps: { + placeholder: '请输入商品名称', + }, + }, + { + fieldName: 'no', + label: '退款编号', + component: 'Input', + componentProps: { + placeholder: '请输入退款编号', + }, + }, + { + fieldName: 'orderNo', + label: '订单编号', + component: 'Input', + componentProps: { + placeholder: '请输入订单编号', + }, + }, + { + fieldName: 'status', + label: '售后状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.TRADE_AFTER_SALE_STATUS, 'number'), + placeholder: '请选择售后状态', + clearable: true, + }, + }, + { + fieldName: 'way', + label: '售后方式', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.TRADE_AFTER_SALE_WAY, 'number'), + placeholder: '请选择售后方式', + clearable: true, + }, + }, + { + fieldName: 'type', + label: '售后类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.TRADE_AFTER_SALE_TYPE, 'number'), + placeholder: '请选择售后类型', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeGridPropTypes.Columns { + return [ + { + field: 'no', + title: '退款编号', + fixed: 'left', + minWidth: 200, + }, + { + field: 'orderNo', + title: '订单编号', + fixed: 'left', + minWidth: 200, + slots: { default: 'orderNo' }, + }, + { + field: 'productInfo', + title: '商品信息', + minWidth: 600, + slots: { default: 'productInfo' }, + }, + { + field: 'refundPrice', + title: '订单金额', + width: 120, + formatter: 'formatAmount2', + }, + { + field: 'user.nickname', + title: '买家', + minWidth: 120, + }, + { + field: 'createTime', + title: '申请时间', + width: 180, + formatter: 'formatDateTime', + }, + { + field: 'status', + title: '售后状态', + width: 100, + cellRender: { + name: 'CellDict', + props: { + type: DICT_TYPE.TRADE_AFTER_SALE_STATUS, + }, + }, + }, + { + field: 'way', + title: '售后方式', + width: 100, + cellRender: { + name: 'CellDict', + props: { + type: DICT_TYPE.TRADE_AFTER_SALE_WAY, + }, + }, + }, + { + title: '操作', + width: 160, + fixed: 'right', + align: 'center', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/afterSale/detail/data.ts b/apps/web-ele/src/views/mall/trade/afterSale/detail/data.ts new file mode 100644 index 000000000..1e532c935 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/afterSale/detail/data.ts @@ -0,0 +1,221 @@ +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { fenToYuan, formatDate } from '@vben/utils'; + +import { ElImage } from 'element-plus'; + +import { DictTag } from '#/components/dict-tag'; + +/** 订单信息 schema */ +export function useOrderInfoSchema(): DescriptionItemSchema[] { + return [ + { + field: 'orderNo', + label: '订单号', + }, + { + field: 'order.deliveryType', + label: '配送方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TRADE_DELIVERY_TYPE, + value: val, + }), + }, + { + field: 'order.type', + label: '订单类型', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TRADE_ORDER_TYPE, + value: val, + }), + }, + { + field: 'order.receiverName', + label: '收货人', + }, + { + field: 'order.userRemark', + label: '买家留言', + }, + { + field: 'order.terminal', + label: '订单来源', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TERMINAL, + value: val, + }), + }, + { + field: 'order.receiverMobile', + label: '联系电话', + }, + { + field: 'order.remark', + label: '商家备注', + }, + { + field: 'order.payOrderId', + label: '支付单号', + }, + { + field: 'order.payChannelCode', + label: '付款方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_CHANNEL_CODE, + value: val, + }), + }, + { + field: 'user.nickname', + label: '买家', + }, + ]; +} + +/** 售后信息 schema */ +export function useAfterSaleInfoSchema(): DescriptionItemSchema[] { + return [ + { + field: 'no', + label: '退款编号', + }, + { + field: 'auditTime', + label: '申请时间', + render: (val) => formatDate(val) as string, + }, + { + field: 'type', + label: '售后类型', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TRADE_AFTER_SALE_TYPE, + value: val, + }), + }, + { + field: 'way', + label: '售后方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TRADE_AFTER_SALE_WAY, + value: val, + }), + }, + { + field: 'refundPrice', + label: '退款金额', + render: (val) => fenToYuan(val ?? 0), + }, + { + field: 'applyReason', + label: '退款原因', + }, + { + field: 'applyDescription', + label: '补充描述', + }, + { + field: 'applyPicUrls', + label: '凭证图片', + render: (val) => { + const images = val || []; + return h( + 'div', + { class: 'flex gap-10px' }, + images.map((url: string, index: number) => + h(ElImage, { + key: index, + src: url, + width: 60, + height: 60, + }), + ), + ); + }, + }, + ]; +} + +/** 退款状态 schema */ +export function useRefundStatusSchema(): DescriptionItemSchema[] { + return [ + { + field: 'status', + label: '退款状态', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TRADE_AFTER_SALE_STATUS, + value: val, + }), + }, + { + field: 'reminder', + label: '提醒', + render: () => + h('div', { class: 'text-red-500 mb-10px' }, [ + h('div', '如果未发货,请点击同意退款给买家。'), + h('div', '如果实际已发货,请主动与买家联系。'), + h('div', '如果订单整体退款后,优惠券和余额会退还给买家.'), + ]), + }, + ]; +} + +/** 商品信息 columns */ +export function useProductColumns() { + return [ + { + field: 'spuName', + title: '商品信息', + minWidth: 300, + slots: { default: 'spuName' }, + }, + { + field: 'price', + title: '商品原价', + minWidth: 150, + formatter: 'formatFenToYuanAmount', + }, + { + field: 'count', + title: '数量', + minWidth: 100, + }, + { + field: 'payPrice', + title: '合计', + minWidth: 150, + formatter: 'formatFenToYuanAmount', + }, + ]; +} + +/** 操作日志 columns */ +export function useOperateLogSchema() { + return [ + { + field: 'createTime', + title: '操作时间', + width: 180, + formatter: 'formatDateTime', + }, + { + field: 'userType', + title: '操作人', + width: 100, + slots: { default: 'userType' }, + }, + { + field: 'content', + title: '操作内容', + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/afterSale/detail/index.vue b/apps/web-ele/src/views/mall/trade/afterSale/detail/index.vue new file mode 100644 index 000000000..66d4e6bdd --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/afterSale/detail/index.vue @@ -0,0 +1,307 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/afterSale/index.vue b/apps/web-ele/src/views/mall/trade/afterSale/index.vue new file mode 100644 index 000000000..8afaae748 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/afterSale/index.vue @@ -0,0 +1,157 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/afterSale/modules/disagree-form.vue b/apps/web-ele/src/views/mall/trade/afterSale/modules/disagree-form.vue new file mode 100644 index 000000000..c9d1adf23 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/afterSale/modules/disagree-form.vue @@ -0,0 +1,78 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/brokerage/record/data.ts b/apps/web-ele/src/views/mall/trade/brokerage/record/data.ts new file mode 100644 index 000000000..a6ed265ac --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/record/data.ts @@ -0,0 +1,137 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { fenToYuan } from '@vben/utils'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + clearable: true, + }, + }, + { + fieldName: 'bizType', + label: '业务类型', + component: 'Select', + componentProps: { + placeholder: '请选择业务类型', + clearable: true, + options: getDictOptions(DICT_TYPE.BROKERAGE_RECORD_BIZ_TYPE, 'number'), + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + clearable: true, + options: getDictOptions(DICT_TYPE.BROKERAGE_RECORD_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 60, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 80, + }, + { + field: 'userAvatar', + title: '头像', + minWidth: 70, + cellRender: { + name: 'CellImage', + props: { + height: 40, + width: 40, + shape: 'circle', + }, + }, + }, + { + field: 'userNickname', + title: '昵称', + minWidth: 80, + }, + { + field: 'bizType', + title: '业务类型', + minWidth: 85, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BROKERAGE_RECORD_BIZ_TYPE }, + }, + }, + { + field: 'bizId', + title: '业务编号', + minWidth: 80, + }, + { + field: 'title', + title: '标题', + minWidth: 110, + }, + { + field: 'price', + title: '金额', + minWidth: 60, + formatter: ({ row }) => `¥${fenToYuan(row.price)}`, + }, + { + field: 'description', + title: '说明', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 85, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BROKERAGE_RECORD_STATUS }, + }, + }, + { + field: 'unfreezeTime', + title: '解冻时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '创建时间', + width: 180, + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/brokerage/record/index.vue b/apps/web-ele/src/views/mall/trade/brokerage/record/index.vue new file mode 100644 index 000000000..a95964874 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/record/index.vue @@ -0,0 +1,56 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/brokerage/user/data.ts b/apps/web-ele/src/views/mall/trade/brokerage/user/data.ts new file mode 100644 index 000000000..903935b9a --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/user/data.ts @@ -0,0 +1,367 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MallBrokerageUserApi } from '#/api/mall/trade/brokerage/user'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { fenToYuan } from '@vben/utils'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'bindUserId', + label: '推广员编号', + component: 'Input', + componentProps: { + placeholder: '请输入推广员编号', + clearable: true, + }, + }, + { + fieldName: 'brokerageEnabled', + label: '推广资格', + component: 'Select', + componentProps: { + placeholder: '请选择推广资格', + clearable: true, + options: [ + { label: '有', value: true }, + { label: '无', value: false }, + ], + }, + defaultValue: true, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onBrokerageEnabledChange?: ( + newEnabled: boolean, + row: MallBrokerageUserApi.BrokerageUser, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '用户编号', + minWidth: 80, + }, + { + field: 'avatar', + title: '头像', + minWidth: 70, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'nickname', + title: '昵称', + minWidth: 80, + }, + { + field: 'brokerageUserCount', + title: '推广人数', + minWidth: 80, + }, + { + field: 'brokerageOrderCount', + title: '推广订单数量', + minWidth: 110, + }, + { + field: 'brokerageOrderPrice', + title: '推广订单金额', + minWidth: 110, + formatter: ({ row }) => `¥${fenToYuan(row.brokerageOrderPrice)}`, + }, + { + field: 'withdrawPrice', + title: '已提现金额', + minWidth: 100, + formatter: ({ row }) => `¥${fenToYuan(row.withdrawPrice)}`, + }, + { + field: 'withdrawCount', + title: '已提现次数', + minWidth: 100, + }, + { + field: 'price', + title: '未提现金额', + minWidth: 100, + formatter: ({ row }) => `¥${fenToYuan(row.price)}`, + }, + { + field: 'frozenPrice', + title: '冻结中佣金', + minWidth: 100, + formatter: ({ row }) => `¥${fenToYuan(row.frozenPrice)}`, + }, + { + field: 'brokerageEnabled', + title: '推广资格', + minWidth: 80, + align: 'center', + cellRender: { + attrs: { beforeChange: onBrokerageEnabledChange }, + name: 'CellSwitch', + props: { + activeValue: true, + inactiveValue: false, + activeText: '有', + inactiveText: '无', + }, + }, + }, + { + field: 'brokerageTime', + title: '成为推广员时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'bindUserId', + title: '上级推广员编号', + minWidth: 150, + }, + { + field: 'bindUserTime', + title: '推广员绑定时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 创建分销员表单配置 */ +export function useCreateFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '分销员编号', + component: 'Input', + componentProps: { + placeholder: '请输入分销员编号', + }, + rules: 'required', + }, + { + fieldName: 'bindUserId', + label: '上级推广员编号', + component: 'Input', + componentProps: { + placeholder: '请输入上级推广员编号', + }, + rules: 'required', + }, + ]; +} + +/** 修改分销用户表单配置 */ +export function useUpdateFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'bindUserId', + label: '上级推广员编号', + component: 'Input', + componentProps: { + placeholder: '请输入上级推广员编号', + }, + rules: 'required', + }, + ]; +} + +/** 用户列表弹窗搜索表单配置 */ +export function useUserListFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'level', + label: '用户类型', + component: 'Select', + componentProps: { + options: [ + { label: '全部', value: undefined }, + { label: '一级推广人', value: '1' }, + { label: '二级推广人', value: '2' }, + ], + clearable: true, + }, + }, + { + fieldName: 'bindUserTime', + label: '绑定时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 用户列表弹窗表格列配置 */ +export function useUserListColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '用户编号', + minWidth: 80, + }, + { + field: 'avatar', + title: '头像', + minWidth: 70, + cellRender: { + name: 'CellImage', + props: { + width: 24, + height: 24, + shape: 'circle', + }, + }, + }, + { + field: 'nickname', + title: '昵称', + minWidth: 80, + }, + { + field: 'brokerageUserCount', + title: '推广人数', + minWidth: 80, + }, + { + field: 'brokerageOrderCount', + title: '推广订单数量', + minWidth: 110, + }, + { + field: 'brokerageEnabled', + title: '推广资格', + minWidth: 80, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'bindUserTime', + title: '绑定时间', + width: 180, + formatter: 'formatDateTime', + }, + ]; +} + +/** 推广订单列表弹窗搜索表单配置 */ +export function useOrderListFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'sourceUserLevel', + label: '用户类型', + component: 'Select', + componentProps: { + options: [ + { label: '全部', value: 0 }, + { label: '一级推广人', value: 1 }, + { label: '二级推广人', value: 2 }, + ], + }, + defaultValue: 0, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + clearable: true, + options: getDictOptions(DICT_TYPE.BROKERAGE_RECORD_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 推广订单列表弹窗表格列配置 */ +export function useOrderListColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'bizId', + title: '订单编号', + minWidth: 80, + }, + { + field: 'sourceUserId', + title: '用户编号', + minWidth: 80, + }, + { + field: 'sourceUserAvatar', + title: '头像', + minWidth: 70, + cellRender: { + name: 'CellImage', + props: { + width: 24, + height: 24, + }, + }, + }, + { + field: 'sourceUserNickname', + title: '昵称', + minWidth: 80, + }, + { + field: 'price', + title: '佣金', + minWidth: 100, + formatter: ({ row }) => `¥${fenToYuan(row.price)}`, + }, + { + field: 'status', + title: '状态', + minWidth: 85, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BROKERAGE_RECORD_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + width: 180, + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/brokerage/user/index.vue b/apps/web-ele/src/views/mall/trade/brokerage/user/index.vue new file mode 100644 index 000000000..5b7d958dc --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/user/index.vue @@ -0,0 +1,210 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/brokerage/user/modules/create-form.vue b/apps/web-ele/src/views/mall/trade/brokerage/user/modules/create-form.vue new file mode 100644 index 000000000..0ceb8bc98 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/user/modules/create-form.vue @@ -0,0 +1,191 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/brokerage/user/modules/order-list-modal.vue b/apps/web-ele/src/views/mall/trade/brokerage/user/modules/order-list-modal.vue new file mode 100644 index 000000000..7e9520fe5 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/user/modules/order-list-modal.vue @@ -0,0 +1,61 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/brokerage/user/modules/update-form.vue b/apps/web-ele/src/views/mall/trade/brokerage/user/modules/update-form.vue new file mode 100644 index 000000000..f89a7e1c4 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/user/modules/update-form.vue @@ -0,0 +1,161 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/brokerage/user/modules/user-list-modal.vue b/apps/web-ele/src/views/mall/trade/brokerage/user/modules/user-list-modal.vue new file mode 100644 index 000000000..40667488d --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/user/modules/user-list-modal.vue @@ -0,0 +1,52 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/brokerage/withdraw/data.ts b/apps/web-ele/src/views/mall/trade/brokerage/withdraw/data.ts new file mode 100644 index 000000000..86452dd8e --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/withdraw/data.ts @@ -0,0 +1,148 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + allowClear: true, + }, + }, + { + fieldName: 'type', + label: '提现类型', + component: 'Select', + componentProps: { + placeholder: '请选择提现类型', + allowClear: true, + options: getDictOptions(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE, 'number'), + }, + }, + { + fieldName: 'userAccount', + label: '账号', + component: 'Input', + componentProps: { + placeholder: '请输入账号', + allowClear: true, + }, + }, + { + fieldName: 'userName', + label: '真实姓名', + component: 'Input', + componentProps: { + placeholder: '请输入真实姓名', + allowClear: true, + }, + }, + { + fieldName: 'bankName', + label: '提现银行', + component: 'Select', + componentProps: { + placeholder: '请选择提现银行', + allowClear: true, + options: getDictOptions(DICT_TYPE.BROKERAGE_BANK_NAME, 'string'), + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + allowClear: true, + options: getDictOptions(DICT_TYPE.BROKERAGE_WITHDRAW_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '申请时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + allowClear: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 80, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 80, + }, + { + field: 'userNickname', + title: '用户昵称', + minWidth: 80, + }, + { + field: 'price', + title: '提现金额', + minWidth: 80, + formatter: 'formatAmount2', + }, + { + field: 'feePrice', + title: '提现手续费', + minWidth: 80, + formatter: 'formatAmount2', + }, + { + field: 'type', + title: '提现方式', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.BROKERAGE_WITHDRAW_TYPE }, + }, + }, + { + title: '提现信息', + minWidth: 200, + slots: { default: 'withdraw-info' }, + }, + { + field: 'createTime', + title: '申请时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'remark', + title: '备注', + minWidth: 120, + }, + { + title: '状态', + minWidth: 200, + slots: { default: 'status-info' }, + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/brokerage/withdraw/index.vue b/apps/web-ele/src/views/mall/trade/brokerage/withdraw/index.vue new file mode 100644 index 000000000..633f9af56 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/brokerage/withdraw/index.vue @@ -0,0 +1,233 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/config/data.ts b/apps/web-ele/src/views/mall/trade/config/data.ts new file mode 100644 index 000000000..6e91a597f --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/config/data.ts @@ -0,0 +1,255 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +export const schema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'type', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'afterSaleRefundReasons', + label: '退款理由', + component: 'Select', + componentProps: { + placeholder: '请直接输入退款理由', + multiple: true, + options: [], + class: 'w-full', + allowCreate: true, + filterable: true, + reserveKeyword: false, + }, + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'afterSale', + }, + }, + { + fieldName: 'afterSaleReturnReasons', + label: '退货理由', + component: 'Select', + componentProps: { + placeholder: '请直接输入退货理由', + multiple: true, + options: [], + class: 'w-full', + allowCreate: true, + filterable: true, + reserveKeyword: false, + }, + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'afterSale', + }, + }, + { + fieldName: 'deliveryExpressFreeEnabled', + label: '启用包邮', + component: 'Switch', + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'delivery', + }, + help: '商城是否启用全场包邮', + }, + { + fieldName: 'deliveryExpressFreePrice', + label: '满额包邮', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + placeholder: '请输入满额包邮金额', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'delivery', + }, + help: '商城商品满多少金额即可包邮,单位:元', + }, + { + fieldName: 'deliveryPickUpEnabled', + label: '启用门店自提', + component: 'Switch', + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'delivery', + }, + }, + { + fieldName: 'brokerageEnabled', + label: '启用分佣', + component: 'Switch', + help: '商城是否开启分销模式', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + }, + { + fieldName: 'brokerageEnabledCondition', + label: '分佣模式', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.BROKERAGE_ENABLED_CONDITION, 'number'), + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '人人分销:每个用户都可以成为推广员 \n 指定分销:仅可在后台手动设置推广员', + }, + { + fieldName: 'brokerageBindMode', + label: '分销关系绑定', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.BROKERAGE_BIND_MODE, 'number'), + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '首次绑定:只要用户没有推广人,随时都可以绑定推广关系 \n 注册绑定:只有新用户注册时或首次进入系统时才可以绑定推广关系', + }, + { + fieldName: 'brokeragePosterUrls', + label: '分销海报图', + component: 'ImageUpload', + componentProps: { + maxNumber: 9, + }, + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '个人中心分销海报图片,建议尺寸 600x1000', + }, + { + fieldName: 'brokerageFirstPercent', + label: '一级返佣比例(%)', + component: 'InputNumber', + componentProps: { + min: 0, + max: 100, + placeholder: '请输入一级返佣比例', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '订单交易成功后给推广人返佣的百分比', + }, + { + fieldName: 'brokerageSecondPercent', + label: '二级返佣比例(%)', + component: 'InputNumber', + componentProps: { + min: 0, + max: 100, + placeholder: '请输入二级返佣比例', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '订单交易成功后给推广人的推荐人返佣的百分比', + }, + { + fieldName: 'brokerageFrozenDays', + label: '佣金冻结天数', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入佣金冻结天数', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '防止用户退款,佣金被提现了,所以需要设置佣金冻结时间,单位:天', + }, + { + fieldName: 'brokerageWithdrawMinPrice', + label: '提现最低金额(元)', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 2, + placeholder: '请输入提现最低金额', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '用户提现最低金额限制,单位:元', + }, + { + fieldName: 'brokerageWithdrawFeePercent', + label: '提现手续费(%)', + component: 'InputNumber', + componentProps: { + min: 0, + max: 100, + precision: 2, + placeholder: '请输入提现手续费百分比', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '提现手续费百分比,范围 0-100,0 为无提现手续费。例:设置 10,即收取 10% 手续费,提现10 元,到账 9 元,1 元手续费', + }, + { + fieldName: 'brokerageWithdrawTypes', + label: '提现方式', + component: 'CheckboxGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.BROKERAGE_WITHDRAW_TYPE, 'number'), + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => values.type === 'brokerage', + }, + help: '商城开通提现的付款方式', + }, +]; diff --git a/apps/web-ele/src/views/mall/trade/config/index.vue b/apps/web-ele/src/views/mall/trade/config/index.vue new file mode 100644 index 000000000..39745b806 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/config/index.vue @@ -0,0 +1,99 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/express/data.ts b/apps/web-ele/src/views/mall/trade/delivery/express/data.ts new file mode 100644 index 000000000..7e0b67632 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/express/data.ts @@ -0,0 +1,156 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'code', + label: '公司编码', + componentProps: { + placeholder: '请输入快递编码', + }, + rules: 'required', + }, + { + component: 'Input', + fieldName: 'name', + label: '公司名称', + componentProps: { + placeholder: '请输入快递名称', + }, + rules: 'required', + }, + { + component: 'ImageUpload', + fieldName: 'logo', + label: '公司 logo', + rules: 'required', + help: '推荐 180x180 图片分辨率', + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '快递公司名称', + component: 'Input', + componentProps: { + placeholder: '请输入快递公司名称', + allowClear: true, + }, + }, + { + fieldName: 'code', + label: '快递公司编号', + component: 'Input', + componentProps: { + placeholder: '请输入快递公司编号', + allowClear: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + allowClear: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'code', + title: '公司编码', + minWidth: 120, + }, + { + field: 'name', + title: '公司名称', + minWidth: 150, + }, + { + field: 'logo', + title: '公司 logo', + minWidth: 120, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'sort', + title: '显示顺序', + minWidth: 100, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/delivery/express/index.vue b/apps/web-ele/src/views/mall/trade/delivery/express/index.vue new file mode 100644 index 000000000..06cb74333 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/express/index.vue @@ -0,0 +1,143 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/express/modules/form.vue b/apps/web-ele/src/views/mall/trade/delivery/express/modules/form.vue new file mode 100644 index 000000000..5db07b902 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/express/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/data.ts b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/data.ts new file mode 100644 index 000000000..bb1b0ce35 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/data.ts @@ -0,0 +1,228 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 计费方式列标题映射 */ +export const CHARGE_MODE_TITLE_MAP: Record< + number, + { + extraCountTitle: string; + startCountTitle: string; + } +> = { + 1: { startCountTitle: '首件', extraCountTitle: '续件' }, + 2: { startCountTitle: '首件重量(kg)', extraCountTitle: '续件重量(kg)' }, + 3: { startCountTitle: '首件体积(m³)', extraCountTitle: '续件体积(m³)' }, +}; + +/** 包邮方式列标题映射 */ +export const FREE_MODE_TITLE_MAP: Record = { + 1: { freeCountTitle: '包邮件数' }, + 2: { freeCountTitle: '包邮重量(kg)' }, + 3: { freeCountTitle: '包邮体积(m³)' }, +}; + +/** 运费设置表格列 */ +export function useChargesColumns( + chargeMode = 1, +): VxeTableGridOptions['columns'] { + const chargeTitleMap = CHARGE_MODE_TITLE_MAP[chargeMode]; + return [ + { + field: 'areaIds', + title: '区域', + minWidth: 300, + slots: { default: 'areaIds' }, + }, + { + field: 'startCount', + title: chargeTitleMap?.startCountTitle, + width: 120, + slots: { default: 'startCount' }, + }, + { + field: 'startPrice', + title: '运费(元)', + width: 120, + slots: { default: 'startPrice' }, + }, + { + field: 'extraCount', + title: chargeTitleMap?.extraCountTitle, + width: 120, + slots: { default: 'extraCount' }, + }, + { + field: 'extraPrice', + title: '续费(元)', + width: 120, + slots: { default: 'extraPrice' }, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 包邮设置表格列 */ +export function useFreesColumns( + chargeMode = 1, +): VxeTableGridOptions['columns'] { + const freeTitleMap = FREE_MODE_TITLE_MAP[chargeMode]; + return [ + { + field: 'areaIds', + title: '区域', + minWidth: 300, + slots: { default: 'areaIds' }, + }, + { + field: 'freeCount', + title: freeTitleMap?.freeCountTitle, + width: 120, + slots: { default: 'freeCount' }, + }, + { + field: 'freePrice', + title: '包邮金额(元)', + width: 120, + slots: { default: 'freePrice' }, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '模板名称', + componentProps: { + placeholder: '请输入模板名称', + }, + rules: 'required', + }, + { + fieldName: 'chargeMode', + label: '计费方式', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.EXPRESS_CHARGE_MODE, 'number'), + }, + rules: z.number().default(1), + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入显示顺序', + min: 0, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'charges', + label: '运费设置', + component: 'Input', + formItemClass: 'col-span-3', + }, + { + fieldName: 'frees', + label: '包邮设置', + component: 'Input', + formItemClass: 'col-span-3', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + clearable: true, + }, + }, + { + fieldName: 'chargeMode', + label: '计费方式', + component: 'Select', + componentProps: { + placeholder: '请选择计费方式', + clearable: true, + options: getDictOptions(DICT_TYPE.EXPRESS_CHARGE_MODE, 'number'), + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '模板名称', + minWidth: 200, + }, + { + field: 'chargeMode', + title: '计费方式', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.EXPRESS_CHARGE_MODE }, + }, + }, + { + field: 'sort', + title: '显示顺序', + minWidth: 100, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/index.vue b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/index.vue new file mode 100644 index 000000000..fef323572 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/index.vue @@ -0,0 +1,132 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/charge-item-form.vue b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/charge-item-form.vue new file mode 100644 index 000000000..9025baac3 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/charge-item-form.vue @@ -0,0 +1,231 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/form.vue b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/form.vue new file mode 100644 index 000000000..25b8c238d --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/form.vue @@ -0,0 +1,197 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/free-item-form.vue b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/free-item-form.vue new file mode 100644 index 000000000..bbdfacf8b --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/expressTemplate/modules/free-item-form.vue @@ -0,0 +1,202 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/pickUpOrder/data.ts b/apps/web-ele/src/views/mall/trade/delivery/pickUpOrder/data.ts new file mode 100644 index 000000000..f3f9be687 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/pickUpOrder/data.ts @@ -0,0 +1,162 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; +import type { MallDeliveryPickUpStoreApi } from '#/api/mall/trade/delivery/pickUpStore'; + +import { ref } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { useUserStore } from '@vben/stores'; + +import { getSimpleDeliveryPickUpStoreList } from '#/api/mall/trade/delivery/pickUpStore'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +const userStore = useUserStore(); +const pickUpStoreList = ref([]); +getSimpleDeliveryPickUpStoreList().then((res) => { + pickUpStoreList.value = res; + // 移除自己无法核销的门店 + const userId = userStore?.userInfo?.id; + pickUpStoreList.value = pickUpStoreList.value.filter((item) => + item.verifyUserIds?.includes(userId), + ); +}); + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'pickUpStoreIds', + label: '自提门店', + component: 'Select', + componentProps: { + options: pickUpStoreList, + props: { + label: 'name', + value: 'id', + }, + placeholder: '请选择自提门店', + }, + defaultValue: pickUpStoreList.value[0]?.id, + }, + { + fieldName: 'no', + label: '订单号', + component: 'Input', + componentProps: { + placeholder: '请输入订单号', + clearable: true, + }, + }, + { + fieldName: 'userId', + label: '用户 UID', + component: 'Input', + componentProps: { + placeholder: '请输入用户 UID', + clearable: true, + }, + }, + { + fieldName: 'userNickname', + label: '用户昵称', + component: 'Input', + componentProps: { + placeholder: '请输入用户昵称', + clearable: true, + }, + }, + { + fieldName: 'userMobile', + label: '用户电话', + component: 'Input', + componentProps: { + placeholder: '请输入用户电话', + clearable: true, + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeGridPropTypes.Columns { + return [ + { + field: 'no', + title: '订单号', + fixed: 'left', + minWidth: 180, + }, + { + field: 'user.nickname', + title: '用户信息', + minWidth: 100, + }, + { + field: 'brokerageUser.nickname', + title: '推荐人信息', + minWidth: 100, + }, + { + field: 'spuName', + title: '商品信息', + minWidth: 300, + slots: { default: 'spuName' }, + }, + { + field: 'payPrice', + title: '实付金额(元)', + formatter: 'formatAmount2', + minWidth: 180, + }, + { + field: 'storeStaffName', + title: '核销员', + minWidth: 160, + }, + { + field: 'pickUpStoreId', + title: '核销门店', + minWidth: 160, + formatter: ({ row }) => { + return ( + pickUpStoreList.value.find((item) => item.id === row.pickUpStoreId) + ?.name || '' + ); + }, + }, + { + field: 'payStatus', + title: '支付状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + minWidth: 80, + }, + { + field: 'status', + title: '订单状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.TRADE_ORDER_STATUS }, + }, + minWidth: 80, + }, + { + field: 'createTime', + title: '下单时间', + formatter: 'formatDateTime', + minWidth: 160, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/delivery/pickUpOrder/index.vue b/apps/web-ele/src/views/mall/trade/delivery/pickUpOrder/index.vue new file mode 100644 index 000000000..878f38920 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/pickUpOrder/index.vue @@ -0,0 +1,290 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/data.ts b/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/data.ts new file mode 100644 index 000000000..19922a003 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/data.ts @@ -0,0 +1,265 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getAreaTree } from '#/api/system/area'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '门店名称', + rules: 'required', + componentProps: { + placeholder: '请输入门店名称', + }, + }, + { + component: 'Input', + fieldName: 'phone', + label: '门店手机', + rules: 'mobileRequired', + componentProps: { + placeholder: '请输入门店手机', + }, + }, + { + component: 'ImageUpload', + fieldName: 'logo', + label: '门店 logo', + rules: 'required', + formItemClass: 'col-span-2', + componentProps: { + placeholder: '请上传门店 logo', + }, + help: '推荐 180x180 图片分辨率', + }, + { + component: 'Textarea', + fieldName: 'introduction', + label: '门店简介', + formItemClass: 'col-span-2', + componentProps: { + placeholder: '请输入门店简介', + rows: 4, + }, + }, + { + fieldName: 'areaId', + label: '门店所在地区', + component: 'ApiTreeSelect', + rules: 'required', + componentProps: { + api: getAreaTree, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择省市区', + }, + }, + { + component: 'Input', + fieldName: 'detailAddress', + label: '门店详细地址', + rules: 'required', + componentProps: { + placeholder: '请输入门店详细地址', + }, + }, + { + component: 'TimePicker', + fieldName: 'rangeTime', + label: '营业时间', + rules: 'required', + componentProps: { + isRange: true, + format: 'HH:mm', + }, + }, + { + fieldName: 'status', + label: '门店状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + component: 'Input', + fieldName: 'longitude', + label: '经度', + rules: 'required', + componentProps: { + placeholder: '请输入门店经度', + }, + }, + { + component: 'Input', + fieldName: 'latitude', + label: '纬度', + rules: 'required', + componentProps: { + placeholder: '请输入门店纬度', + }, + }, + ]; +} + +/** 绑定店员的表单 */ +export function useBindFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '门店名称', + dependencies: { + triggerFields: ['id'], + disabled: true, + }, + }, + { + component: 'ApiSelect', + fieldName: 'verifyUserIds', + label: '门店店员', + rules: 'required', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + multiple: true, + clearable: true, + placeholder: '请选择门店店员', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'phone', + label: '门店手机', + component: 'Input', + componentProps: { + placeholder: '请输入门店手机', + clearable: true, + }, + }, + { + fieldName: 'name', + label: '门店名称', + component: 'Input', + componentProps: { + placeholder: '请输入门店名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '门店状态', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择门店状态', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 80, + }, + { + field: 'logo', + title: '门店 logo', + minWidth: 100, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'name', + title: '门店名称', + minWidth: 150, + }, + { + field: 'phone', + title: '门店手机', + minWidth: 120, + }, + { + field: 'detailAddress', + title: '地址', + minWidth: 200, + }, + { + field: 'openingTime', + title: '营业时间', + minWidth: 160, + formatter: ({ row }) => { + return `${row.openingTime} ~ ${row.closingTime}`; + }, + }, + { + field: 'status', + title: '开启状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 160, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/index.vue b/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/index.vue new file mode 100644 index 000000000..8f470c089 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/index.vue @@ -0,0 +1,150 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/modules/bind-form.vue b/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/modules/bind-form.vue new file mode 100644 index 000000000..5d747f3b5 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/modules/bind-form.vue @@ -0,0 +1,85 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/modules/form.vue b/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/modules/form.vue new file mode 100644 index 000000000..0ca3656d5 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/delivery/pickUpStore/modules/form.vue @@ -0,0 +1,157 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/order/data.ts b/apps/web-ele/src/views/mall/trade/order/data.ts new file mode 100644 index 000000000..0d4e92364 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/order/data.ts @@ -0,0 +1,450 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; +import type { MallDeliveryPickUpStoreApi } from '#/api/mall/trade/delivery/pickUpStore'; + +import { DeliveryTypeEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { convertToInteger, formatToFraction } from '@vben/utils'; + +import { getSimpleDeliveryExpressList } from '#/api/mall/trade/delivery/express'; +import { getSimpleDeliveryPickUpStoreList } from '#/api/mall/trade/delivery/pickUpStore'; +import { getAreaTree } from '#/api/system/area'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let pickUpStoreList: MallDeliveryPickUpStoreApi.DeliveryPickUpStore[] = []; +getSimpleDeliveryPickUpStoreList().then((data) => { + pickUpStoreList = data; +}); + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'status', + label: '订单状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.TRADE_ORDER_STATUS, 'number'), + placeholder: '请选择订单状态', + clearable: true, + }, + }, + { + fieldName: 'payChannelCode', + label: '支付方式', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.PAY_CHANNEL_CODE, 'number'), + placeholder: '请选择支付方式', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'terminal', + label: '订单来源', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.TERMINAL, 'number'), + placeholder: '请选择订单来源', + clearable: true, + }, + }, + { + fieldName: 'deliveryType', + label: '配送方式', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.TRADE_DELIVERY_TYPE, 'number'), + placeholder: '请选择配送方式', + clearable: true, + }, + }, + { + fieldName: 'logisticsId', + label: '快递公司', + component: 'ApiSelect', + componentProps: { + api: getSimpleDeliveryExpressList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择快递公司', + clearable: true, + }, + dependencies: { + triggerFields: ['deliveryType'], + show: (values) => values.deliveryType === DeliveryTypeEnum.EXPRESS.type, + }, + }, + { + fieldName: 'pickUpStoreId', + label: '自提门店', + component: 'ApiSelect', + componentProps: { + api: getSimpleDeliveryPickUpStoreList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择自提门店', + clearable: true, + }, + dependencies: { + triggerFields: ['deliveryType'], + show: (values) => values.deliveryType === DeliveryTypeEnum.PICK_UP.type, + }, + }, + { + fieldName: 'pickUpVerifyCode', + label: '核销码', + component: 'Input', + componentProps: { + placeholder: '请输入核销码', + clearable: true, + }, + dependencies: { + triggerFields: ['deliveryType'], + show: (values) => values.deliveryType === DeliveryTypeEnum.PICK_UP.type, + }, + }, + { + fieldName: 'no', + label: '订单号', + component: 'Input', + componentProps: { + placeholder: '请输入订单号', + clearable: true, + }, + }, + { + fieldName: 'userId', + label: '用户 UID', + component: 'Input', + componentProps: { + placeholder: '请输入用户 UID', + clearable: true, + }, + }, + { + fieldName: 'userNickname', + label: '用户昵称', + component: 'Input', + componentProps: { + placeholder: '请输入用户昵称', + clearable: true, + }, + }, + { + fieldName: 'userMobile', + label: '用户电话', + component: 'Input', + componentProps: { + placeholder: '请输入用户电话', + clearable: true, + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeGridPropTypes.Columns { + return [ + { + type: 'expand', + width: 80, + slots: { content: 'expand_content' }, + fixed: 'left', + }, + { + field: 'no', + title: '订单号', + fixed: 'left', + minWidth: 180, + }, + { + field: 'createTime', + title: '下单时间', + formatter: 'formatDateTime', + minWidth: 160, + }, + { + field: 'terminal', + title: '订单来源', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.TERMINAL }, + }, + minWidth: 120, + }, + { + field: 'payChannelCode', + title: '支付方式', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_CHANNEL_CODE }, + }, + minWidth: 120, + }, + { + field: 'payTime', + title: '支付时间', + formatter: 'formatDateTime', + minWidth: 160, + }, + { + field: 'type', + title: '订单类型', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.TRADE_ORDER_TYPE }, + }, + minWidth: 80, + }, + { + field: 'payPrice', + title: '实际支付', + formatter: 'formatAmount2', + minWidth: 180, + }, + { + field: 'user', + title: '买家/收货人', + formatter: ({ row }) => { + if (row.deliveryType === DeliveryTypeEnum.EXPRESS.type) { + return `买家:${row.user?.nickname} / 收货人: ${row.receiverName} ${row.receiverMobile}${row.receiverAreaName}${row.receiverDetailAddress}`; + } + if (row.deliveryType === DeliveryTypeEnum.PICK_UP.type) { + return `门店名称:${pickUpStoreList.find((item) => item.id === row.pickUpStoreId)?.name} / + 门店手机:${pickUpStoreList.find((item) => item.id === row.pickUpStoreId)?.phone} / + 自提门店:${pickUpStoreList.find((item) => item.id === row.pickUpStoreId)?.detailAddress} + `; + } + return ''; + }, + minWidth: 180, + }, + { + field: 'deliveryType', + title: '配送方式', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.TRADE_DELIVERY_TYPE }, + }, + minWidth: 80, + }, + { + field: 'status', + title: '订单状态', + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.TRADE_ORDER_STATUS }, + }, + minWidth: 80, + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 订单备注表单配置 */ +export function useRemarkFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + type: 'textarea', + rows: 3, + }, + }, + ]; +} + +/** 订单调价表单配置 */ +export function usePriceFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'payPrice', + label: '应付金额(总)', + component: 'Input', + componentProps: { + placeholder: '请输入应付金额(总)', + disabled: true, + formatter: (value: string) => `${value}元`, + }, + }, + { + fieldName: 'adjustPrice', + label: '订单调价', + component: 'InputNumber', + componentProps: { + placeholder: '请输入订单调价', + step: 0.1, + precision: 2, + controlsPosition: 'right', + class: '!w-full', + }, + help: '订单调价。 正数,加价;负数,减价', + rules: 'required', + }, + { + fieldName: 'newPayPrice', + label: '调价后', + component: 'Input', + componentProps: { + placeholder: '', + formatter: (value: string) => `${value}元`, + }, + dependencies: { + triggerFields: ['payPrice', 'adjustPrice'], + disabled: true, + trigger(values, form) { + const originalPrice = convertToInteger(values.payPrice); + const adjustPrice = convertToInteger(values.adjustPrice); + const newPrice = originalPrice + adjustPrice; + form.setFieldValue('newPayPrice', formatToFraction(newPrice)); + }, + }, + }, + ]; +} + +/** 订单修改地址表单配置 */ +export function useAddressFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'receiverName', + label: '收件人', + component: 'Input', + componentProps: { + placeholder: '请输入收件人名称', + }, + rules: 'required', + }, + { + fieldName: 'receiverMobile', + label: '手机号', + component: 'Input', + componentProps: { + placeholder: '请输入收件人手机号', + }, + rules: 'required', + }, + { + fieldName: 'receiverAreaId', + label: '所在地', + component: 'ApiTreeSelect', + componentProps: { + api: getAreaTree, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择收件人所在地', + treeDefaultExpandAll: true, + }, + rules: 'required', + }, + { + fieldName: 'receiverDetailAddress', + label: '详细地址', + component: 'Input', + componentProps: { + placeholder: '请输入收件人详细地址', + type: 'textarea', + rows: 3, + }, + rules: 'required', + }, + ]; +} + +/** 订单发货表单配置 */ +export function useDeliveryFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'expressType', + label: '发货方式', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '快递', value: 'express' }, + { label: '无需发货', value: 'none' }, + ], + }, + defaultValue: 'express', + }, + { + fieldName: 'logisticsId', + label: '物流公司', + component: 'ApiSelect', + componentProps: { + api: getSimpleDeliveryExpressList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择物流公司', + }, + dependencies: { + triggerFields: ['expressType'], + show: (values) => values.expressType === 'express', + }, + rules: 'required', + }, + { + fieldName: 'logisticsNo', + label: '物流单号', + component: 'Input', + componentProps: { + placeholder: '请输入物流单号', + }, + dependencies: { + triggerFields: ['expressType'], + show: (values) => values.expressType === 'express', + }, + rules: 'required', + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/order/detail/data.ts b/apps/web-ele/src/views/mall/trade/order/detail/data.ts new file mode 100644 index 000000000..febf4ed5e --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/order/detail/data.ts @@ -0,0 +1,255 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { fenToYuan, formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; + +/** 订单基础信息 schema */ +export function useOrderInfoSchema(): DescriptionItemSchema[] { + return [ + { + field: 'no', + label: '订单号', + }, + { + field: 'user.nickname', + label: '买家', + }, + { + field: 'type', + label: '订单类型', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TRADE_ORDER_TYPE, + value: val, + }), + }, + { + field: 'terminal', + label: '订单来源', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TERMINAL, + value: val, + }), + }, + { + field: 'userRemark', + label: '买家留言', + }, + { + field: 'remark', + label: '商家备注', + }, + { + field: 'payOrderId', + label: '支付单号', + }, + { + field: 'payChannelCode', + label: '付款方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_CHANNEL_CODE, + value: val, + }), + }, + { + field: 'brokerageUser.nickname', + label: '推广用户', + }, + ]; +} + +/** 订单状态信息 schema */ +export function useOrderStatusSchema(): DescriptionItemSchema[] { + return [ + { + field: 'status', + label: '订单状态', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TRADE_ORDER_STATUS, + value: val, + }), + }, + { + field: 'reminder', + label: '提醒', + render: () => + h('div', { class: 'space-y-1' }, [ + h('div', '买家付款成功后,货款将直接进入您的商户号(微信、支付宝)'), + h('div', '请及时关注你发出的包裹状态,确保可以配送至买家手中'), + h( + 'div', + '如果买家表示没收到货或货物有问题,请及时联系买家处理,友好协商', + ), + ]), + }, + ]; +} + +/** 订单金额信息 schema */ +export function useOrderPriceSchema(): DescriptionItemSchema[] { + return [ + { + field: 'totalPrice', + label: '商品总额', + render: (val) => `${fenToYuan(val ?? 0)} 元`, + }, + { + field: 'deliveryPrice', + label: '运费金额', + render: (val) => `${fenToYuan(val ?? 0)} 元`, + }, + { + field: 'adjustPrice', + label: '订单调价', + render: (val) => `${fenToYuan(val ?? 0)} 元`, + }, + { + field: 'couponPrice', + label: '优惠劵优惠', + render: (val) => + h('span', { class: 'text-red-500' }, `${fenToYuan(val ?? 0)} 元`), + }, + { + field: 'vipPrice', + label: 'VIP 优惠', + render: (val) => + h('span', { class: 'text-red-500' }, `${fenToYuan(val ?? 0)} 元`), + }, + { + field: 'discountPrice', + label: '活动优惠', + render: (val) => + h('span', { class: 'text-red-500' }, `${fenToYuan(val ?? 0)} 元`), + }, + { + field: 'pointPrice', + label: '积分抵扣', + render: (val) => + h('span', { class: 'text-red-500' }, `${fenToYuan(val ?? 0)} 元`), + }, + { + field: 'payPrice', + label: '应付金额', + render: (val) => `${fenToYuan(val ?? 0)} 元`, + }, + ]; +} + +/** 收货信息 schema */ +export function useDeliveryInfoSchema(): DescriptionItemSchema[] { + return [ + { + field: 'deliveryType', + label: '配送方式', + render: (val) => + h(DictTag, { + type: DICT_TYPE.TRADE_DELIVERY_TYPE, + value: val, + }), + }, + { + field: 'receiverName', + label: '收货人', + }, + { + field: 'receiverMobile', + label: '联系电话', + }, + { + field: 'receiverAddress', + label: '收货地址', + render: (val, data) => `${data?.receiverAreaName} ${val}`.trim(), + }, + { + field: 'deliveryTime', + label: '发货时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} + +/** 商品信息 columns */ +export function useProductColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'spuName', + title: '商品', + minWidth: 300, + slots: { default: 'spuName' }, + }, + { + field: 'price', + title: '商品原价', + width: 150, + formatter: 'formatFenToYuanAmount', + }, + { + field: 'count', + title: '数量', + width: 100, + }, + { + field: 'payPrice', + title: '合计', + width: 150, + formatter: 'formatFenToYuanAmount', + }, + { + field: 'afterSaleStatus', + title: '售后状态', + width: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.TRADE_ORDER_ITEM_AFTER_SALE_STATUS }, + }, + }, + ]; +} + +/** 物流详情 columns */ +export function useExpressTrackColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'time', + title: '时间', + width: 180, + formatter: 'formatDateTime', + }, + { + field: 'content', + title: '物流状态', + minWidth: 300, + }, + ]; +} + +/** 操作日志 columns */ +export function useOperateLogColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'createTime', + title: '操作时间', + width: 180, + formatter: 'formatDateTime', + }, + { + field: 'userType', + title: '操作人', + width: 100, + slots: { default: 'userType' }, + }, + { + field: 'content', + title: '操作内容', + minWidth: 200, + }, + ]; +} diff --git a/apps/web-ele/src/views/mall/trade/order/detail/index.vue b/apps/web-ele/src/views/mall/trade/order/detail/index.vue new file mode 100644 index 000000000..4a84bd8ed --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/order/detail/index.vue @@ -0,0 +1,341 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/order/index.vue b/apps/web-ele/src/views/mall/trade/order/index.vue new file mode 100644 index 000000000..c19b0fdf4 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/order/index.vue @@ -0,0 +1,182 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/order/modules/address-form.vue b/apps/web-ele/src/views/mall/trade/order/modules/address-form.vue new file mode 100644 index 000000000..18712c94d --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/order/modules/address-form.vue @@ -0,0 +1,77 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/order/modules/delivery-form.vue b/apps/web-ele/src/views/mall/trade/order/modules/delivery-form.vue new file mode 100644 index 000000000..1e95cc849 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/order/modules/delivery-form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/order/modules/price-form.vue b/apps/web-ele/src/views/mall/trade/order/modules/price-form.vue new file mode 100644 index 000000000..bb6eafd78 --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/order/modules/price-form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/mall/trade/order/modules/remark-form.vue b/apps/web-ele/src/views/mall/trade/order/modules/remark-form.vue new file mode 100644 index 000000000..7fb5d296a --- /dev/null +++ b/apps/web-ele/src/views/mall/trade/order/modules/remark-form.vue @@ -0,0 +1,78 @@ + + + diff --git a/apps/web-ele/src/views/member/config/data.ts b/apps/web-ele/src/views/member/config/data.ts new file mode 100644 index 000000000..7004d702f --- /dev/null +++ b/apps/web-ele/src/views/member/config/data.ts @@ -0,0 +1,55 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +export const schema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Switch', + fieldName: 'pointTradeDeductEnable', + label: '积分抵扣', + help: '下单积分是否抵用订单金额', + }, + { + component: 'InputNumber', + fieldName: 'pointTradeDeductUnitPrice', + label: '积分抵扣', + help: '积分抵用比例(1 积分抵多少金额),单位:元', + componentProps: { + min: 0, + precision: 2, + placeholder: '请输入积分抵扣单价', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + component: 'InputNumber', + fieldName: 'pointTradeDeductMaxPrice', + label: '积分抵扣最大值', + help: '单次下单积分使用上限,0 不限制', + componentProps: { + min: 0, + placeholder: '请输入积分抵扣最大值', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + component: 'InputNumber', + fieldName: 'pointTradeGivePoint', + label: '1 元赠送多少分', + help: '下单支付金额按比例赠送积分(实际支付 1 元赠送多少积分)', + componentProps: { + min: 0, + placeholder: '请输入赠送积分比例', + controlsPosition: 'right', + class: '!w-full', + }, + }, +]; diff --git a/apps/web-ele/src/views/member/config/index.vue b/apps/web-ele/src/views/member/config/index.vue new file mode 100644 index 000000000..a3eefd258 --- /dev/null +++ b/apps/web-ele/src/views/member/config/index.vue @@ -0,0 +1,67 @@ + + + diff --git a/apps/web-ele/src/views/member/group/data.ts b/apps/web-ele/src/views/member/group/data.ts new file mode 100644 index 000000000..75b8b3da5 --- /dev/null +++ b/apps/web-ele/src/views/member/group/data.ts @@ -0,0 +1,124 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '分组名称', + component: 'Input', + componentProps: { + placeholder: '请输入分组名称', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '分组名称', + component: 'Input', + componentProps: { + placeholder: '请输入分组名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + placeholder: ['开始日期', '结束日期'], + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '分组名称', + minWidth: 150, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + showOverflow: 'tooltip', + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/member/group/index.vue b/apps/web-ele/src/views/member/group/index.vue new file mode 100644 index 000000000..e7e06ed05 --- /dev/null +++ b/apps/web-ele/src/views/member/group/index.vue @@ -0,0 +1,125 @@ + + + diff --git a/apps/web-ele/src/views/member/group/modules/form.vue b/apps/web-ele/src/views/member/group/modules/form.vue new file mode 100644 index 000000000..6e36fe288 --- /dev/null +++ b/apps/web-ele/src/views/member/group/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/member/level/data.ts b/apps/web-ele/src/views/member/level/data.ts new file mode 100644 index 000000000..535dddfde --- /dev/null +++ b/apps/web-ele/src/views/member/level/data.ts @@ -0,0 +1,192 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '等级名称', + component: 'Input', + componentProps: { + placeholder: '请输入等级名称', + }, + rules: 'required', + }, + { + fieldName: 'level', + label: '等级', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 0, + placeholder: '请输入等级', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'experience', + label: '升级经验', + component: 'InputNumber', + componentProps: { + min: 0, + precision: 0, + placeholder: '请输入升级经验', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'discountPercent', + label: '享受折扣(%)', + component: 'InputNumber', + componentProps: { + min: 0, + max: 100, + precision: 0, + placeholder: '请输入享受折扣', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'icon', + label: '等级图标', + component: 'ImageUpload', + }, + { + fieldName: 'backgroundUrl', + label: '等级背景图', + component: 'ImageUpload', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '等级名称', + component: 'Input', + componentProps: { + placeholder: '请输入等级名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '等级编号', + minWidth: 80, + }, + { + field: 'icon', + title: '等级图标', + minWidth: 100, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'backgroundUrl', + title: '等级背景图', + minWidth: 120, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'name', + title: '等级名称', + minWidth: 120, + }, + { + field: 'level', + title: '等级', + minWidth: 80, + }, + { + field: 'experience', + title: '升级经验', + minWidth: 100, + }, + { + field: 'discountPercent', + title: '享受折扣(%)', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 80, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/member/level/index.vue b/apps/web-ele/src/views/member/level/index.vue new file mode 100644 index 000000000..3284397f4 --- /dev/null +++ b/apps/web-ele/src/views/member/level/index.vue @@ -0,0 +1,133 @@ + + + diff --git a/apps/web-ele/src/views/member/level/modules/form.vue b/apps/web-ele/src/views/member/level/modules/form.vue new file mode 100644 index 000000000..087d0d40a --- /dev/null +++ b/apps/web-ele/src/views/member/level/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/member/point/record/data.ts b/apps/web-ele/src/views/member/point/record/data.ts new file mode 100644 index 000000000..0ddb07272 --- /dev/null +++ b/apps/web-ele/src/views/member/point/record/data.ts @@ -0,0 +1,121 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { ElTag } from 'element-plus'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'nickname', + label: '用户', + component: 'Input', + componentProps: { + placeholder: '请输入用户昵称', + clearable: true, + }, + }, + { + fieldName: 'bizType', + label: '业务类型', + component: 'Select', + componentProps: { + placeholder: '请选择业务类型', + clearable: true, + options: getDictOptions(DICT_TYPE.MEMBER_POINT_BIZ_TYPE, 'number'), + }, + }, + { + fieldName: 'title', + label: '积分标题', + component: 'Input', + componentProps: { + placeholder: '请输入积分标题', + clearable: true, + }, + }, + { + fieldName: 'createDate', + label: '获得时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'createTime', + title: '获得时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'nickname', + title: '用户', + minWidth: 150, + }, + { + field: 'point', + title: '获得积分', + minWidth: 120, + slots: { + default: ({ row }) => { + return h( + ElTag, + { + type: row.point > 0 ? 'primary' : 'danger', + }, + () => (row.point > 0 ? `+${row.point}` : row.point), + ); + }, + }, + }, + { + field: 'totalPoint', + title: '总积分', + minWidth: 100, + }, + { + field: 'title', + title: '标题', + minWidth: 200, + }, + { + field: 'description', + title: '描述', + minWidth: 200, + }, + { + field: 'bizId', + title: '业务编码', + minWidth: 120, + }, + { + field: 'bizType', + title: '业务类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MEMBER_POINT_BIZ_TYPE }, + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/member/point/record/index.vue b/apps/web-ele/src/views/member/point/record/index.vue new file mode 100644 index 000000000..cbc15c614 --- /dev/null +++ b/apps/web-ele/src/views/member/point/record/index.vue @@ -0,0 +1,54 @@ + + + diff --git a/apps/web-ele/src/views/member/signin/config/data.ts b/apps/web-ele/src/views/member/signin/config/data.ts new file mode 100644 index 000000000..e3b17dc3a --- /dev/null +++ b/apps/web-ele/src/views/member/signin/config/data.ts @@ -0,0 +1,108 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'InputNumber', + fieldName: 'day', + label: '签到天数', + help: '只允许设置 1-7,默认签到 7 天为一个周期', + componentProps: { + min: 1, + max: 7, + precision: 0, + placeholder: '请输入签到天数', + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(1).max(7, '签到天数必须在 1-7 之间'), + }, + { + component: 'InputNumber', + fieldName: 'point', + label: '获得积分', + componentProps: { + min: 0, + precision: 0, + placeholder: '请输入获得积分', + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0, '获得积分不能小于 0'), + }, + { + component: 'InputNumber', + fieldName: 'experience', + label: '奖励经验', + componentProps: { + min: 0, + precision: 0, + placeholder: '请输入奖励经验', + controlsPosition: 'right', + class: '!w-full', + }, + rules: z.number().min(0, '奖励经验不能小于 0'), + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'day', + title: '签到天数', + minWidth: 120, + formatter: ({ cellValue }) => ['第', cellValue, '天'].join(' '), + }, + { + field: 'point', + title: '获得积分', + minWidth: 120, + }, + { + field: 'experience', + title: '奖励经验', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/member/signin/config/index.vue b/apps/web-ele/src/views/member/signin/config/index.vue new file mode 100644 index 000000000..3b253a40b --- /dev/null +++ b/apps/web-ele/src/views/member/signin/config/index.vue @@ -0,0 +1,131 @@ + + + diff --git a/apps/web-ele/src/views/member/signin/config/modules/form.vue b/apps/web-ele/src/views/member/signin/config/modules/form.vue new file mode 100644 index 000000000..f54539929 --- /dev/null +++ b/apps/web-ele/src/views/member/signin/config/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/member/signin/record/data.ts b/apps/web-ele/src/views/member/signin/record/data.ts new file mode 100644 index 000000000..8217b3fb0 --- /dev/null +++ b/apps/web-ele/src/views/member/signin/record/data.ts @@ -0,0 +1,85 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { h } from 'vue'; + +import { ElTag } from 'element-plus'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'nickname', + label: '签到用户', + component: 'Input', + componentProps: { + placeholder: '请输入签到用户', + clearable: true, + }, + }, + { + fieldName: 'day', + label: '签到天数', + component: 'Input', + componentProps: { + placeholder: '请输入签到天数', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '签到时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'nickname', + title: '签到用户', + minWidth: 150, + }, + { + field: 'day', + title: '签到天数', + minWidth: 120, + formatter: ({ cellValue }) => ['第', cellValue, '天'].join(' '), + }, + { + field: 'point', + title: '获得积分', + minWidth: 120, + slots: { + default: ({ row }) => { + return h( + ElTag, + { + type: row.point > 0 ? 'primary' : 'danger', + }, + () => (row.point > 0 ? `+${row.point}` : row.point), + ); + }, + }, + }, + { + field: 'createTime', + title: '签到时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-ele/src/views/member/signin/record/index.vue b/apps/web-ele/src/views/member/signin/record/index.vue new file mode 100644 index 000000000..d5a36b78b --- /dev/null +++ b/apps/web-ele/src/views/member/signin/record/index.vue @@ -0,0 +1,53 @@ + + + diff --git a/apps/web-ele/src/views/member/tag/data.ts b/apps/web-ele/src/views/member/tag/data.ts new file mode 100644 index 000000000..d5e24d1ab --- /dev/null +++ b/apps/web-ele/src/views/member/tag/data.ts @@ -0,0 +1,79 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '标签名称', + componentProps: { + placeholder: '请输入标签名称', + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '标签名称', + component: 'Input', + componentProps: { + placeholder: '请输入标签名称', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + placeholder: ['开始日期', '结束日期'], + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '标签名称', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 150, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/member/tag/index.vue b/apps/web-ele/src/views/member/tag/index.vue new file mode 100644 index 000000000..6bc412497 --- /dev/null +++ b/apps/web-ele/src/views/member/tag/index.vue @@ -0,0 +1,131 @@ + + + diff --git a/apps/web-ele/src/views/member/tag/modules/form.vue b/apps/web-ele/src/views/member/tag/modules/form.vue new file mode 100644 index 000000000..c1bffdd9b --- /dev/null +++ b/apps/web-ele/src/views/member/tag/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/member/user/data.ts b/apps/web-ele/src/views/member/user/data.ts new file mode 100644 index 000000000..d64a178f2 --- /dev/null +++ b/apps/web-ele/src/views/member/user/data.ts @@ -0,0 +1,499 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { h } from 'vue'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { convertToInteger, formatToFraction } from '@vben/utils'; + +import { ElTag } from 'element-plus'; + +import { z } from '#/adapter/form'; +import { getSimpleGroupList } from '#/api/member/group'; +import { getSimpleLevelList } from '#/api/member/level'; +import { getSimpleTagList } from '#/api/member/tag'; +import { getAreaTree } from '#/api/system/area'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'mobile', + label: '手机号', + component: 'Input', + componentProps: { + placeholder: '请输入手机号', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE).optional(), + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + placeholder: '请输入用户昵称', + }, + }, + { + fieldName: 'avatar', + label: '头像', + component: 'ImageUpload', + }, + { + fieldName: 'name', + label: '真实名字', + component: 'Input', + componentProps: { + placeholder: '请输入真实名字', + }, + }, + { + fieldName: 'sex', + label: '用户性别', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD', + valueFormat: 'x', + placeholder: '请选择出生日期', + class: '!w-full', + }, + }, + { + fieldName: 'areaId', + label: '所在地', + component: 'ApiTreeSelect', + componentProps: { + api: getAreaTree, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择所在地', + }, + }, + { + fieldName: 'tagIds', + label: '用户标签', + component: 'ApiSelect', + componentProps: { + api: getSimpleTagList, + labelField: 'name', + valueField: 'id', + multiple: true, + placeholder: '请选择用户标签', + }, + }, + { + fieldName: 'groupId', + label: '用户分组', + component: 'ApiSelect', + componentProps: { + api: getSimpleGroupList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择用户分组', + }, + }, + { + fieldName: 'mark', + label: '会员备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入会员备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + placeholder: '请输入用户昵称', + clearable: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号', + component: 'Input', + componentProps: { + placeholder: '请输入手机号', + clearable: true, + }, + }, + { + fieldName: 'loginDate', + label: '登录时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '注册时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'tagIds', + label: '用户标签', + component: 'ApiSelect', + componentProps: { + api: getSimpleTagList, + labelField: 'name', + valueField: 'id', + multiple: true, + placeholder: '请选择用户标签', + clearable: true, + }, + }, + { + fieldName: 'levelId', + label: '用户等级', + component: 'ApiSelect', + componentProps: { + api: getSimpleLevelList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择用户等级', + clearable: true, + }, + }, + { + fieldName: 'groupId', + label: '用户分组', + component: 'ApiSelect', + componentProps: { + api: getSimpleGroupList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择用户分组', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + type: 'checkbox', + width: 50, + }, + { + field: 'id', + title: '用户编号', + minWidth: 100, + }, + { + field: 'avatar', + title: '头像', + minWidth: 80, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'mobile', + title: '手机号', + minWidth: 120, + }, + { + field: 'nickname', + title: '昵称', + minWidth: 120, + }, + { + field: 'levelName', + title: '等级', + minWidth: 100, + }, + { + field: 'groupName', + title: '分组', + minWidth: 100, + }, + { + field: 'tagNames', + title: '用户标签', + minWidth: 150, + slots: { + default: ({ row }) => { + return row.tagNames?.map((tagName: string, index: number) => { + return h( + ElTag, + { + key: index, + class: 'mr-1', + type: 'primary', + }, + () => tagName, + ); + }); + }, + }, + }, + { + field: 'point', + title: '积分', + minWidth: 80, + }, + { + field: 'status', + title: '状态', + minWidth: 80, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'loginDate', + title: '登录时间', + minWidth: 160, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '注册时间', + minWidth: 160, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 修改用户等级 */ +export function useLevelFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + label: '用户编号', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'levelId', + label: '用户等级', + component: 'ApiSelect', + componentProps: { + api: getSimpleLevelList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择用户等级', + clearable: true, + }, + }, + { + fieldName: 'reason', + label: '修改原因', + component: 'Textarea', + componentProps: { + placeholder: '请输入修改原因', + }, + rules: 'required', + }, + ]; +} + +/** 修改用户余额 */ +export function useBalanceFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + label: '用户编号', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'balance', + label: '变动前余额(元)', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'changeType', + label: '变动类型', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '增加', value: 1 }, + { label: '减少', value: -1 }, + ], + }, + defaultValue: 1, + }, + { + fieldName: 'changeBalance', + label: '变动余额(元)', + component: 'InputNumber', + rules: 'required', + componentProps: { + min: 0, + precision: 2, + step: 0.1, + placeholder: '请输入变动余额', + controlsPosition: 'right', + class: '!w-full', + }, + defaultValue: 0, + }, + { + fieldName: 'balanceResult', + label: '变动后余额(元)', + component: 'Input', + dependencies: { + triggerFields: ['balance', 'changeBalance', 'changeType'], + disabled: true, + trigger(values, form) { + form.setFieldValue( + 'balanceResult', + formatToFraction( + convertToInteger(values.balance) + + convertToInteger(values.changeBalance) * values.changeType, + ), + ); + }, + }, + }, + ]; +} + +/** 修改用户积分 */ +export function usePointFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + label: '用户编号', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'point', + label: '变动前积分', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'changeType', + label: '变动类型', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '增加', value: 1 }, + { label: '减少', value: -1 }, + ], + }, + defaultValue: 1, + }, + { + fieldName: 'changePoint', + label: '变动积分', + component: 'InputNumber', + rules: 'required', + componentProps: { + min: 0, + precision: 0, + placeholder: '请输入变动积分', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'pointResult', + label: '变动后积分', + component: 'Input', + componentProps: { + placeholder: '', + }, + dependencies: { + triggerFields: ['point', 'changePoint', 'changeType'], + disabled: true, + trigger(values, form) { + form.setFieldValue( + 'pointResult', + values.point + values.changePoint * values.changeType || + values.point, + ); + }, + }, + rules: z.number().min(0), + }, + ]; +} diff --git a/apps/web-ele/src/views/member/user/detail/index.vue b/apps/web-ele/src/views/member/user/detail/index.vue new file mode 100644 index 000000000..bf2ef4c4b --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/index.vue @@ -0,0 +1,127 @@ + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/account-info.vue b/apps/web-ele/src/views/member/user/detail/modules/account-info.vue new file mode 100644 index 000000000..d99cbf0ce --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/account-info.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/address-list.vue b/apps/web-ele/src/views/member/user/detail/modules/address-list.vue new file mode 100644 index 000000000..488058e07 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/address-list.vue @@ -0,0 +1,87 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/after-sale-list.vue b/apps/web-ele/src/views/member/user/detail/modules/after-sale-list.vue new file mode 100644 index 000000000..5661f01f3 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/after-sale-list.vue @@ -0,0 +1,156 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/balance-list.vue b/apps/web-ele/src/views/member/user/detail/modules/balance-list.vue new file mode 100644 index 000000000..86b148ce7 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/balance-list.vue @@ -0,0 +1,43 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/basic-info.vue b/apps/web-ele/src/views/member/user/detail/modules/basic-info.vue new file mode 100644 index 000000000..c6e3891cc --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/basic-info.vue @@ -0,0 +1,98 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/brokerage-list.vue b/apps/web-ele/src/views/member/user/detail/modules/brokerage-list.vue new file mode 100644 index 000000000..5adb98f20 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/brokerage-list.vue @@ -0,0 +1,125 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/coupon-list.vue b/apps/web-ele/src/views/member/user/detail/modules/coupon-list.vue new file mode 100644 index 000000000..51b908f7a --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/coupon-list.vue @@ -0,0 +1,156 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/experience-record-list.vue b/apps/web-ele/src/views/member/user/detail/modules/experience-record-list.vue new file mode 100644 index 000000000..cd3d71e40 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/experience-record-list.vue @@ -0,0 +1,150 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/favorite-list.vue b/apps/web-ele/src/views/member/user/detail/modules/favorite-list.vue new file mode 100644 index 000000000..7fa7231c7 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/favorite-list.vue @@ -0,0 +1,98 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/order-list.vue b/apps/web-ele/src/views/member/user/detail/modules/order-list.vue new file mode 100644 index 000000000..37b562b51 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/order-list.vue @@ -0,0 +1,133 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/point-list.vue b/apps/web-ele/src/views/member/user/detail/modules/point-list.vue new file mode 100644 index 000000000..40ef48271 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/point-list.vue @@ -0,0 +1,68 @@ + + + diff --git a/apps/web-ele/src/views/member/user/detail/modules/sign-list.vue b/apps/web-ele/src/views/member/user/detail/modules/sign-list.vue new file mode 100644 index 000000000..afede6537 --- /dev/null +++ b/apps/web-ele/src/views/member/user/detail/modules/sign-list.vue @@ -0,0 +1,65 @@ + + + diff --git a/apps/web-ele/src/views/member/user/index.vue b/apps/web-ele/src/views/member/user/index.vue new file mode 100644 index 000000000..6e166cd46 --- /dev/null +++ b/apps/web-ele/src/views/member/user/index.vue @@ -0,0 +1,212 @@ + + + diff --git a/apps/web-ele/src/views/member/user/modules/balance-form.vue b/apps/web-ele/src/views/member/user/modules/balance-form.vue new file mode 100644 index 000000000..0a5c34ffa --- /dev/null +++ b/apps/web-ele/src/views/member/user/modules/balance-form.vue @@ -0,0 +1,96 @@ + + + diff --git a/apps/web-ele/src/views/member/user/modules/form.vue b/apps/web-ele/src/views/member/user/modules/form.vue new file mode 100644 index 000000000..b70c08171 --- /dev/null +++ b/apps/web-ele/src/views/member/user/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/member/user/modules/level-form.vue b/apps/web-ele/src/views/member/user/modules/level-form.vue new file mode 100644 index 000000000..50d5c6f86 --- /dev/null +++ b/apps/web-ele/src/views/member/user/modules/level-form.vue @@ -0,0 +1,81 @@ + + + diff --git a/apps/web-ele/src/views/member/user/modules/point-form.vue b/apps/web-ele/src/views/member/user/modules/point-form.vue new file mode 100644 index 000000000..16d810dba --- /dev/null +++ b/apps/web-ele/src/views/member/user/modules/point-form.vue @@ -0,0 +1,83 @@ + + + diff --git a/apps/web-ele/src/views/mp/account/data.ts b/apps/web-ele/src/views/mp/account/data.ts new file mode 100644 index 000000000..d52015355 --- /dev/null +++ b/apps/web-ele/src/views/mp/account/data.ts @@ -0,0 +1,143 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入名称', + }, + }, + { + fieldName: 'account', + label: '微信号', + component: 'Input', + help: '在微信公众平台(mp.weixin.qq.com)的菜单 [设置与开发 - 公众号设置 - 账号详情] 中能找到「微信号」', + rules: 'required', + componentProps: { + placeholder: '请输入微信号', + }, + }, + { + fieldName: 'appId', + label: 'appId', + component: 'Input', + help: '在微信公众平台(mp.weixin.qq.com)的菜单 [设置与开发 - 公众号设置 - 基本设置] 中能找到「开发者ID(AppID)」', + rules: 'required', + componentProps: { + placeholder: '请输入公众号 appId', + }, + }, + { + fieldName: 'appSecret', + label: 'appSecret', + component: 'Input', + help: '在微信公众平台(mp.weixin.qq.com)的菜单 [设置与开发 - 公众号设置 - 基本设置] 中能找到「开发者密码(AppSecret)」', + rules: 'required', + componentProps: { + placeholder: '请输入公众号 appSecret', + }, + }, + { + fieldName: 'token', + label: 'token', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入公众号 token', + }, + }, + { + fieldName: 'aesKey', + label: '消息加解密密钥', + component: 'Input', + componentProps: { + placeholder: '请输入消息加解密密钥', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 搜索表单配置 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名称', + component: 'Input', + componentProps: { + placeholder: '请输入名称', + clearable: true, + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeGridPropTypes.Columns { + return [ + { + title: '名称', + field: 'name', + minWidth: 150, + }, + { + title: '微信号', + field: 'account', + minWidth: 180, + }, + { + title: 'appId', + field: 'appId', + minWidth: 180, + }, + { + title: '服务器地址(URL)', + field: 'utl', + minWidth: 360, + slots: { + default: ({ row }) => { + return `http://服务端地址/admin-api/mp/open/${row.appId}`; + }, + }, + }, + { + title: '二维码', + field: 'qrCodeUrl', + minWidth: 120, + cellRender: { name: 'CellImage' }, + }, + { + title: '备注', + field: 'remark', + minWidth: 150, + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/account/index.vue b/apps/web-ele/src/views/mp/account/index.vue new file mode 100644 index 000000000..ff779584a --- /dev/null +++ b/apps/web-ele/src/views/mp/account/index.vue @@ -0,0 +1,178 @@ + + + diff --git a/apps/web-ele/src/views/mp/account/modules/form.vue b/apps/web-ele/src/views/mp/account/modules/form.vue new file mode 100644 index 000000000..792f9e02b --- /dev/null +++ b/apps/web-ele/src/views/mp/account/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/mp/autoReply/data.ts b/apps/web-ele/src/views/mp/autoReply/data.ts new file mode 100644 index 000000000..2f8c3e497 --- /dev/null +++ b/apps/web-ele/src/views/mp/autoReply/data.ts @@ -0,0 +1,147 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; + +import { markRaw } from 'vue'; + +import { + AutoReplyMsgType, + DICT_TYPE, + RequestMessageTypes, +} from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { WxReply } from '#/views/mp/components'; + +/** 获取表格列配置 */ +export function useGridColumns( + msgType: AutoReplyMsgType, +): VxeGridPropTypes.Columns { + const columns: VxeGridPropTypes.Columns = []; + // 请求消息类型列(仅消息回复显示) + if (msgType === AutoReplyMsgType.Message) { + columns.push({ + field: 'requestMessageType', + title: '请求消息类型', + minWidth: 120, + }); + } + // 关键词列(仅关键词回复显示) + if (msgType === AutoReplyMsgType.Keyword) { + columns.push({ + field: 'requestKeyword', + title: '关键词', + minWidth: 150, + }); + } + // 匹配类型列(仅关键词回复显示) + if (msgType === AutoReplyMsgType.Keyword) { + columns.push({ + field: 'requestMatch', + title: '匹配类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH }, + }, + }); + } + // 回复消息类型列 + columns.push( + { + field: 'responseMessageType', + title: '回复消息类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MP_MESSAGE_TYPE }, + }, + }, + { + field: 'responseContent', + title: '回复内容', + minWidth: 200, + slots: { default: 'replyContent' }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 140, + fixed: 'right', + slots: { default: 'actions' }, + }, + ); + return columns; +} + +/** 新增/修改的表单 */ +export function useFormSchema(msgType: AutoReplyMsgType): VbenFormSchema[] { + const schema: VbenFormSchema[] = []; + // 消息类型(仅消息回复显示) + if (msgType === AutoReplyMsgType.Message) { + schema.push({ + fieldName: 'requestMessageType', + label: '消息类型', + component: 'Select', + componentProps: { + placeholder: '请选择', + options: getDictOptions(DICT_TYPE.MP_MESSAGE_TYPE).filter((d) => + RequestMessageTypes.has(d.value as string), + ), + }, + }); + } + // 匹配类型(仅关键词回复显示) + if (msgType === AutoReplyMsgType.Keyword) { + schema.push({ + fieldName: 'requestMatch', + label: '匹配类型', + component: 'Select', + componentProps: { + placeholder: '请选择匹配类型', + allowClear: true, + options: getDictOptions( + DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH, + 'number', + ), + }, + rules: 'required', + }); + } + // 关键词(仅关键词回复显示) + if (msgType === AutoReplyMsgType.Keyword) { + schema.push({ + fieldName: 'requestKeyword', + label: '关键词', + component: 'Input', + componentProps: { + placeholder: '请输入内容', + allowClear: true, + }, + rules: 'required', + }); + } + // 回复消息 + schema.push({ + fieldName: 'reply', + label: '回复消息', + component: markRaw(WxReply), + modelPropName: 'modelValue', + }); + return schema; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: 'Input', + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/autoReply/index.vue b/apps/web-ele/src/views/mp/autoReply/index.vue new file mode 100644 index 000000000..4ed1b8492 --- /dev/null +++ b/apps/web-ele/src/views/mp/autoReply/index.vue @@ -0,0 +1,233 @@ + + + diff --git a/apps/web-ele/src/views/mp/autoReply/modules/content.vue b/apps/web-ele/src/views/mp/autoReply/modules/content.vue new file mode 100644 index 000000000..caa022c6e --- /dev/null +++ b/apps/web-ele/src/views/mp/autoReply/modules/content.vue @@ -0,0 +1,57 @@ + + + diff --git a/apps/web-ele/src/views/mp/autoReply/modules/form.vue b/apps/web-ele/src/views/mp/autoReply/modules/form.vue new file mode 100644 index 000000000..ef7eaaa83 --- /dev/null +++ b/apps/web-ele/src/views/mp/autoReply/modules/form.vue @@ -0,0 +1,156 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/index.ts b/apps/web-ele/src/views/mp/components/index.ts new file mode 100644 index 000000000..b14a2c4c1 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/index.ts @@ -0,0 +1,9 @@ +export { default as WxAccountSelect } from './wx-account-select/wx-account-select.vue'; +export { default as WxLocation } from './wx-location/wx-location.vue'; +export { default as WxMaterialSelect } from './wx-material-select/wx-material-select.vue'; +export { default as WxMsg } from './wx-msg/msg.vue'; // TODO @hw、@dylan:貌似和 antd 不同。antd 这里是 export { default as WxMsg } from './wx-msg/wx-msg.vue'; 看看哪个是对的 +export { default as WxMusic } from './wx-music/wx-music.vue'; +export { default as WxNews } from './wx-news/wx-news.vue'; +export { default as WxReply } from './wx-reply/wx-reply.vue'; +export { default as WxVideoPlayer } from './wx-video-play/wx-video-play.vue'; +export { default as WxVoicePlayer } from './wx-voice-play/wx-voice-play.vue'; diff --git a/apps/web-ele/src/views/mp/components/wx-account-select/wx-account-select.vue b/apps/web-ele/src/views/mp/components/wx-account-select/wx-account-select.vue new file mode 100644 index 000000000..5a37a5bde --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-account-select/wx-account-select.vue @@ -0,0 +1,72 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-location/types.ts b/apps/web-ele/src/views/mp/components/wx-location/types.ts new file mode 100644 index 000000000..9566c2a6c --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-location/types.ts @@ -0,0 +1,6 @@ +export interface WxLocationProps { + label: string; + locationX: number; + locationY: number; + qqMapKey?: string; +} diff --git a/apps/web-ele/src/views/mp/components/wx-location/wx-location.vue b/apps/web-ele/src/views/mp/components/wx-location/wx-location.vue new file mode 100644 index 000000000..99130bd5f --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-location/wx-location.vue @@ -0,0 +1,72 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-material-select/wx-material-select.vue b/apps/web-ele/src/views/mp/components/wx-material-select/wx-material-select.vue new file mode 100644 index 000000000..f7e3c38e7 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-material-select/wx-material-select.vue @@ -0,0 +1,274 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-msg/card.scss b/apps/web-ele/src/views/mp/components/wx-msg/card.scss new file mode 100644 index 000000000..639f4ca8e --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/card.scss @@ -0,0 +1,116 @@ +.mp-card { + &__item { + box-sizing: border-box; + height: 200px; + margin-bottom: 16px; + font-size: 14px; + font-feature-settings: 'tnum'; + font-variant: tabular-nums; + line-height: 1.5; + color: rgb(0 0 0 / 65%); + cursor: pointer; + list-style: none; + background-color: #fff; + border: 1px solid #e8e8e8; + + &:hover { + border-color: rgb(0 0 0 / 9%); + box-shadow: 0 2px 8px rgb(0 0 0 / 9%); + } + + &--add { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + font-size: 16px; + color: rgb(0 0 0 / 45%); + background-color: #fff; + border: 1px dashed #000; + border-color: #d9d9d9; + border-radius: 2px; + + i { + margin-right: 10px; + } + + &:hover { + color: #40a9ff; + background-color: #fff; + border-color: #40a9ff; + } + } + } + + &__body { + display: flex; + padding: 24px; + } + + &__detail { + flex: 1; + } + + &__avatar { + width: 48px; + height: 48px; + margin-right: 12px; + overflow: hidden; + border-radius: 48px; + + img { + width: 100%; + height: 100%; + } + } + + &__title { + margin-bottom: 12px; + font-size: 16px; + color: rgb(0 0 0 / 85%); + + &:hover { + color: #1890ff; + } + } + + &__info { + display: -webkit-box; + height: 64px; + overflow: hidden; + -webkit-line-clamp: 3; + color: rgb(0 0 0 / 45%); + -webkit-box-orient: vertical; + } + + &__menu { + display: flex; + justify-content: space-around; + height: 50px; + line-height: 50px; + color: rgb(0 0 0 / 45%); + text-align: center; + background: #f7f9fa; + + &:hover { + color: #1890ff; + } + } +} + +/** joolun 额外加的 */ +.mp-comment__main { + flex: unset !important; + margin: 0 8px !important; + border-radius: 5px !important; +} + +.mp-comment__header { + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +.mp-comment__body { + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} diff --git a/apps/web-ele/src/views/mp/components/wx-msg/comment.scss b/apps/web-ele/src/views/mp/components/wx-msg/comment.scss new file mode 100644 index 000000000..98e137d4b --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/comment.scss @@ -0,0 +1,109 @@ +/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */ +.mp-comment { + display: flex; + align-items: flex-start; + margin-bottom: 30px; + + &--reverse { + flex-direction: row-reverse; + + .mp-comment__main { + &::before, + &::after { + right: -8px; + left: auto; + border-width: 8px 0 8px 8px; + } + + &::before { + border-left-color: #dedede; + } + + &::after { + margin-right: 1px; + margin-left: auto; + border-left-color: #f8f8f8; + } + } + } + + &__avatar { + box-sizing: border-box; + width: 48px; + height: 48px; + vertical-align: middle; + border: 1px solid transparent; + border-radius: 50%; + } + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 5px 15px; + background: #f8f8f8; + border-bottom: 1px solid #eee; + } + + &__author { + font-size: 14px; + font-weight: 700; + color: #999; + } + + &__main { + position: relative; + flex: 1; + margin: 0 20px; + border: 1px solid #dedede; + border-radius: 2px; + + &::before, + &::after { + position: absolute; + top: 10px; + right: 100%; + left: -8px; + display: block; + width: 0; + height: 0; + pointer-events: none; + content: ' '; + border-color: transparent; + border-style: solid solid outset; + border-width: 8px 8px 8px 0; + } + + &::before { + z-index: 1; + border-right-color: #dedede; + } + + &::after { + z-index: 2; + margin-left: 1px; + border-right-color: #f8f8f8; + } + } + + &__body { + padding: 15px; + overflow: hidden; + font-family: + 'Segoe UI', 'Lucida Grande', Helvetica, Arial, 'Microsoft YaHei', + FreeSans, Arimo, 'Droid Sans', 'wenquanyi micro hei', 'Hiragino Sans GB', + 'Hiragino Sans GB W3', FontAwesome, sans-serif; + font-size: 14px; + color: #333; + background: #fff; + } + + blockquote { + padding: 1px 0 1px 15px; + margin: 0; + font-family: + Georgia, 'Times New Roman', Times, Kai, 'Kaiti SC', KaiTi, BiauKai, + FontAwesome, serif; + border-left: 4px solid #ddd; + } +} diff --git a/apps/web-ele/src/views/mp/components/wx-msg/msg-event.vue b/apps/web-ele/src/views/mp/components/wx-msg/msg-event.vue new file mode 100644 index 000000000..12b86cdec --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/msg-event.vue @@ -0,0 +1,54 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-msg/msg-list.vue b/apps/web-ele/src/views/mp/components/wx-msg/msg-list.vue new file mode 100644 index 000000000..8bed75e56 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/msg-list.vue @@ -0,0 +1,82 @@ + + diff --git a/apps/web-ele/src/views/mp/components/wx-msg/msg.vue b/apps/web-ele/src/views/mp/components/wx-msg/msg.vue new file mode 100644 index 000000000..1a9ab5058 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/msg.vue @@ -0,0 +1,92 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-msg/wx-msg.vue b/apps/web-ele/src/views/mp/components/wx-msg/wx-msg.vue new file mode 100644 index 000000000..f6ca8c531 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-msg/wx-msg.vue @@ -0,0 +1,176 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-music/types.ts b/apps/web-ele/src/views/mp/components/wx-music/types.ts new file mode 100644 index 000000000..5164f5319 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-music/types.ts @@ -0,0 +1,7 @@ +export interface WxMusicProps { + title?: string; + description?: string; + musicUrl?: string; + hqMusicUrl?: string; + thumbMediaUrl: string; +} diff --git a/apps/web-ele/src/views/mp/components/wx-music/wx-music.vue b/apps/web-ele/src/views/mp/components/wx-music/wx-music.vue new file mode 100644 index 000000000..959b94030 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-music/wx-music.vue @@ -0,0 +1,54 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-news/wx-news.vue b/apps/web-ele/src/views/mp/components/wx-news/wx-news.vue new file mode 100644 index 000000000..6e52761c1 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-news/wx-news.vue @@ -0,0 +1,57 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/tab-image.vue b/apps/web-ele/src/views/mp/components/wx-reply/tab-image.vue new file mode 100644 index 000000000..c8700abf5 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/tab-image.vue @@ -0,0 +1,158 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/tab-music.vue b/apps/web-ele/src/views/mp/components/wx-reply/tab-music.vue new file mode 100644 index 000000000..728609d60 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/tab-music.vue @@ -0,0 +1,146 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/tab-news.vue b/apps/web-ele/src/views/mp/components/wx-reply/tab-news.vue new file mode 100644 index 000000000..80f58e0b7 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/tab-news.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/tab-text.vue b/apps/web-ele/src/views/mp/components/wx-reply/tab-text.vue new file mode 100644 index 000000000..790f7fabd --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/tab-text.vue @@ -0,0 +1,32 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/tab-video.vue b/apps/web-ele/src/views/mp/components/wx-reply/tab-video.vue new file mode 100644 index 000000000..13be72953 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/tab-video.vue @@ -0,0 +1,145 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/tab-voice.vue b/apps/web-ele/src/views/mp/components/wx-reply/tab-voice.vue new file mode 100644 index 000000000..3893f94b6 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/tab-voice.vue @@ -0,0 +1,158 @@ + + diff --git a/apps/web-ele/src/views/mp/components/wx-reply/types.ts b/apps/web-ele/src/views/mp/components/wx-reply/types.ts new file mode 100644 index 000000000..502b4c574 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/types.ts @@ -0,0 +1,42 @@ +import type { Ref } from 'vue'; + +import type { ReplyType } from '@vben/constants'; + +import { unref } from 'vue'; + +export interface Reply { + accountId: number; + articles?: any[]; + content?: null | string; + description?: null | string; + hqMusicUrl?: null | string; + introduction?: null | string; + mediaId?: null | string; + musicUrl?: null | string; + name?: null | string; + thumbMediaId?: null | string; + thumbMediaUrl?: null | string; + title?: null | string; + type: ReplyType; + url?: null | string; +} + +/** 利用旧的 reply[accountId, type] 初始化新的 Reply */ +export function createEmptyReply(old: Ref | Reply): Reply { + return { + accountId: unref(old).accountId, + articles: [], + content: null, + description: null, + hqMusicUrl: null, + introduction: null, + mediaId: null, + musicUrl: null, + name: null, + thumbMediaId: null, + thumbMediaUrl: null, + title: null, + type: unref(old).type, + url: null, + }; +} diff --git a/apps/web-ele/src/views/mp/components/wx-reply/wx-reply.vue b/apps/web-ele/src/views/mp/components/wx-reply/wx-reply.vue new file mode 100644 index 000000000..f80decea0 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-reply/wx-reply.vue @@ -0,0 +1,171 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-video-play/wx-video-play.vue b/apps/web-ele/src/views/mp/components/wx-video-play/wx-video-play.vue new file mode 100644 index 000000000..520d2e5a7 --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-video-play/wx-video-play.vue @@ -0,0 +1,48 @@ + + + diff --git a/apps/web-ele/src/views/mp/components/wx-voice-play/wx-voice-play.vue b/apps/web-ele/src/views/mp/components/wx-voice-play/wx-voice-play.vue new file mode 100644 index 000000000..70b8efa0a --- /dev/null +++ b/apps/web-ele/src/views/mp/components/wx-voice-play/wx-voice-play.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/mp/draft/data.ts b/apps/web-ele/src/views/mp/draft/data.ts new file mode 100644 index 000000000..1ee1e10c1 --- /dev/null +++ b/apps/web-ele/src/views/mp/draft/data.ts @@ -0,0 +1,45 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { formatDateTime } from '@vben/utils'; + +/** 获取表格列配置 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'cover', + title: '图片', + width: 360, + slots: { default: 'cover' }, + }, + { + field: 'title', + title: '标题', + slots: { default: 'title' }, + }, + { + field: 'updateTime', + title: '修改时间', + formatter: ({ row }) => { + return formatDateTime(row.updateTime * 1000); + }, + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: 'Input', + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/draft/index.vue b/apps/web-ele/src/views/mp/draft/index.vue new file mode 100644 index 000000000..e6bfa4fd8 --- /dev/null +++ b/apps/web-ele/src/views/mp/draft/index.vue @@ -0,0 +1,267 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/draft/modules/cover-select.vue b/apps/web-ele/src/views/mp/draft/modules/cover-select.vue new file mode 100644 index 000000000..0a4a302d3 --- /dev/null +++ b/apps/web-ele/src/views/mp/draft/modules/cover-select.vue @@ -0,0 +1,146 @@ + + + diff --git a/apps/web-ele/src/views/mp/draft/modules/form.vue b/apps/web-ele/src/views/mp/draft/modules/form.vue new file mode 100644 index 000000000..e77e8f6d7 --- /dev/null +++ b/apps/web-ele/src/views/mp/draft/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/mp/draft/modules/news-form.vue b/apps/web-ele/src/views/mp/draft/modules/news-form.vue new file mode 100644 index 000000000..632e38500 --- /dev/null +++ b/apps/web-ele/src/views/mp/draft/modules/news-form.vue @@ -0,0 +1,260 @@ + + + diff --git a/apps/web-ele/src/views/mp/freePublish/data.ts b/apps/web-ele/src/views/mp/freePublish/data.ts new file mode 100644 index 000000000..82cfb5f16 --- /dev/null +++ b/apps/web-ele/src/views/mp/freePublish/data.ts @@ -0,0 +1,59 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; +import type { MpAccountApi } from '#/api/mp/account'; + +import { formatDateTime } from '@vben/utils'; + +import { getSimpleAccountList } from '#/api/mp/account'; + +let accountList: MpAccountApi.Account[] = []; +getSimpleAccountList().then((data) => (accountList = data)); + +/** 搜索表单配置 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: 'Select', + componentProps: { + options: accountList.map((item) => ({ + label: item.name, + value: item.id, + })), + placeholder: '请选择公众号', + clearable: true, + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeGridPropTypes.Columns { + return [ + { + field: 'cover', + title: '图片', + width: 360, + slots: { default: 'cover' }, + }, + { + field: 'title', + title: '标题', + slots: { default: 'title' }, + }, + { + field: 'updateTime', + title: '修改时间', + formatter: ({ row }) => { + return formatDateTime(row.updateTime * 1000); + }, + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/freePublish/index.vue b/apps/web-ele/src/views/mp/freePublish/index.vue new file mode 100644 index 000000000..fdd69150c --- /dev/null +++ b/apps/web-ele/src/views/mp/freePublish/index.vue @@ -0,0 +1,172 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/menu/assets/iphone_backImg.png b/apps/web-ele/src/views/mp/menu/assets/iphone_backImg.png new file mode 100644 index 000000000..bb09591a7 Binary files /dev/null and b/apps/web-ele/src/views/mp/menu/assets/iphone_backImg.png differ diff --git a/apps/web-ele/src/views/mp/menu/assets/menu_foot.png b/apps/web-ele/src/views/mp/menu/assets/menu_foot.png new file mode 100644 index 000000000..4a89d4bd2 Binary files /dev/null and b/apps/web-ele/src/views/mp/menu/assets/menu_foot.png differ diff --git a/apps/web-ele/src/views/mp/menu/assets/menu_head.png b/apps/web-ele/src/views/mp/menu/assets/menu_head.png new file mode 100644 index 000000000..248cfb761 Binary files /dev/null and b/apps/web-ele/src/views/mp/menu/assets/menu_head.png differ diff --git a/apps/web-ele/src/views/mp/menu/data.ts b/apps/web-ele/src/views/mp/menu/data.ts new file mode 100644 index 000000000..ddb2bea42 --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/data.ts @@ -0,0 +1,31 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { getSimpleAccountList } from '#/api/mp/account'; + +/** 菜单未选中标识 */ +export const MENU_NOT_SELECTED = '__MENU_NOT_SELECTED__'; + +/** 菜单级别枚举 */ +export enum Level { + Child = '2', + Parent = '1', + Undefined = '0', +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: 'ApiSelect', + componentProps: { + api: getSimpleAccountList, + labelField: 'name', + valueField: 'id', + autoSelect: 'first', + placeholder: '请选择公众号', + }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/menu/index.vue b/apps/web-ele/src/views/mp/menu/index.vue new file mode 100644 index 000000000..b4a0ec558 --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/index.vue @@ -0,0 +1,374 @@ + + + diff --git a/apps/web-ele/src/views/mp/menu/modules/editor.vue b/apps/web-ele/src/views/mp/menu/modules/editor.vue new file mode 100644 index 000000000..103b2dea4 --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/modules/editor.vue @@ -0,0 +1,222 @@ + + + diff --git a/apps/web-ele/src/views/mp/menu/modules/previewer.vue b/apps/web-ele/src/views/mp/menu/modules/previewer.vue new file mode 100644 index 000000000..ba7c24c95 --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/modules/previewer.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/apps/web-ele/src/views/mp/menu/modules/types.ts b/apps/web-ele/src/views/mp/menu/modules/types.ts new file mode 100644 index 000000000..ae0edbd21 --- /dev/null +++ b/apps/web-ele/src/views/mp/menu/modules/types.ts @@ -0,0 +1,116 @@ +export interface Replay { + title: string; + description: string; + picUrl: string; + url: string; +} + +export type MenuType = + | '' + | 'article_view_limited' + | 'click' + | 'location_select' + | 'pic_photo_or_album' + | 'pic_sysphoto' + | 'pic_weixin' + | 'scancode_push' + | 'scancode_waitmsg' + | 'view'; + +interface _RawMenu { + // db + id: number; + parentId: number; + accountId: number; + appId: string; + createTime: number; + + // mp-native + name: string; + menuKey: string; + type: MenuType; + url: string; + miniProgramAppId: string; + miniProgramPagePath: string; + articleId: string; + replyMessageType: string; + replyContent: string; + replyMediaId: string; + replyMediaUrl: string; + replyThumbMediaId: string; + replyThumbMediaUrl: string; + replyTitle: string; + replyDescription: string; + replyArticles: Replay; + replyMusicUrl: string; + replyHqMusicUrl: string; +} + +export type RawMenu = Partial<_RawMenu>; + +interface _Reply { + type: string; + accountId: number; + content: string; + mediaId: string; + url: string; + thumbMediaId: string; + thumbMediaUrl: string; + title: string; + description: string; + articles: null | Replay[]; + musicUrl: string; + hqMusicUrl: string; +} + +export type Reply = Partial<_Reply>; + +interface _Menu extends RawMenu { + children: _Menu[]; + reply: Reply; +} + +export type Menu = Partial<_Menu>; + +export const menuOptions = [ + { + value: 'view', + label: '跳转网页', + }, + { + value: 'miniprogram', + label: '跳转小程序', + }, + { + value: 'click', + label: '点击回复', + }, + { + value: 'article_view_limited', + label: '跳转图文消息', + }, + { + value: 'scancode_push', + label: '扫码直接返回结果', + }, + { + value: 'scancode_waitmsg', + label: '扫码回复', + }, + { + value: 'pic_sysphoto', + label: '系统拍照发图', + }, + { + value: 'pic_photo_or_album', + label: '拍照或者相册', + }, + { + value: 'pic_weixin', + label: '微信相册', + }, + { + value: 'location_select', + label: '选择地理位置', + }, +]; diff --git a/apps/web-ele/src/views/mp/messageTemplate/data.ts b/apps/web-ele/src/views/mp/messageTemplate/data.ts new file mode 100644 index 000000000..8dadf5507 --- /dev/null +++ b/apps/web-ele/src/views/mp/messageTemplate/data.ts @@ -0,0 +1,143 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; + +import { getUserPage } from '#/api/mp/user'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: 'Input', + }, + ]; +} + +/** 发送消息模板表单 */ +export function useSendFormSchema(accountId?: number): VbenFormSchema[] { + return [ + { + fieldName: 'id', + label: '模板编号', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'title', + label: '模板标题', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'userId', + label: '用户', + component: 'ApiSelect', + componentProps: { + api: async () => { + if (!accountId) { + return []; + } + const data = await getUserPage({ + pageNo: 1, + pageSize: 100, + accountId, + }); + return (data.list || []).map((user) => ({ + label: user.nickname || user.openid, + value: user.id, + })); + }, + filterable: true, + placeholder: '请选择用户', + }, + rules: 'required', + }, + { + fieldName: 'data', + label: '模板数据', + component: 'Textarea', + componentProps: { + rows: 4, + placeholder: + '请输入模板数据(JSON 格式),例如:{"keyword1": {"value": "测试内容"}}', + }, + }, + { + fieldName: 'url', + label: '跳转链接', + component: 'Input', + componentProps: { + placeholder: '请输入跳转链接', + }, + }, + { + fieldName: 'miniProgramAppId', + label: '小程序 appId', + component: 'Input', + componentProps: { + placeholder: '请输入小程序 appId', + }, + }, + { + fieldName: 'miniProgramPagePath', + label: '小程序页面路径', + component: 'Input', + componentProps: { + placeholder: '请输入小程序页面路径', + }, + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeGridPropTypes.Columns { + return [ + { + title: '公众号模板 ID', + field: 'templateId', + minWidth: 400, + }, + { + title: '标题', + field: 'title', + minWidth: 150, + }, + { + title: '模板内容', + field: 'content', + minWidth: 400, + }, + { + title: '模板示例', + field: 'example', + minWidth: 200, + }, + { + title: '一级行业', + field: 'primaryIndustry', + minWidth: 120, + }, + { + title: '二级行业', + field: 'deputyIndustry', + minWidth: 120, + }, + { + title: '创建时间', + field: 'createTime', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + title: '操作', + width: 140, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/messageTemplate/index.vue b/apps/web-ele/src/views/mp/messageTemplate/index.vue new file mode 100644 index 000000000..67405fb99 --- /dev/null +++ b/apps/web-ele/src/views/mp/messageTemplate/index.vue @@ -0,0 +1,165 @@ + + + diff --git a/apps/web-ele/src/views/mp/messageTemplate/modules/send-form.vue b/apps/web-ele/src/views/mp/messageTemplate/modules/send-form.vue new file mode 100644 index 000000000..ef809aa7e --- /dev/null +++ b/apps/web-ele/src/views/mp/messageTemplate/modules/send-form.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-ele/src/views/mp/modules/wx-msg/card.scss b/apps/web-ele/src/views/mp/modules/wx-msg/card.scss new file mode 100644 index 000000000..639f4ca8e --- /dev/null +++ b/apps/web-ele/src/views/mp/modules/wx-msg/card.scss @@ -0,0 +1,116 @@ +.mp-card { + &__item { + box-sizing: border-box; + height: 200px; + margin-bottom: 16px; + font-size: 14px; + font-feature-settings: 'tnum'; + font-variant: tabular-nums; + line-height: 1.5; + color: rgb(0 0 0 / 65%); + cursor: pointer; + list-style: none; + background-color: #fff; + border: 1px solid #e8e8e8; + + &:hover { + border-color: rgb(0 0 0 / 9%); + box-shadow: 0 2px 8px rgb(0 0 0 / 9%); + } + + &--add { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + font-size: 16px; + color: rgb(0 0 0 / 45%); + background-color: #fff; + border: 1px dashed #000; + border-color: #d9d9d9; + border-radius: 2px; + + i { + margin-right: 10px; + } + + &:hover { + color: #40a9ff; + background-color: #fff; + border-color: #40a9ff; + } + } + } + + &__body { + display: flex; + padding: 24px; + } + + &__detail { + flex: 1; + } + + &__avatar { + width: 48px; + height: 48px; + margin-right: 12px; + overflow: hidden; + border-radius: 48px; + + img { + width: 100%; + height: 100%; + } + } + + &__title { + margin-bottom: 12px; + font-size: 16px; + color: rgb(0 0 0 / 85%); + + &:hover { + color: #1890ff; + } + } + + &__info { + display: -webkit-box; + height: 64px; + overflow: hidden; + -webkit-line-clamp: 3; + color: rgb(0 0 0 / 45%); + -webkit-box-orient: vertical; + } + + &__menu { + display: flex; + justify-content: space-around; + height: 50px; + line-height: 50px; + color: rgb(0 0 0 / 45%); + text-align: center; + background: #f7f9fa; + + &:hover { + color: #1890ff; + } + } +} + +/** joolun 额外加的 */ +.mp-comment__main { + flex: unset !important; + margin: 0 8px !important; + border-radius: 5px !important; +} + +.mp-comment__header { + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +.mp-comment__body { + border-bottom-right-radius: 5px; + border-bottom-left-radius: 5px; +} diff --git a/apps/web-ele/src/views/mp/modules/wx-msg/comment.scss b/apps/web-ele/src/views/mp/modules/wx-msg/comment.scss new file mode 100644 index 000000000..98e137d4b --- /dev/null +++ b/apps/web-ele/src/views/mp/modules/wx-msg/comment.scss @@ -0,0 +1,109 @@ +/* 来自 https://github.com/nmxiaowei/avue/blob/master/styles/src/element-ui/comment.scss */ +.mp-comment { + display: flex; + align-items: flex-start; + margin-bottom: 30px; + + &--reverse { + flex-direction: row-reverse; + + .mp-comment__main { + &::before, + &::after { + right: -8px; + left: auto; + border-width: 8px 0 8px 8px; + } + + &::before { + border-left-color: #dedede; + } + + &::after { + margin-right: 1px; + margin-left: auto; + border-left-color: #f8f8f8; + } + } + } + + &__avatar { + box-sizing: border-box; + width: 48px; + height: 48px; + vertical-align: middle; + border: 1px solid transparent; + border-radius: 50%; + } + + &__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 5px 15px; + background: #f8f8f8; + border-bottom: 1px solid #eee; + } + + &__author { + font-size: 14px; + font-weight: 700; + color: #999; + } + + &__main { + position: relative; + flex: 1; + margin: 0 20px; + border: 1px solid #dedede; + border-radius: 2px; + + &::before, + &::after { + position: absolute; + top: 10px; + right: 100%; + left: -8px; + display: block; + width: 0; + height: 0; + pointer-events: none; + content: ' '; + border-color: transparent; + border-style: solid solid outset; + border-width: 8px 8px 8px 0; + } + + &::before { + z-index: 1; + border-right-color: #dedede; + } + + &::after { + z-index: 2; + margin-left: 1px; + border-right-color: #f8f8f8; + } + } + + &__body { + padding: 15px; + overflow: hidden; + font-family: + 'Segoe UI', 'Lucida Grande', Helvetica, Arial, 'Microsoft YaHei', + FreeSans, Arimo, 'Droid Sans', 'wenquanyi micro hei', 'Hiragino Sans GB', + 'Hiragino Sans GB W3', FontAwesome, sans-serif; + font-size: 14px; + color: #333; + background: #fff; + } + + blockquote { + padding: 1px 0 1px 15px; + margin: 0; + font-family: + Georgia, 'Times New Roman', Times, Kai, 'Kaiti SC', KaiTi, BiauKai, + FontAwesome, serif; + border-left: 4px solid #ddd; + } +} diff --git a/apps/web-ele/src/views/mp/statistics/chart-options.ts b/apps/web-ele/src/views/mp/statistics/chart-options.ts new file mode 100644 index 000000000..700cb3471 --- /dev/null +++ b/apps/web-ele/src/views/mp/statistics/chart-options.ts @@ -0,0 +1,163 @@ +import type { MpStatisticsApi } from '#/api/mp/statistics'; + +/** 用户增减数据图表配置项 */ +export function userSummaryOption( + data: MpStatisticsApi.StatisticsUserSummaryRespVO[], + dates: string[], +): any { + return { + color: ['#67C23A', '#E5323E'], + legend: { + data: ['新增用户', '取消关注的用户'], + }, + tooltip: {}, + xAxis: { + data: dates, + }, + yAxis: { + minInterval: 1, + }, + series: [ + { + name: '新增用户', + type: 'bar', + label: { + show: true, + }, + barGap: 0, + data: data.map((item) => item.newUser), // 新增用户的数据 + }, + { + name: '取消关注的用户', + type: 'bar', + label: { + show: true, + }, + data: data.map((item) => item.cancelUser), // 取消关注的用户的数据 + }, + ], + }; +} + +/** 累计用户数据图表配置项 */ +export function userCumulateOption( + data: MpStatisticsApi.StatisticsUserCumulateRespVO[], + dates: string[], +): any { + return { + legend: { + data: ['累计用户量'], + }, + xAxis: { + type: 'category', + data: dates, + }, + yAxis: { + minInterval: 1, + }, + series: [ + { + name: '累计用户量', + data: data.map((item) => item.cumulateUser), // 累计用户量的数据 + type: 'line', + smooth: true, + label: { + show: true, + }, + }, + ], + }; +} + +/** 消息发送概况数据图表配置项 */ +export function upstreamMessageOption( + data: MpStatisticsApi.StatisticsUpstreamMessageRespVO[], + dates: string[], +): any { + return { + color: ['#67C23A', '#E5323E'], + legend: { + data: ['用户发送人数', '用户发送条数'], + }, + tooltip: {}, + xAxis: { + data: dates, // X 轴的日期范围 + }, + yAxis: { + minInterval: 1, + }, + series: [ + { + name: '用户发送人数', + type: 'line', + smooth: true, + label: { + show: true, + }, + data: data.map((item) => item.msgUser), // 用户发送人数的数据 + }, + { + name: '用户发送条数', + type: 'line', + smooth: true, + label: { + show: true, + }, + data: data.map((item) => item.msgCount), // 用户发送条数的数据 + }, + ], + }; +} + +/** 接口分析况数据图表配置项 */ +export function interfaceSummaryOption( + data: MpStatisticsApi.StatisticsInterfaceSummaryRespVO[], + dates: string[], +): any { + return { + color: ['#67C23A', '#E5323E', '#E6A23C', '#409EFF'], + legend: { + data: ['被动回复用户消息的次数', '失败次数', '最大耗时', '总耗时'], + }, + tooltip: {}, + xAxis: { + data: dates, // X 轴的日期范围 + }, + yAxis: {}, + series: [ + { + name: '被动回复用户消息的次数', + type: 'bar', + label: { + show: true, + }, + barGap: 0, + data: data.map((item) => item.callbackCount), // 被动回复用户消息的次数的数据 + }, + { + name: '失败次数', + type: 'bar', + label: { + show: true, + }, + data: data.map((item) => item.failCount), // 失败次数的数据 + }, + { + name: '最大耗时', + type: 'bar', + label: { + show: true, + }, + data: data.map((item) => item.maxTimeCost), // 最大耗时的数据 + }, + { + name: '总耗时', + type: 'bar', + label: { + show: true, + }, + data: data.map((item) => item.totalTimeCost), // 总耗时的数据 + }, + ], + }; +} diff --git a/apps/web-ele/src/views/mp/statistics/data.ts b/apps/web-ele/src/views/mp/statistics/data.ts new file mode 100644 index 000000000..b1e34efbe --- /dev/null +++ b/apps/web-ele/src/views/mp/statistics/data.ts @@ -0,0 +1,27 @@ +import type { VbenFormSchema } from '#/adapter/form'; + +import { beginOfDay, endOfDay, formatDateTime } from '@vben/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: 'Input', + }, + { + fieldName: 'dateRange', + label: '时间范围', + component: 'RangePicker', + componentProps: { + format: 'YYYY-MM-DD', + valueFormat: 'YYYY-MM-DD HH:mm:ss', + }, + defaultValue: [ + formatDateTime(beginOfDay(new Date(Date.now() - 3600 * 1000 * 24 * 7))), + formatDateTime(endOfDay(new Date(Date.now() - 3600 * 1000 * 24))), + ], + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/statistics/index.vue b/apps/web-ele/src/views/mp/statistics/index.vue new file mode 100644 index 000000000..180ff6db5 --- /dev/null +++ b/apps/web-ele/src/views/mp/statistics/index.vue @@ -0,0 +1,157 @@ + + + diff --git a/apps/web-ele/src/views/mp/tag/data.ts b/apps/web-ele/src/views/mp/tag/data.ts new file mode 100644 index 000000000..ffce47fdd --- /dev/null +++ b/apps/web-ele/src/views/mp/tag/data.ts @@ -0,0 +1,78 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'accountId', + label: '公众号', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '标签名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入名称', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: 'Input', + }, + ]; +} + +/** 表格列配置 */ +export function useGridColumns(): VxeGridPropTypes.Columns { + return [ + { + title: '编号', + field: 'id', + minWidth: 80, + }, + { + title: '标签名称', + field: 'name', + minWidth: 150, + }, + { + title: '粉丝数', + field: 'count', + minWidth: 100, + }, + { + title: '创建时间', + field: 'createTime', + formatter: 'formatDateTime', + minWidth: 180, + }, + { + title: '操作', + width: 140, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/tag/index.vue b/apps/web-ele/src/views/mp/tag/index.vue new file mode 100644 index 000000000..8ae00b516 --- /dev/null +++ b/apps/web-ele/src/views/mp/tag/index.vue @@ -0,0 +1,177 @@ + + + diff --git a/apps/web-ele/src/views/mp/tag/modules/form.vue b/apps/web-ele/src/views/mp/tag/modules/form.vue new file mode 100644 index 000000000..ca1bf4a02 --- /dev/null +++ b/apps/web-ele/src/views/mp/tag/modules/form.vue @@ -0,0 +1,83 @@ + + + diff --git a/apps/web-ele/src/views/mp/user/data.ts b/apps/web-ele/src/views/mp/user/data.ts new file mode 100644 index 000000000..293554454 --- /dev/null +++ b/apps/web-ele/src/views/mp/user/data.ts @@ -0,0 +1,124 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** 修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'nickname', + label: '昵称', + component: 'Input', + componentProps: { + placeholder: '请输入昵称', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: 'Input', + }, + { + fieldName: 'openid', + label: '用户标识', + component: 'Input', + componentProps: { + placeholder: '请输入用户标识', + clearable: true, + }, + }, + { + fieldName: 'nickname', + label: '昵称', + component: 'Input', + componentProps: { + placeholder: '请输入昵称', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'openid', + title: '用户标识', + minWidth: 260, + }, + { + field: 'headImageUrl', + title: '用户头像', + minWidth: 80, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'nickname', + title: '昵称', + minWidth: 120, + }, + { + field: 'remark', + title: '备注', + minWidth: 120, + }, + { + field: 'tagIds', + title: '标签', + minWidth: 200, + cellRender: { + name: 'CellTags', + }, + }, + { + field: 'subscribeStatus', + title: '订阅状态', + minWidth: 100, + align: 'center', + formatter: ({ cellValue }) => { + return cellValue === 0 ? '已订阅' : '未订阅'; + }, + }, + { + field: 'subscribeTime', + title: '订阅时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/user/index.vue b/apps/web-ele/src/views/mp/user/index.vue new file mode 100644 index 000000000..42212f816 --- /dev/null +++ b/apps/web-ele/src/views/mp/user/index.vue @@ -0,0 +1,137 @@ + + + diff --git a/apps/web-ele/src/views/mp/user/modules/form.vue b/apps/web-ele/src/views/mp/user/modules/form.vue new file mode 100644 index 000000000..dc1c6874c --- /dev/null +++ b/apps/web-ele/src/views/mp/user/modules/form.vue @@ -0,0 +1,80 @@ + + + diff --git a/apps/web-ele/src/views/pay/app/data.ts b/apps/web-ele/src/views/pay/app/data.ts new file mode 100644 index 000000000..8ee19190c --- /dev/null +++ b/apps/web-ele/src/views/pay/app/data.ts @@ -0,0 +1,664 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { PayAppApi } from '#/api/pay/app'; + +import { h } from 'vue'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { InputUpload } from '#/components/upload'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '开启状态', + component: 'Select', + componentProps: { + placeholder: '请选择开启状态', + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onStatusChange?: ( + newStatus: number, + row: PayAppApi.App, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { + field: 'appKey', + title: '应用标识', + minWidth: 40, + }, + { + field: 'name', + title: '应用名', + minWidth: 40, + }, + { + field: 'status', + title: '状态', + align: 'center', + minWidth: 40, + cellRender: { + attrs: { beforeChange: onStatusChange }, + name: 'CellSwitch', + props: { + activeValue: CommonStatusEnum.ENABLE, + inactiveValue: CommonStatusEnum.DISABLE, + }, + }, + }, + { + title: '支付宝配置', + children: [ + { + title: 'APP', + slots: { + default: 'alipayAppConfig', + }, + }, + { + title: 'PC 网站', + slots: { + default: 'alipayPCConfig', + }, + }, + { + title: 'WAP 网站', + slots: { + default: 'alipayWAPConfig', + }, + minWidth: 10, + }, + { + title: '扫码', + slots: { + default: 'alipayQrConfig', + }, + }, + { + title: '条码', + slots: { + default: 'alipayBarConfig', + }, + }, + ], + }, + { + title: '微信配置', + children: [ + { + title: '小程序', + slots: { + default: 'wxLiteConfig', + }, + }, + { + title: 'JSAPI', + slots: { + default: 'wxPubConfig', + }, + }, + { + title: 'APP', + slots: { + default: 'wxAppConfig', + }, + }, + { + title: 'Native', + slots: { + default: 'wxNativeConfig', + }, + }, + { + title: 'WAP 网站', + slots: { + default: 'wxWapConfig', + }, + minWidth: 10, + }, + { + title: '条码', + slots: { + default: 'wxBarConfig', + }, + }, + ], + }, + { + title: '钱包支付配置', + field: 'walletConfig', + slots: { + default: 'walletConfig', + }, + }, + { + title: '模拟支付配置', + field: 'mockConfig', + slots: { + default: 'mockConfig', + }, + }, + { + title: '操作', + width: 140, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 应用新增/修改的表单 */ +export function useAppFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '应用名', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入应用名', + }, + }, + { + fieldName: 'appKey', + label: '应用标识', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入应用标识', + }, + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + rules: z.number().default(CommonStatusEnum.ENABLE), + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'orderNotifyUrl', + label: '支付结果的回调地址', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入支付结果的回调地址', + }, + }, + { + fieldName: 'refundNotifyUrl', + label: '退款结果的回调地址', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入退款结果的回调地址', + }, + }, + { + fieldName: 'transferNotifyUrl', + label: '转账结果的回调地址', + component: 'Input', + componentProps: { + placeholder: '请输入转账结果的回调地址', + }, + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + rows: 3, + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 渠道新增/修改的表单 */ +export function useChannelFormSchema(formType: string = ''): VbenFormSchema[] { + const schema: VbenFormSchema[] = [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + label: '应用编号', + fieldName: 'appId', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + { + label: '渠道编码', + fieldName: 'code', + component: 'Input', + dependencies: { + show: () => false, + triggerFields: [''], + }, + }, + { + label: '渠道费率', + fieldName: 'feeRate', + component: 'InputNumber', + rules: 'required', + componentProps: { + placeholder: '请输入渠道费率', + addonAfter: '%', + controlsPosition: 'right', + class: '!w-full', + }, + defaultValue: 0, + }, + { + label: '渠道状态', + fieldName: 'status', + component: 'RadioGroup', + rules: z.number().default(CommonStatusEnum.ENABLE), + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + ]; + // 添加通用字段 + // 根据类型添加特定字段 + if (formType.includes('alipay_')) { + schema.push( + { + label: '开放平台 APPID', + fieldName: 'config.appId', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入开放平台 APPID', + }, + }, + { + label: '网关地址', + fieldName: 'config.serverUrl', + component: 'RadioGroup', + rules: 'required', + componentProps: { + options: [ + { + value: 'https://openapi.alipay.com/gateway.do', + label: '线上环境', + }, + { + value: 'https://openapi-sandbox.dl.alipaydev.com/gateway.do', + label: '沙箱环境', + }, + ], + }, + }, + { + label: '算法类型', + fieldName: 'config.signType', + component: 'RadioGroup', + rules: 'required', + componentProps: { + options: [ + { + value: 'RSA2', + label: 'RSA2', + }, + ], + }, + defaultValue: 'RSA2', + }, + { + label: '公钥类型', + fieldName: 'config.mode', + component: 'RadioGroup', + rules: 'required', + componentProps: { + options: [ + { + value: 1, + label: '公钥模式', + }, + { + value: 2, + label: '证书模式', + }, + ], + }, + }, + { + label: '应用私钥', + fieldName: 'config.privateKey', + component: 'Textarea', + rules: 'required', + componentProps: { + placeholder: '请输入应用私钥', + rows: 3, + }, + }, + { + label: '支付宝公钥', + fieldName: 'config.alipayPublicKey', + component: 'Textarea', + rules: 'required', + componentProps: { + placeholder: '请输入支付宝公钥', + rows: 3, + }, + dependencies: { + show(values: any) { + return values?.config?.mode === 1; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: '商户公钥应用证书', + fieldName: 'config.appCertContent', + component: h(InputUpload, { + inputType: 'textarea', + textareaProps: { + rows: 3, + placeholder: '请上传商户公钥应用证书', + }, + fileUploadProps: { + accept: ['crt'], + }, + }), + rules: 'required', + dependencies: { + show(values: any) { + return values?.config?.mode === 2; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: '支付宝公钥证书', + fieldName: 'config.alipayPublicCertContent', + component: h(InputUpload, { + inputType: 'textarea', + textareaProps: { rows: 3, placeholder: '请上传支付宝公钥证书' }, + fileUploadProps: { + accept: ['crt'], + }, + }), + rules: 'required', + dependencies: { + show(values: any) { + return values?.config?.mode === 2; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: '根证书', + fieldName: 'config.rootCertContent', + component: h(InputUpload, { + inputType: 'textarea', + textareaProps: { rows: 3, placeholder: '请上传根证书' }, + fileUploadProps: { + accept: ['crt'], + }, + }), + rules: 'required', + dependencies: { + show(values: any) { + return values?.config?.mode === 2; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: '接口内容加密方式', + fieldName: 'config.encryptType', + component: 'RadioGroup', + rules: 'required', + componentProps: { + options: [ + { + value: 'NONE', + label: '无加密', + }, + { + value: 'AES', + label: 'AES', + }, + ], + }, + defaultValue: 'NONE', + }, + { + label: '接口内容加密密钥', + fieldName: 'config.encryptKey', + component: 'Input', + rules: 'required', + dependencies: { + show(values: any) { + return values?.config?.encryptType === 'AES'; + }, + triggerFields: ['config.encryptType', 'encryptType', 'config'], + }, + }, + ); + } else if (formType.includes('wx_')) { + schema.push( + { + label: '微信 APPID', + fieldName: 'config.appId', + help: '前往微信商户平台[https://pay.weixin.qq.com/index.php/extend/merchant_appid/mapay_platform/account_manage]查看 APPID', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入微信 APPID', + }, + }, + { + label: '商户号', + fieldName: 'config.mchId', + help: '前往微信商户平台[https://pay.weixin.qq.com/index.php/extend/pay_setting]查看商户号', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入商户号', + }, + }, + { + label: 'API 版本', + fieldName: 'config.apiVersion', + component: 'RadioGroup', + rules: 'required', + componentProps: { + options: [ + { + label: 'v2', + value: 'v2', + }, + { + label: 'v3', + value: 'v3', + }, + ], + }, + }, + { + label: '商户密钥', + fieldName: 'config.mchKey', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入商户密钥', + }, + dependencies: { + show(values: any) { + return values?.config?.apiVersion === 'v2'; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: 'apiclient_cert.p12 证书', + fieldName: 'config.keyContent', + component: h(InputUpload, { + inputType: 'textarea', + textareaProps: { + rows: 3, + placeholder: '请上传 apiclient_cert.p12 证书', + }, + fileUploadProps: { + accept: ['p12'], + }, + }), + rules: 'required', + dependencies: { + show(values: any) { + return values?.config?.apiVersion === 'v2'; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: 'API V3 密钥', + fieldName: 'config.apiV3Key', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入 API V3 密钥', + }, + dependencies: { + show(values: any) { + return values?.config?.apiVersion === 'v3'; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: 'apiclient_key.pem 证书', + fieldName: 'config.privateKeyContent', + component: h(InputUpload, { + inputType: 'textarea', + textareaProps: { + rows: 3, + placeholder: '请上传 apiclient_key.pem 证书', + }, + fileUploadProps: { + accept: ['pem'], + }, + }), + rules: 'required', + dependencies: { + show(values: any) { + return values?.config?.apiVersion === 'v3'; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: '证书序列号', + fieldName: 'config.certSerialNo', + component: 'Input', + help: '前往微信商户平台[https://pay.weixin.qq.com/index.php/core/cert/api_cert#/api-cert-manage]查看证书序列号', + rules: 'required', + componentProps: { + placeholder: '请输入证书序列号', + }, + dependencies: { + show(values: any) { + return values?.config?.apiVersion === 'v3'; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: 'public_key.pem 证书', + fieldName: 'config.publicKeyContent', + component: h(InputUpload, { + inputType: 'textarea', + textareaProps: { + rows: 3, + placeholder: '请上传 public_key.pem 证书', + }, + fileUploadProps: { + accept: ['pem'], + }, + }), + dependencies: { + show(values: any) { + return values?.config?.apiVersion === 'v3'; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + { + label: '公钥 ID', + fieldName: 'config.publicKeyId', + component: 'Input', + help: '微信支付公钥产品简介及使用说明[https://pay.weixin.qq.com/doc/v3/merchant/4012153196]', + rules: 'required', + componentProps: { + placeholder: '请输入公钥 ID', + }, + dependencies: { + show(values: any) { + return values?.config?.apiVersion === 'v3'; + }, + triggerFields: ['config.mode', 'mode', 'config'], + }, + }, + ); + } + // 添加备注字段(所有类型都有) + schema.push({ + label: '备注', + fieldName: 'remark', + component: 'Input', + componentProps: { + placeholder: '请输入备注', + }, + }); + return schema; +} diff --git a/apps/web-ele/src/views/pay/app/index.vue b/apps/web-ele/src/views/pay/app/index.vue new file mode 100644 index 000000000..57920fe12 --- /dev/null +++ b/apps/web-ele/src/views/pay/app/index.vue @@ -0,0 +1,262 @@ + + + diff --git a/apps/web-ele/src/views/pay/app/modules/app-form.vue b/apps/web-ele/src/views/pay/app/modules/app-form.vue new file mode 100644 index 000000000..0f41f2c0d --- /dev/null +++ b/apps/web-ele/src/views/pay/app/modules/app-form.vue @@ -0,0 +1,83 @@ + + diff --git a/apps/web-ele/src/views/pay/app/modules/channel-form.vue b/apps/web-ele/src/views/pay/app/modules/channel-form.vue new file mode 100644 index 000000000..707069293 --- /dev/null +++ b/apps/web-ele/src/views/pay/app/modules/channel-form.vue @@ -0,0 +1,139 @@ + + + diff --git a/apps/web-ele/src/views/pay/cashier/data.ts b/apps/web-ele/src/views/pay/cashier/data.ts new file mode 100644 index 000000000..eb0cec957 --- /dev/null +++ b/apps/web-ele/src/views/pay/cashier/data.ts @@ -0,0 +1,83 @@ +import { + SvgAlipayAppIcon, + SvgAlipayBarIcon, + SvgAlipayPcIcon, + SvgAlipayQrIcon, + SvgAlipayWapIcon, + SvgMockIcon, + SvgWalletIcon, + SvgWxAppIcon, + SvgWxBarIcon, + SvgWxLiteIcon, + SvgWxNativeIcon, + SvgWxPubIcon, +} from '@vben/icons'; + +export const channelsAlipay = [ + { + name: '支付宝 PC 网站支付', + icon: SvgAlipayPcIcon, + code: 'alipay_pc', + }, + { + name: '支付宝 Wap 网站支付', + icon: SvgAlipayWapIcon, + code: 'alipay_wap', + }, + { + name: '支付宝 App 网站支付', + icon: SvgAlipayAppIcon, + code: 'alipay_app', + }, + { + name: '支付宝扫码支付', + icon: SvgAlipayQrIcon, + code: 'alipay_qr', + }, + { + name: '支付宝条码支付', + icon: SvgAlipayBarIcon, + code: 'alipay_bar', + }, +]; + +export const channelsWechat = [ + { + name: '微信公众号支付', + icon: SvgWxPubIcon, + code: 'wx_pub', + }, + { + name: '微信小程序支付', + icon: SvgWxLiteIcon, + code: 'wx_lite', + }, + { + name: '微信 App 支付', + icon: SvgWxAppIcon, + code: 'wx_app', + }, + { + name: '微信扫码支付', + icon: SvgWxNativeIcon, + code: 'wx_native', + }, + { + name: '微信条码支付', + icon: SvgWxBarIcon, + code: 'wx_bar', + }, +]; + +export const channelsMock = [ + { + name: '钱包支付', + icon: SvgWalletIcon, + code: 'wallet', + }, + { + name: '模拟支付', + icon: SvgMockIcon, + code: 'mock', + }, +]; diff --git a/apps/web-ele/src/views/pay/cashier/index.vue b/apps/web-ele/src/views/pay/cashier/index.vue new file mode 100644 index 000000000..ed5d450b9 --- /dev/null +++ b/apps/web-ele/src/views/pay/cashier/index.vue @@ -0,0 +1,397 @@ + + diff --git a/apps/web-ele/src/views/pay/demo/order/data.ts b/apps/web-ele/src/views/pay/demo/order/data.ts new file mode 100644 index 000000000..b88fba343 --- /dev/null +++ b/apps/web-ele/src/views/pay/demo/order/data.ts @@ -0,0 +1,115 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { formatDateTime } from '@vben/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'spuId', + label: '商品', + component: 'Select', + componentProps: { + options: [ + { label: '华为手机 --- 1.00元', value: 1 }, + { label: '小米电视 --- 10.00元', value: 2 }, + { label: '苹果手表 --- 100.00元', value: 3 }, + { label: '华硕笔记本 --- 1000.00元', value: 4 }, + { label: '蔚来汽车 --- 200000.00元', value: 5 }, + ], + placeholder: '请选择下单商品', + clearable: true, + }, + rules: 'required', + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '订单编号', + minWidth: 100, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'spuName', + title: '商品名字', + minWidth: 150, + }, + { + field: 'price', + title: '支付价格', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'refundPrice', + title: '退款金额', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'payOrderId', + title: '支付单号', + minWidth: 120, + }, + { + field: 'payStatus', + title: '是否支付', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'payTime', + title: '支付时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'refundTime', + title: '退款时间', + minWidth: 180, + formatter: ({ cellValue, row }) => { + if (cellValue) { + return formatDateTime(cellValue) as string; + } + if (row.payRefundId) { + return '退款中,等待退款结果'; + } + return ''; + }, + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/pay/demo/order/index.vue b/apps/web-ele/src/views/pay/demo/order/index.vue new file mode 100644 index 000000000..cbfee2b12 --- /dev/null +++ b/apps/web-ele/src/views/pay/demo/order/index.vue @@ -0,0 +1,148 @@ + + + diff --git a/apps/web-ele/src/views/pay/demo/order/modules/form.vue b/apps/web-ele/src/views/pay/demo/order/modules/form.vue new file mode 100644 index 000000000..36b1b9d9b --- /dev/null +++ b/apps/web-ele/src/views/pay/demo/order/modules/form.vue @@ -0,0 +1,55 @@ + + + diff --git a/apps/web-ele/src/views/pay/demo/withdraw/data.ts b/apps/web-ele/src/views/pay/demo/withdraw/data.ts new file mode 100644 index 000000000..d2622c0f5 --- /dev/null +++ b/apps/web-ele/src/views/pay/demo/withdraw/data.ts @@ -0,0 +1,168 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'subject', + label: '提现标题', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入提现标题', + }, + }, + { + fieldName: 'price', + label: '提现金额', + component: 'InputNumber', + rules: 'required', + componentProps: { + min: 1, + precision: 2, + step: 0.01, + placeholder: '请输入提现金额', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'type', + label: '提现类型', + component: 'Select', + rules: 'required', + componentProps: { + options: [ + { label: '支付宝', value: 1 }, + { label: '微信余额', value: 2 }, + { label: '钱包余额', value: 3 }, + ], + placeholder: '请选择提现类型', + }, + }, + { + fieldName: 'userAccount', + label: '收款人账号', + component: 'Input', + rules: 'required', + dependencies: { + triggerFields: ['type'], + componentProps: (values) => { + const type = values.type; + let placeholder = '请输入收款人账号'; + switch (type) { + case 1: { + placeholder = '请输入支付宝账号'; + break; + } + case 2: { + placeholder = '请输入微信 openid'; + break; + } + case 3: { + placeholder = '请输入钱包编号'; + break; + } + } + return { + placeholder, + }; + }, + }, + }, + { + fieldName: 'userName', + label: '收款人姓名', + component: 'Input', + componentProps: { + placeholder: '请输入收款人姓名', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '提现单编号', + minWidth: 100, + }, + { + field: 'subject', + title: '提现标题', + minWidth: 150, + }, + { + field: 'type', + title: '提现类型', + minWidth: 100, + slots: { default: 'type' }, + }, + { + field: 'price', + title: '提现金额', + minWidth: 100, + formatter: 'formatAmount2', + }, + { + field: 'userName', + title: '收款人姓名', + minWidth: 120, + }, + { + field: 'userAccount', + title: '收款人账号', + minWidth: 150, + }, + { + field: 'status', + title: '提现状态', + minWidth: 100, + slots: { default: 'status' }, + }, + { + field: 'payTransferId', + title: '转账单号', + minWidth: 120, + }, + { + field: 'transferChannelCode', + title: '转账渠道', + minWidth: 130, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_CHANNEL_CODE }, + }, + }, + { + field: 'transferTime', + title: '转账时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'transferErrorMsg', + title: '转账失败原因', + minWidth: 150, + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/pay/demo/withdraw/index.vue b/apps/web-ele/src/views/pay/demo/withdraw/index.vue new file mode 100644 index 000000000..6357705d8 --- /dev/null +++ b/apps/web-ele/src/views/pay/demo/withdraw/index.vue @@ -0,0 +1,145 @@ + + + diff --git a/apps/web-ele/src/views/pay/demo/withdraw/modules/form.vue b/apps/web-ele/src/views/pay/demo/withdraw/modules/form.vue new file mode 100644 index 000000000..2370988da --- /dev/null +++ b/apps/web-ele/src/views/pay/demo/withdraw/modules/form.vue @@ -0,0 +1,55 @@ + + + diff --git a/apps/web-ele/src/views/pay/notify/data.ts b/apps/web-ele/src/views/pay/notify/data.ts new file mode 100644 index 000000000..5917b416c --- /dev/null +++ b/apps/web-ele/src/views/pay/notify/data.ts @@ -0,0 +1,271 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { getAppList } from '#/api/pay/app'; +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'appId', + label: '应用编号', + component: 'ApiSelect', + componentProps: { + api: getAppList, + labelField: 'name', + valueField: 'id', + autoSelect: 'first', + placeholder: '请选择应用编号', + }, + }, + { + fieldName: 'type', + label: '通知类型', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.PAY_NOTIFY_TYPE, 'number'), + placeholder: '请选择通知类型', + }, + }, + { + fieldName: 'dataId', + label: '关联编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入关联编号', + }, + }, + { + fieldName: 'status', + label: '通知状态', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.PAY_NOTIFY_STATUS, 'number'), + placeholder: '请选择通知状态', + }, + }, + { + fieldName: 'merchantOrderId', + label: '商户订单编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入商户订单编号', + }, + }, + { + fieldName: 'merchantRefundId', + label: '商户退款编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入商户退款编号', + }, + }, + { + fieldName: 'merchantTransferId', + label: '商户转账编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入商户转账编号', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '任务编号', + minWidth: 100, + }, + { + field: 'appName', + title: '应用名称', + minWidth: 150, + }, + { + field: 'merchantInfo', + title: '商户单信息', + minWidth: 240, + slots: { + default: 'merchantInfo', + }, + }, + { + field: 'type', + title: '通知类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_NOTIFY_TYPE }, + }, + }, + { + field: 'dataId', + title: '关联编号', + minWidth: 120, + }, + { + field: 'status', + title: '通知状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_NOTIFY_STATUS }, + }, + }, + { + field: 'lastExecuteTime', + title: '最后通知时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'nextNotifyTime', + title: '下次通知时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'notifyTimes', + title: '通知次数', + minWidth: 120, + formatter: ({ row }) => `${row.notifyTimes} / ${row.maxNotifyTimes}`, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'appId', + label: '应用编号', + }, + { + field: 'appName', + label: '应用名称', + }, + { + field: 'type', + label: '通知类型', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_NOTIFY_TYPE, + value: val, + }), + }, + { + field: 'dataId', + label: '关联编号', + }, + { + field: 'status', + label: '通知状态', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_NOTIFY_STATUS, + value: val, + }), + }, + { + field: 'merchantOrderId', + label: '商户订单编号', + }, + { + field: 'lastExecuteTime', + label: '最后通知时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'nextNotifyTime', + label: '下次通知时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'notifyTimes', + label: '通知次数', + }, + { + field: 'maxNotifyTimes', + label: '最大通知次数', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'updateTime', + label: '更新时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} + +/** 详情的日志字段 */ +export function useDetailLogColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 120, + }, + { + field: 'status', + title: '通知状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_NOTIFY_STATUS }, + }, + }, + { + field: 'notifyTimes', + title: '通知次数', + minWidth: 120, + }, + { + field: 'createTime', + title: '通知时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'response', + title: '响应结果', + minWidth: 200, + }, + ]; +} diff --git a/apps/web-ele/src/views/pay/notify/index.vue b/apps/web-ele/src/views/pay/notify/index.vue new file mode 100644 index 000000000..9821fb9ff --- /dev/null +++ b/apps/web-ele/src/views/pay/notify/index.vue @@ -0,0 +1,104 @@ + + diff --git a/apps/web-ele/src/views/pay/notify/modules/detail.vue b/apps/web-ele/src/views/pay/notify/modules/detail.vue new file mode 100644 index 000000000..d5b7bb71c --- /dev/null +++ b/apps/web-ele/src/views/pay/notify/modules/detail.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-ele/src/views/pay/order/data.ts b/apps/web-ele/src/views/pay/order/data.ts new file mode 100644 index 000000000..ff55e09f6 --- /dev/null +++ b/apps/web-ele/src/views/pay/order/data.ts @@ -0,0 +1,271 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { ElTag } from 'element-plus'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'appId', + label: '应用编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入应用编号', + }, + }, + { + fieldName: 'channelCode', + label: '支付渠道', + component: 'Select', + componentProps: { + clearable: true, + placeholder: '请选择支付渠道', + options: getDictOptions(DICT_TYPE.PAY_CHANNEL_CODE, 'string'), + }, + }, + { + fieldName: 'merchantOrderId', + label: '商户单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入商户单号', + }, + }, + { + fieldName: 'no', + label: '支付单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入支付单号', + }, + }, + { + fieldName: 'channelOrderNo', + label: '渠道单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入渠道单号', + }, + }, + { + fieldName: 'status', + label: '支付状态', + component: 'Select', + componentProps: { + clearable: true, + placeholder: '请选择支付状态', + options: getDictOptions(DICT_TYPE.PAY_ORDER_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'price', + title: '支付金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'refundPrice', + title: '退款金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'channelFeePrice', + title: '手续金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'no', + title: '订单号', + minWidth: 240, + slots: { + default: 'no', + }, + }, + { + field: 'status', + title: '支付状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_ORDER_STATUS }, + }, + }, + { + field: 'channelCode', + title: '支付渠道', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_CHANNEL_CODE }, + }, + }, + { + field: 'successTime', + title: '支付时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'appName', + title: '支付应用', + minWidth: 150, + }, + { + field: 'subject', + title: '商品标题', + minWidth: 200, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'merchantOrderId', + label: '商户单号', + }, + { + field: 'no', + label: '支付单号', + }, + { + field: 'appId', + label: '应用编号', + }, + { + field: 'appName', + label: '应用名称', + }, + { + field: 'status', + label: '支付状态', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_ORDER_STATUS, + value: val, + }), + }, + { + field: 'price', + label: '支付金额', + render: (val) => `¥${erpPriceInputFormatter(val)}`, + }, + { + field: 'channelFeePrice', + label: '手续费', + render: (val) => `¥${erpPriceInputFormatter(val)}`, + }, + { + field: 'channelFeeRate', + label: '手续费比例', + render: (val) => `${erpPriceInputFormatter(val)}%`, + }, + { + field: 'successTime', + label: '支付时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'expireTime', + label: '失效时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'updateTime', + label: '更新时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'subject', + label: '商品标题', + }, + { + field: 'body', + label: '商品描述', + }, + { + field: 'channelCode', + label: '支付渠道', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_CHANNEL_CODE, + value: val, + }), + }, + { + field: 'userIp', + label: '支付 IP', + }, + { + field: 'channelOrderNo', + label: '渠道单号', + render: (val) => (val ? h(ElTag, { color: 'green' }, () => val) : ''), + }, + { + field: 'channelUserId', + label: '渠道用户', + }, + { + field: 'refundPrice', + label: '退款金额', + render: (val) => `¥${erpPriceInputFormatter(val)}`, + }, + { + field: 'notifyUrl', + label: '通知 URL', + }, + { + field: 'channelNotifyData', + label: '支付通道异步回调内容', + }, + ]; +} diff --git a/apps/web-ele/src/views/pay/order/index.vue b/apps/web-ele/src/views/pay/order/index.vue new file mode 100644 index 000000000..adf9818d3 --- /dev/null +++ b/apps/web-ele/src/views/pay/order/index.vue @@ -0,0 +1,134 @@ + + + diff --git a/apps/web-ele/src/views/pay/order/modules/detail.vue b/apps/web-ele/src/views/pay/order/modules/detail.vue new file mode 100644 index 000000000..99c261d77 --- /dev/null +++ b/apps/web-ele/src/views/pay/order/modules/detail.vue @@ -0,0 +1,50 @@ + + diff --git a/apps/web-ele/src/views/pay/refund/data.ts b/apps/web-ele/src/views/pay/refund/data.ts new file mode 100644 index 000000000..8e1afd615 --- /dev/null +++ b/apps/web-ele/src/views/pay/refund/data.ts @@ -0,0 +1,280 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { ElTag } from 'element-plus'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'appId', + label: '应用编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入应用编号', + }, + }, + { + fieldName: 'channelCode', + label: '退款渠道', + component: 'Select', + componentProps: { + clearable: true, + placeholder: '请选择退款渠道', + options: getDictOptions(DICT_TYPE.PAY_CHANNEL_CODE, 'string'), + }, + }, + { + fieldName: 'merchantOrderId', + label: '商户单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入商户单号', + }, + }, + { + fieldName: 'merchantRefundId', + label: '退款单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入退款单号', + }, + }, + { + fieldName: 'channelOrderNo', + label: '渠道单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入渠道单号', + }, + }, + { + fieldName: 'channelRefundNo', + label: '渠道退款单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入渠道退款单号', + }, + }, + { + fieldName: 'status', + label: '退款状态', + component: 'Select', + componentProps: { + clearable: true, + placeholder: '请选择退款状态', + options: getDictOptions(DICT_TYPE.PAY_REFUND_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'payPrice', + title: '支付金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'refundPrice', + title: '退款金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'merchantRefundId', + title: '退款单号', + minWidth: 240, + slots: { + default: 'no', + }, + }, + { + field: 'status', + title: '退款状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_REFUND_STATUS }, + }, + }, + { + field: 'channelCode', + title: '退款渠道', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_CHANNEL_CODE }, + }, + }, + { + field: 'successTime', + title: '退款时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'reason', + title: '退款原因', + minWidth: 200, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + // 基本信息部分 + { + field: 'merchantRefundId', + label: '商户退款单号', + render: (val) => h(ElTag, {}, () => val || '-'), + }, + { + field: 'channelRefundNo', + label: '渠道退款单号', + render: (val) => h(ElTag, { type: 'success' }, () => val || '-'), + }, + { + field: 'merchantOrderId', + label: '商户支付单号', + render: (val) => h(ElTag, {}, () => val || '-'), + }, + { + field: 'channelOrderNo', + label: '渠道支付单号', + render: (val) => h(ElTag, { type: 'success' }, () => val || '-'), + }, + { + field: 'appId', + label: '应用编号', + }, + { + field: 'appName', + label: '应用名称', + }, + { + field: 'payPrice', + label: '支付金额', + render: (val) => + h( + ElTag, + { type: 'success' }, + () => `¥${erpPriceInputFormatter(val || 0)}`, + ), + }, + { + field: 'refundPrice', + label: '退款金额', + render: (val) => + h( + ElTag, + { type: 'danger' }, + () => `¥${erpPriceInputFormatter(val || 0)}`, + ), + }, + { + field: 'status', + label: '退款状态', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_REFUND_STATUS, + value: val, + }), + }, + { + field: 'successTime', + label: '退款时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'updateTime', + label: '更新时间', + render: (val) => formatDateTime(val) as string, + }, + // 渠道信息部分 + { + field: 'channelCode', + label: '退款渠道', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_CHANNEL_CODE, + value: val, + }), + }, + { + field: 'reason', + label: '退款原因', + }, + { + field: 'userIp', + label: '退款 IP', + }, + { + field: 'notifyUrl', + label: '通知 URL', + }, + // 错误信息部分 + { + field: 'channelErrorCode', + label: '渠道错误码', + }, + { + field: 'channelErrorMsg', + label: '渠道错误码描述', + }, + { + field: 'channelNotifyData', + label: '支付通道异步回调内容', + }, + ]; +} diff --git a/apps/web-ele/src/views/pay/refund/index.vue b/apps/web-ele/src/views/pay/refund/index.vue new file mode 100644 index 000000000..c363ec21d --- /dev/null +++ b/apps/web-ele/src/views/pay/refund/index.vue @@ -0,0 +1,126 @@ + + diff --git a/apps/web-ele/src/views/pay/refund/modules/detail.vue b/apps/web-ele/src/views/pay/refund/modules/detail.vue new file mode 100644 index 000000000..1b93a77e6 --- /dev/null +++ b/apps/web-ele/src/views/pay/refund/modules/detail.vue @@ -0,0 +1,51 @@ + + + diff --git a/apps/web-ele/src/views/pay/transfer/data.ts b/apps/web-ele/src/views/pay/transfer/data.ts new file mode 100644 index 000000000..0e3b1a04c --- /dev/null +++ b/apps/web-ele/src/views/pay/transfer/data.ts @@ -0,0 +1,268 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { erpPriceInputFormatter, formatDateTime } from '@vben/utils'; + +import { ElTag } from 'element-plus'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'no', + label: '转账单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入转账单号', + }, + }, + { + fieldName: 'channelCode', + label: '转账渠道', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.PAY_CHANNEL_CODE), + clearable: true, + placeholder: '请选择支付渠道', + }, + }, + { + fieldName: 'merchantTransferId', + label: '商户单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入商户单号', + }, + }, + { + fieldName: 'type', + label: '类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.PAY_TRANSFER_TYPE), + clearable: true, + placeholder: '请选择类型', + }, + }, + { + fieldName: 'status', + label: '转账状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.PAY_TRANSFER_STATUS), + clearable: true, + placeholder: '请选择转账状态', + }, + }, + { + fieldName: 'userName', + label: '收款人姓名', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入收款人姓名', + }, + }, + { + fieldName: 'userAccount', + label: '收款人账号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入收款人账号', + }, + }, + { + fieldName: 'channelTransferNo', + label: '渠道单号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入渠道单号', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'price', + title: '转账金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'merchantTransferId', + title: '转账单号', + minWidth: 350, + slots: { + default: 'no', + }, + }, + { + field: 'status', + title: '转账状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_TRANSFER_STATUS }, + }, + }, + { + field: 'channelCode', + title: '转账渠道', + minWidth: 140, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.PAY_CHANNEL_CODE }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'successTime', + title: '转账时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'subject', + title: '转账标题', + minWidth: 150, + }, + { + field: 'appName', + title: '支付应用', + minWidth: 150, + }, + { + field: 'userName', + title: '收款人姓名', + minWidth: 150, + }, + { + field: 'userAccount', + title: '收款账号', + minWidth: 200, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'merchantTransferId', + label: '商户单号', + render: (val) => h(ElTag, {}, () => val), + }, + { + field: 'no', + label: '转账单号', + render: (val) => h(ElTag, { color: 'orange' }, () => val), + }, + { + field: 'appId', + label: '应用编号', + }, + { + field: 'status', + label: '转账状态', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_TRANSFER_STATUS, + value: val, + }), + }, + { + field: 'price', + label: '转账金额', + render: (val) => + h( + ElTag, + { color: 'success' }, + () => `¥${erpPriceInputFormatter(val || 0)}`, + ), + }, + { + field: 'successTime', + label: '转账时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'userName', + label: '收款人姓名', + }, + { + field: 'userAccount', + label: '收款人账号', + }, + { + field: 'channelCode', + label: '支付渠道', + render: (val) => + h(DictTag, { + type: DICT_TYPE.PAY_CHANNEL_CODE, + value: val, + }), + }, + { + field: 'userIp', + label: '支付 IP', + }, + { + field: 'channelTransferNo', + label: '渠道单号', + render: (val) => (val ? h(ElTag, { color: 'success' }, () => val) : ''), + }, + { + field: 'notifyUrl', + label: '通知 URL', + }, + { + field: 'channelNotifyData', + label: '转账渠道通知内容', + }, + ]; +} diff --git a/apps/web-ele/src/views/pay/transfer/index.vue b/apps/web-ele/src/views/pay/transfer/index.vue new file mode 100644 index 000000000..e93f0c2d1 --- /dev/null +++ b/apps/web-ele/src/views/pay/transfer/index.vue @@ -0,0 +1,124 @@ + + + diff --git a/apps/web-ele/src/views/pay/transfer/modules/detail.vue b/apps/web-ele/src/views/pay/transfer/modules/detail.vue new file mode 100644 index 000000000..86dd91569 --- /dev/null +++ b/apps/web-ele/src/views/pay/transfer/modules/detail.vue @@ -0,0 +1,51 @@ + + + diff --git a/apps/web-ele/src/views/pay/wallet/balance/data.ts b/apps/web-ele/src/views/pay/wallet/balance/data.ts new file mode 100644 index 000000000..2b30e865c --- /dev/null +++ b/apps/web-ele/src/views/pay/wallet/balance/data.ts @@ -0,0 +1,137 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + clearable: true, + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + placeholder: '请选择用户类型', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '编号', + field: 'id', + minWidth: 100, + }, + { + title: '用户编号', + field: 'userId', + minWidth: 120, + }, + { + title: '用户类型', + field: 'userType', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + title: '余额', + field: 'balance', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + title: '累计支出', + field: 'totalExpense', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + title: '累计充值', + field: 'totalRecharge', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + title: '冻结金额', + field: 'freezePrice', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + field: 'actions', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 钱包交易记录列表字段 */ +export function useTransactionGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'title', + title: '关联业务标题', + minWidth: 200, + }, + { + field: 'price', + title: '交易金额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'balance', + title: '钱包余额', + minWidth: 120, + formatter: 'formatAmount2', + }, + { + field: 'createTime', + title: '交易时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-ele/src/views/pay/wallet/balance/index.vue b/apps/web-ele/src/views/pay/wallet/balance/index.vue new file mode 100644 index 000000000..7235632bd --- /dev/null +++ b/apps/web-ele/src/views/pay/wallet/balance/index.vue @@ -0,0 +1,83 @@ + + + diff --git a/apps/web-ele/src/views/pay/wallet/balance/modules/detail.vue b/apps/web-ele/src/views/pay/wallet/balance/modules/detail.vue new file mode 100644 index 000000000..96749fd04 --- /dev/null +++ b/apps/web-ele/src/views/pay/wallet/balance/modules/detail.vue @@ -0,0 +1,65 @@ + + diff --git a/apps/web-ele/src/views/pay/wallet/rechargePackage/data.ts b/apps/web-ele/src/views/pay/wallet/rechargePackage/data.ts new file mode 100644 index 000000000..41caff6d1 --- /dev/null +++ b/apps/web-ele/src/views/pay/wallet/rechargePackage/data.ts @@ -0,0 +1,151 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '套餐名称', + component: 'Input', + rules: 'required', + componentProps: { + placeholder: '请输入套餐名称', + }, + }, + { + fieldName: 'payPrice', + label: '支付金额(元)', + component: 'InputNumber', + rules: z.number().min(0, '支付金额不能小于0'), + componentProps: { + min: 0, + precision: 2, + step: 0.01, + placeholder: '请输入支付金额', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'bonusPrice', + label: '赠送金额(元)', + component: 'InputNumber', + rules: z.number().min(0, '赠送金额不能小于0'), + componentProps: { + min: 0, + precision: 2, + step: 0.01, + placeholder: '请输入赠送金额', + controlsPosition: 'right', + class: '!w-full', + }, + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '套餐名称', + component: 'Input', + componentProps: { + placeholder: '请输入套餐名称', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '套餐编号', + minWidth: 100, + }, + { + field: 'name', + title: '套餐名称', + minWidth: 150, + }, + { + field: 'payPrice', + title: '支付金额', + minWidth: 120, + formatter: 'formatFenToYuanAmount', + }, + { + field: 'bonusPrice', + title: '赠送金额', + minWidth: 120, + formatter: 'formatFenToYuanAmount', + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/pay/wallet/rechargePackage/index.vue b/apps/web-ele/src/views/pay/wallet/rechargePackage/index.vue new file mode 100644 index 000000000..3d89c7b93 --- /dev/null +++ b/apps/web-ele/src/views/pay/wallet/rechargePackage/index.vue @@ -0,0 +1,130 @@ + + + diff --git a/apps/web-ele/src/views/pay/wallet/rechargePackage/modules/form.vue b/apps/web-ele/src/views/pay/wallet/rechargePackage/modules/form.vue new file mode 100644 index 000000000..2e1243c5e --- /dev/null +++ b/apps/web-ele/src/views/pay/wallet/rechargePackage/modules/form.vue @@ -0,0 +1,101 @@ + + + diff --git a/apps/web-ele/src/views/system/area/data.ts b/apps/web-ele/src/views/system/area/data.ts new file mode 100644 index 000000000..e6cc06c99 --- /dev/null +++ b/apps/web-ele/src/views/system/area/data.ts @@ -0,0 +1,48 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemAreaApi } from '#/api/system/area'; + +import { z } from '#/adapter/form'; + +/** 查询 IP 的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'ip', + label: 'IP 地址', + component: 'Input', + componentProps: { + placeholder: '请输入 IP 地址', + }, + rules: z.string().ip({ message: '请输入正确的 IP 地址' }), + }, + { + fieldName: 'result', + label: '地址', + component: 'Input', + componentProps: { + placeholder: '展示查询 IP 结果', + readonly: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '地区编码', + minWidth: 120, + align: 'left', + fixed: 'left', + treeNode: true, + }, + { + field: 'name', + title: '地区名称', + minWidth: 200, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/area/index.vue b/apps/web-ele/src/views/system/area/index.vue new file mode 100644 index 000000000..a07141905 --- /dev/null +++ b/apps/web-ele/src/views/system/area/index.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-ele/src/views/system/dept/components/dept-select-modal.vue b/apps/web-ele/src/views/system/dept/components/dept-select-modal.vue new file mode 100644 index 000000000..be14cf6ed --- /dev/null +++ b/apps/web-ele/src/views/system/dept/components/dept-select-modal.vue @@ -0,0 +1,141 @@ +// TODO @芋艿:是否有更好的组织形式?! + + diff --git a/apps/web-ele/src/views/system/dept/components/index.ts b/apps/web-ele/src/views/system/dept/components/index.ts new file mode 100644 index 000000000..d41597e97 --- /dev/null +++ b/apps/web-ele/src/views/system/dept/components/index.ts @@ -0,0 +1,2 @@ +// TODO @xingyu:【待讨论】是不是把 user select 放到 user 目录的 components 下,dept select 放到 dept 目录的 components 下 +export { default as DeptSelectModal } from './dept-select-modal.vue'; diff --git a/apps/web-ele/src/views/system/dept/data.ts b/apps/web-ele/src/views/system/dept/data.ts new file mode 100644 index 000000000..0bc18c1bc --- /dev/null +++ b/apps/web-ele/src/views/system/dept/data.ts @@ -0,0 +1,162 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemDeptApi } from '#/api/system/dept'; +import type { SystemUserApi } from '#/api/system/user'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { handleTree } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getDeptList } from '#/api/system/dept'; +import { getSimpleUserList } from '#/api/system/user'; + +/** 关联数据 */ +let userList: SystemUserApi.User[] = []; +getSimpleUserList().then((data) => (userList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级部门', + component: 'ApiTreeSelect', + componentProps: { + clearable: true, + api: async () => { + const data = await getDeptList(); + data.unshift({ + id: 0, + name: '顶级部门', + }); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级部门', + treeDefaultExpandAll: true, + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '部门名称', + component: 'Input', + componentProps: { + placeholder: '请输入部门名称', + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'leaderUserId', + label: '负责人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择负责人', + clearable: true, + }, + rules: z.number().optional(), + }, + { + fieldName: 'phone', + label: '联系电话', + component: 'Input', + componentProps: { + maxLength: 11, + placeholder: '请输入联系电话', + }, + rules: 'mobileRequired', + }, + { + fieldName: 'email', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + }, + rules: z.string().email('邮箱格式不正确').or(z.literal('')).optional(), + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'name', + title: '部门名称', + minWidth: 150, + align: 'left', + fixed: 'left', + treeNode: true, + }, + { + field: 'leaderUserId', + title: '负责人', + minWidth: 150, + formatter: ({ cellValue }) => + userList.find((user) => user.id === cellValue)?.nickname || '-', + }, + { + field: 'sort', + title: '显示顺序', + minWidth: 100, + }, + { + field: 'status', + title: '部门状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/dept/index.vue b/apps/web-ele/src/views/system/dept/index.vue new file mode 100644 index 000000000..0d3e39c87 --- /dev/null +++ b/apps/web-ele/src/views/system/dept/index.vue @@ -0,0 +1,193 @@ + + + diff --git a/apps/web-ele/src/views/system/dept/modules/form.vue b/apps/web-ele/src/views/system/dept/modules/form.vue new file mode 100644 index 000000000..b0136188b --- /dev/null +++ b/apps/web-ele/src/views/system/dept/modules/form.vue @@ -0,0 +1,84 @@ + + + diff --git a/apps/web-ele/src/views/system/dict/data.ts b/apps/web-ele/src/views/system/dict/data.ts new file mode 100644 index 000000000..a8fe7c683 --- /dev/null +++ b/apps/web-ele/src/views/system/dict/data.ts @@ -0,0 +1,347 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleDictTypeList } from '#/api/system/dict/type'; + +// ============================== 字典类型 ============================== + +/** 类型新增/修改的表单 */ +export function useTypeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '字典名称', + component: 'Input', + componentProps: { + placeholder: '请输入字典名称', + }, + rules: 'required', + }, + { + fieldName: 'type', + label: '字典类型', + component: 'Input', + componentProps: (values) => { + return { + placeholder: '请输入字典类型', + disabled: !!values.id, + }; + }, + rules: 'required', + dependencies: { + triggerFields: [''], + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 类型列表的搜索表单 */ +export function useTypeGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '字典名称', + component: 'Input', + componentProps: { + placeholder: '请输入字典名称', + clearable: true, + }, + }, + { + fieldName: 'type', + label: '字典类型', + component: 'Input', + componentProps: { + placeholder: '请输入字典类型', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + clearable: true, + }, + }, + ]; +} + +/** 类型列表的字段 */ +export function useTypeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '字典编号', + minWidth: 100, + }, + { + field: 'name', + title: '字典名称', + minWidth: 200, + }, + { + field: 'type', + title: '字典类型', + minWidth: 220, + }, + { + field: 'status', + title: '状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + minWidth: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ============================== 字典数据 ============================== + +// TODO @芋艿:后续针对 antd,增加 +/** 颜色选项 */ +const colorOptions = [ + { value: '', label: '无' }, + { value: 'processing', label: '主要' }, + { value: 'success', label: '成功' }, + { value: 'default', label: '默认' }, + { value: 'warning', label: '警告' }, + { value: 'error', label: '危险' }, + { value: 'pink', label: 'pink' }, + { value: 'red', label: 'red' }, + { value: 'orange', label: 'orange' }, + { value: 'green', label: 'green' }, + { value: 'cyan', label: 'cyan' }, + { value: 'blue', label: 'blue' }, + { value: 'purple', label: 'purple' }, +]; + +/** 数据新增/修改的表单 */ +export function useDataFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'dictType', + label: '字典类型', + component: 'ApiSelect', + componentProps: (values) => { + return { + api: getSimpleDictTypeList, + placeholder: '请输入字典类型', + labelField: 'name', + valueField: 'type', + disabled: !!values.id, + }; + }, + rules: 'required', + dependencies: { + triggerFields: [''], + }, + }, + { + fieldName: 'label', + label: '数据标签', + component: 'Input', + componentProps: { + placeholder: '请输入数据标签', + }, + rules: 'required', + }, + { + fieldName: 'value', + label: '数据键值', + component: 'Input', + componentProps: { + placeholder: '请输入数据键值', + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示排序', + component: 'InputNumber', + componentProps: { + placeholder: '请输入显示排序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'colorType', + label: '颜色类型', + component: 'Select', + componentProps: { + options: colorOptions, + placeholder: '请选择颜色类型', + }, + }, + { + fieldName: 'cssClass', + label: 'CSS Class', + component: 'Input', + componentProps: { + placeholder: '请输入 CSS Class', + }, + help: '输入 hex 模式的颜色, 例如 #108ee9', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 字典数据列表搜索表单 */ +export function useDataGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'label', + label: '字典标签', + component: 'Input', + componentProps: { + placeholder: '请输入字典标签', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + clearable: true, + }, + }, + ]; +} + +/** 字典数据表格列 */ +export function useDataGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '字典编码', + minWidth: 100, + }, + { + field: 'label', + title: '字典标签', + minWidth: 180, + }, + { + field: 'value', + title: '字典键值', + minWidth: 100, + }, + { + field: 'sort', + title: '字典排序', + minWidth: 100, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'colorType', + title: '颜色类型', + minWidth: 120, + }, + { + field: 'cssClass', + title: 'CSS Class', + minWidth: 120, + }, + { + title: '创建时间', + field: 'createTime', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + minWidth: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/dict/index.vue b/apps/web-ele/src/views/system/dict/index.vue new file mode 100644 index 000000000..8d824256d --- /dev/null +++ b/apps/web-ele/src/views/system/dict/index.vue @@ -0,0 +1,33 @@ + + + diff --git a/apps/web-ele/src/views/system/dict/modules/data-form.vue b/apps/web-ele/src/views/system/dict/modules/data-form.vue new file mode 100644 index 000000000..6f3203088 --- /dev/null +++ b/apps/web-ele/src/views/system/dict/modules/data-form.vue @@ -0,0 +1,90 @@ + + + diff --git a/apps/web-ele/src/views/system/dict/modules/data-grid.vue b/apps/web-ele/src/views/system/dict/modules/data-grid.vue new file mode 100644 index 000000000..54053c386 --- /dev/null +++ b/apps/web-ele/src/views/system/dict/modules/data-grid.vue @@ -0,0 +1,202 @@ + + + diff --git a/apps/web-ele/src/views/system/dict/modules/type-form.vue b/apps/web-ele/src/views/system/dict/modules/type-form.vue new file mode 100644 index 000000000..5e7cce34c --- /dev/null +++ b/apps/web-ele/src/views/system/dict/modules/type-form.vue @@ -0,0 +1,86 @@ + + + diff --git a/apps/web-ele/src/views/system/dict/modules/type-grid.vue b/apps/web-ele/src/views/system/dict/modules/type-grid.vue new file mode 100644 index 000000000..d2e7ec083 --- /dev/null +++ b/apps/web-ele/src/views/system/dict/modules/type-grid.vue @@ -0,0 +1,189 @@ + + + diff --git a/apps/web-ele/src/views/system/loginlog/data.ts b/apps/web-ele/src/views/system/loginlog/data.ts new file mode 100644 index 000000000..2be5cdf1c --- /dev/null +++ b/apps/web-ele/src/views/system/loginlog/data.ts @@ -0,0 +1,147 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'username', + label: '用户名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入用户名称', + }, + }, + { + fieldName: 'userIp', + label: '登录地址', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入登录地址', + }, + }, + { + fieldName: 'createTime', + label: '登录时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'logType', + title: '操作类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_LOGIN_TYPE }, + }, + }, + { + field: 'username', + title: '用户名称', + minWidth: 180, + }, + { + field: 'userIp', + title: '登录地址', + minWidth: 180, + }, + { + field: 'userAgent', + title: '浏览器', + minWidth: 200, + }, + { + field: 'result', + title: '登录结果', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_LOGIN_RESULT }, + }, + }, + { + field: 'createTime', + title: '登录日期', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'logType', + label: '操作类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_LOGIN_TYPE, + value: val, + }); + }, + }, + { + field: 'username', + label: '用户名称', + }, + { + field: 'userIp', + label: '登录地址', + }, + { + field: 'userAgent', + label: '浏览器', + }, + { + field: 'result', + label: '登录结果', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_LOGIN_RESULT, + value: val, + }); + }, + }, + { + field: 'createTime', + label: '登录日期', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/loginlog/index.vue b/apps/web-ele/src/views/system/loginlog/index.vue new file mode 100644 index 000000000..f5c5dd958 --- /dev/null +++ b/apps/web-ele/src/views/system/loginlog/index.vue @@ -0,0 +1,104 @@ + + + diff --git a/apps/web-ele/src/views/system/loginlog/modules/detail.vue b/apps/web-ele/src/views/system/loginlog/modules/detail.vue new file mode 100644 index 000000000..d1c9bea00 --- /dev/null +++ b/apps/web-ele/src/views/system/loginlog/modules/detail.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/system/mail/account/data.ts b/apps/web-ele/src/views/system/mail/account/data.ts new file mode 100644 index 000000000..ce36cb0f5 --- /dev/null +++ b/apps/web-ele/src/views/system/mail/account/data.ts @@ -0,0 +1,183 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'mail', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + }, + rules: 'required', + }, + { + fieldName: 'username', + label: '用户名', + component: 'Input', + componentProps: { + placeholder: '请输入用户名', + }, + rules: 'required', + }, + { + fieldName: 'password', + label: '密码', + component: 'Input', + componentProps: { + showPassword: true, + placeholder: '请输入密码', + }, + rules: 'required', + }, + { + fieldName: 'host', + label: 'SMTP 服务器域名', + component: 'Input', + componentProps: { + placeholder: '请输入 SMTP 服务器域名', + }, + rules: 'required', + }, + { + fieldName: 'port', + label: 'SMTP 服务器端口', + component: 'InputNumber', + componentProps: { + placeholder: '请输入 SMTP 服务器端口', + min: 0, + max: 65_535, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'sslEnable', + label: '是否开启 SSL', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + }, + rules: z.boolean().default(true), + }, + { + fieldName: 'starttlsEnable', + label: '是否开启 STARTTLS', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + }, + rules: z.boolean().default(false), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'mail', + label: '邮箱', + component: 'Input', + componentProps: { + placeholder: '请输入邮箱', + clearable: true, + }, + }, + { + fieldName: 'username', + label: '用户名', + component: 'Input', + componentProps: { + placeholder: '请输入用户名', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'mail', + title: '邮箱', + minWidth: 160, + }, + { + field: 'username', + title: '用户名', + minWidth: 160, + }, + { + field: 'host', + title: 'SMTP 服务器域名', + minWidth: 150, + }, + { + field: 'port', + title: 'SMTP 服务器端口', + minWidth: 130, + }, + { + field: 'sslEnable', + title: '是否开启 SSL', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'starttlsEnable', + title: '是否开启 STARTTLS', + minWidth: 145, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/mail/account/index.vue b/apps/web-ele/src/views/system/mail/account/index.vue new file mode 100644 index 000000000..6a15a08d9 --- /dev/null +++ b/apps/web-ele/src/views/system/mail/account/index.vue @@ -0,0 +1,172 @@ + + diff --git a/apps/web-ele/src/views/system/mail/account/modules/form.vue b/apps/web-ele/src/views/system/mail/account/modules/form.vue new file mode 100644 index 000000000..a7fc39742 --- /dev/null +++ b/apps/web-ele/src/views/system/mail/account/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/system/mail/log/data.ts b/apps/web-ele/src/views/system/mail/log/data.ts new file mode 100644 index 000000000..cef8cd7f3 --- /dev/null +++ b/apps/web-ele/src/views/system/mail/log/data.ts @@ -0,0 +1,259 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { getSimpleMailAccountList } from '#/api/system/mail/account'; +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'sendTime', + label: '发送时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + clearable: true, + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'sendStatus', + label: '发送状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_MAIL_SEND_STATUS, 'number'), + clearable: true, + placeholder: '请选择发送状态', + }, + }, + { + fieldName: 'accountId', + label: '邮箱账号', + component: 'ApiSelect', + componentProps: { + api: getSimpleMailAccountList, + labelField: 'mail', + valueField: 'id', + clearable: true, + placeholder: '请选择邮箱账号', + }, + }, + { + fieldName: 'templateId', + label: '模板编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板编号', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'sendTime', + title: '发送时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'userType', + title: '接收用户', + minWidth: 150, + slots: { default: 'userInfo' }, + }, + { + field: 'toMails', + title: '接收信息', + minWidth: 300, + formatter: ({ row }) => { + const lines: string[] = []; + if (row.toMails && row.toMails.length > 0) { + lines.push(`收件:${row.toMails.join('、')}`); + } + if (row.ccMails && row.ccMails.length > 0) { + lines.push(`抄送:${row.ccMails.join('、')}`); + } + if (row.bccMails && row.bccMails.length > 0) { + lines.push(`密送:${row.bccMails.join('、')}`); + } + return lines.join('\n'); + }, + }, + { + field: 'templateTitle', + title: '邮件标题', + minWidth: 120, + }, + { + field: 'templateContent', + title: '邮件内容', + minWidth: 300, + }, + { + field: 'fromMail', + title: '发送邮箱', + minWidth: 120, + }, + { + field: 'sendStatus', + title: '发送状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_MAIL_SEND_STATUS }, + }, + }, + { + field: 'templateCode', + title: '模板编码', + minWidth: 120, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '编号', + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => { + return formatDateTime(val) as string; + }, + }, + { + field: 'fromMail', + label: '发送邮箱', + }, + { + field: 'userId', + label: '接收用户', + render: (val, data) => { + if (data?.userType && val) { + return h('div', [ + h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: data.userType, + }), + ` (${val})`, + ]); + } + return '无'; + }, + }, + { + field: 'toMails', + label: '接收信息', + render: (val, data) => { + const lines: string[] = []; + if (val && val.length > 0) { + lines.push(`收件:${val.join('、')}`); + } + if (data?.ccMails && data.ccMails.length > 0) { + lines.push(`抄送:${data.ccMails.join('、')}`); + } + if (data?.bccMails && data.bccMails.length > 0) { + lines.push(`密送:${data.bccMails.join('、')}`); + } + return h( + 'div', + { + style: { whiteSpace: 'pre-line' }, + }, + lines.join('\n'), + ); + }, + }, + { + field: 'templateId', + label: '模板编号', + }, + { + field: 'templateCode', + label: '模板编码', + }, + { + field: 'templateTitle', + label: '邮件标题', + }, + { + field: 'templateContent', + label: '邮件内容', + span: 2, + render: (val) => { + return h('div', { + innerHTML: val || '', + }); + }, + }, + { + field: 'sendStatus', + label: '发送状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_MAIL_SEND_STATUS, + value: val, + }); + }, + }, + { + field: 'sendTime', + label: '发送时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'sendMessageId', + label: '发送消息编号', + }, + { + field: 'sendException', + label: '发送异常', + }, + ]; +} diff --git a/apps/web-ele/src/views/system/mail/log/index.vue b/apps/web-ele/src/views/system/mail/log/index.vue new file mode 100644 index 000000000..548f48508 --- /dev/null +++ b/apps/web-ele/src/views/system/mail/log/index.vue @@ -0,0 +1,92 @@ + + diff --git a/apps/web-ele/src/views/system/mail/log/modules/detail.vue b/apps/web-ele/src/views/system/mail/log/modules/detail.vue new file mode 100644 index 000000000..ab0daa76e --- /dev/null +++ b/apps/web-ele/src/views/system/mail/log/modules/detail.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/system/mail/template/data.ts b/apps/web-ele/src/views/system/mail/template/data.ts new file mode 100644 index 000000000..e57e92f17 --- /dev/null +++ b/apps/web-ele/src/views/system/mail/template/data.ts @@ -0,0 +1,258 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleMailAccountList } from '#/api/system/mail/account'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + placeholder: '请输入模板编码', + }, + rules: 'required', + }, + { + fieldName: 'accountId', + label: '邮箱账号', + component: 'ApiSelect', + componentProps: { + api: getSimpleMailAccountList, + labelField: 'mail', + valueField: 'id', + placeholder: '请选择邮箱账号', + }, + rules: 'required', + }, + { + fieldName: 'nickname', + label: '发送人名称', + component: 'Input', + componentProps: { + placeholder: '请输入发送人名称', + }, + }, + { + fieldName: 'title', + label: '模板标题', + component: 'Input', + componentProps: { + placeholder: '请输入模板标题', + }, + rules: 'required', + }, + { + fieldName: 'content', + label: '模板内容', + component: 'RichTextarea', + rules: 'required', + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 发送邮件表单 */ +export function useSendMailFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'templateParams', + label: '模板参数', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'content', + label: '模板内容', + component: 'RichTextarea', + componentProps: { + options: { + readonly: true, + }, + }, + }, + { + fieldName: 'toMails', + label: '收件邮箱', + component: 'InputTag', + componentProps: { + placeholder: '请输入收件邮箱,按 Enter 添加', + }, + }, + { + fieldName: 'ccMails', + label: '抄送邮箱', + component: 'InputTag', + componentProps: { + placeholder: '请输入抄送邮箱,按 Enter 添加', + }, + }, + { + fieldName: 'bccMails', + label: '密送邮箱', + component: 'InputTag', + componentProps: { + placeholder: '请输入密送邮箱,按 Enter 添加', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'status', + label: '开启状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + clearable: true, + placeholder: '请选择开启状态', + }, + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板编码', + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板名称', + }, + }, + { + fieldName: 'accountId', + label: '邮箱账号', + component: 'ApiSelect', + componentProps: { + api: getSimpleMailAccountList, + labelField: 'mail', + valueField: 'id', + clearable: true, + placeholder: '请选择邮箱账号', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + getAccountMail?: (accountId: number) => string | undefined, +): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'code', + title: '模板编码', + minWidth: 120, + }, + { + field: 'name', + title: '模板名称', + minWidth: 120, + }, + { + field: 'title', + title: '模板标题', + minWidth: 120, + }, + { + field: 'accountId', + title: '邮箱账号', + minWidth: 120, + formatter: ({ cellValue }) => getAccountMail?.(cellValue) || '-', + }, + { + field: 'nickname', + title: '发送人名称', + minWidth: 120, + }, + { + field: 'status', + title: '开启状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/mail/template/index.vue b/apps/web-ele/src/views/system/mail/template/index.vue new file mode 100644 index 000000000..6fc015f89 --- /dev/null +++ b/apps/web-ele/src/views/system/mail/template/index.vue @@ -0,0 +1,205 @@ + + diff --git a/apps/web-ele/src/views/system/mail/template/modules/form.vue b/apps/web-ele/src/views/system/mail/template/modules/form.vue new file mode 100644 index 000000000..4014e46dd --- /dev/null +++ b/apps/web-ele/src/views/system/mail/template/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/system/mail/template/modules/send-form.vue b/apps/web-ele/src/views/system/mail/template/modules/send-form.vue new file mode 100644 index 000000000..8f724ed66 --- /dev/null +++ b/apps/web-ele/src/views/system/mail/template/modules/send-form.vue @@ -0,0 +1,109 @@ + + + diff --git a/apps/web-ele/src/views/system/menu/data.ts b/apps/web-ele/src/views/system/menu/data.ts new file mode 100644 index 000000000..86c9fda8e --- /dev/null +++ b/apps/web-ele/src/views/system/menu/data.ts @@ -0,0 +1,318 @@ +import type { Recordable } from '@vben/types'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemMenuApi } from '#/api/system/menu'; + +import { h } from 'vue'; + +import { + CommonStatusEnum, + DICT_TYPE, + SystemMenuTypeEnum, +} from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { IconifyIcon } from '@vben/icons'; +import { handleTree, isHttpUrl } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getMenuList } from '#/api/system/menu'; +import { $t } from '#/locales'; +import { componentKeys } from '#/router/routes'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级菜单', + component: 'ApiTreeSelect', + componentProps: { + clearable: true, + api: async () => { + const data = await getMenuList(); + data.unshift({ + id: 0, + name: '顶级部门', + } as SystemMenuApi.Menu); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级菜单', + filterTreeNode(input: string, node: Recordable) { + if (!input || input.length === 0) { + return true; + } + const name: string = node.label ?? ''; + if (!name) return false; + return name.includes(input) || $t(name).includes(input); + }, + showSearch: true, + treeDefaultExpandedKeys: [0], + }, + rules: 'selectRequired', + renderComponentContent() { + return { + title({ label, icon }: { icon: string; label: string }) { + const components = []; + if (!label) return ''; + if (icon) { + components.push(h(IconifyIcon, { class: 'size-4', icon })); + } + components.push(h('span', { class: '' }, $t(label || ''))); + return h('div', { class: 'flex items-center gap-1' }, components); + }, + }; + }, + }, + { + fieldName: 'name', + label: '菜单名称', + component: 'Input', + componentProps: { + placeholder: '请输入菜单名称', + }, + rules: 'required', + }, + { + fieldName: 'type', + label: '菜单类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE, 'number'), + }, + rules: z.number().default(SystemMenuTypeEnum.DIR), + }, + { + fieldName: 'icon', + label: '菜单图标', + component: 'IconPicker', + componentProps: { + placeholder: '请选择菜单图标', + prefix: 'carbon', + }, + rules: 'required', + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.DIR, SystemMenuTypeEnum.MENU].includes( + values.type, + ); + }, + }, + }, + { + fieldName: 'path', + label: '路由地址', + component: 'Input', + componentProps: { + placeholder: '请输入路由地址', + }, + rules: z.string(), + help: '访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头', + dependencies: { + triggerFields: ['type', 'parentId'], + show: (values) => { + return [SystemMenuTypeEnum.DIR, SystemMenuTypeEnum.MENU].includes( + values.type, + ); + }, + rules: (values) => { + const schema = z.string().min(1, '路由地址不能为空'); + if (isHttpUrl(values.path)) { + return schema; + } + if (values.parentId === 0) { + return schema.refine( + (path) => path.charAt(0) === '/', + '路径必须以 / 开头', + ); + } + return schema.refine( + (path) => path.charAt(0) !== '/', + '路径不能以 / 开头', + ); + }, + }, + }, + { + fieldName: 'component', + label: '组件地址', + component: 'Input', + componentProps: { + placeholder: '请输入组件地址', + }, + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.MENU].includes(values.type); + }, + }, + }, + { + fieldName: 'componentName', + label: '组件名称', + component: 'AutoComplete', + componentProps: { + clearable: true, + filterOption(input: string, option: { value: string }) { + return option.value.toLowerCase().includes(input.toLowerCase()); + }, + placeholder: '请选择组件名称', + options: componentKeys.map((v) => ({ value: v })), + }, + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.MENU].includes(values.type); + }, + }, + }, + { + fieldName: 'permission', + label: '权限标识', + component: 'Input', + componentProps: { + placeholder: '请输入菜单描述', + }, + dependencies: { + show: (values) => { + return [SystemMenuTypeEnum.BUTTON, SystemMenuTypeEnum.MENU].includes( + values.type, + ); + }, + triggerFields: ['type'], + }, + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '菜单状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'alwaysShow', + label: '总是显示', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '总是', value: true }, + { label: '不是', value: false }, + ], + }, + rules: 'required', + defaultValue: true, + help: '选择不是时,当该菜单只有一个子菜单时,不展示自己,直接展示子菜单', + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.MENU].includes(values.type); + }, + }, + }, + { + fieldName: 'keepAlive', + label: '缓存状态', + component: 'RadioGroup', + componentProps: { + options: [ + { label: '缓存', value: true }, + { label: '不缓存', value: false }, + ], + }, + rules: 'required', + defaultValue: true, + help: '选择缓存时,则会被 `keep-alive` 缓存,必须填写「组件名称」字段', + dependencies: { + triggerFields: ['type'], + show: (values) => { + return [SystemMenuTypeEnum.MENU].includes(values.type); + }, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '菜单名称', + minWidth: 250, + align: 'left', + fixed: 'left', + slots: { default: 'name' }, + treeNode: true, + }, + { + field: 'type', + title: '菜单类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_MENU_TYPE }, + }, + }, + { + field: 'sort', + title: '显示排序', + minWidth: 100, + }, + { + field: 'permission', + title: '权限标识', + minWidth: 200, + }, + { + field: 'path', + title: '组件路径', + minWidth: 200, + }, + { + field: 'componentName', + title: '组件名称', + minWidth: 200, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/menu/index.vue b/apps/web-ele/src/views/system/menu/index.vue new file mode 100644 index 000000000..783bc74d0 --- /dev/null +++ b/apps/web-ele/src/views/system/menu/index.vue @@ -0,0 +1,181 @@ + + + diff --git a/apps/web-ele/src/views/system/menu/modules/form.vue b/apps/web-ele/src/views/system/menu/modules/form.vue new file mode 100644 index 000000000..9a91c16f5 --- /dev/null +++ b/apps/web-ele/src/views/system/menu/modules/form.vue @@ -0,0 +1,84 @@ + + + diff --git a/apps/web-ele/src/views/system/notice/data.ts b/apps/web-ele/src/views/system/notice/data.ts new file mode 100644 index 000000000..1746f6915 --- /dev/null +++ b/apps/web-ele/src/views/system/notice/data.ts @@ -0,0 +1,134 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'title', + label: '公告标题', + component: 'Input', + componentProps: { + placeholder: '请输入公告标题', + }, + rules: 'required', + }, + { + fieldName: 'type', + label: '公告类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_NOTICE_TYPE, 'number'), + }, + rules: 'required', + }, + { + fieldName: 'content', + label: '公告内容', + component: 'RichTextarea', + rules: 'required', + }, + { + fieldName: 'status', + label: '公告状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'title', + label: '公告标题', + component: 'Input', + componentProps: { + placeholder: '请输入公告标题', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '公告状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择公告状态', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '公告编号', + minWidth: 100, + }, + { + field: 'title', + title: '公告标题', + minWidth: 200, + }, + { + field: 'type', + title: '公告类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_NOTICE_TYPE }, + }, + }, + { + field: 'status', + title: '公告状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/notice/index.vue b/apps/web-ele/src/views/system/notice/index.vue new file mode 100644 index 000000000..1809a1a17 --- /dev/null +++ b/apps/web-ele/src/views/system/notice/index.vue @@ -0,0 +1,191 @@ + + + diff --git a/apps/web-ele/src/views/system/notice/modules/form.vue b/apps/web-ele/src/views/system/notice/modules/form.vue new file mode 100644 index 000000000..f00a4972d --- /dev/null +++ b/apps/web-ele/src/views/system/notice/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/system/notify/message/data.ts b/apps/web-ele/src/views/system/notify/message/data.ts new file mode 100644 index 000000000..c786ee144 --- /dev/null +++ b/apps/web-ele/src/views/system/notify/message/data.ts @@ -0,0 +1,237 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'templateCode', + label: '模板编码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板编码', + }, + }, + { + fieldName: 'templateType', + label: '模版类型', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + 'number', + ), + clearable: true, + placeholder: '请选择模版类型', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'templateCode', + title: '模板编码', + minWidth: 120, + }, + { + field: 'templateNickname', + title: '发送人名称', + minWidth: 180, + }, + { + field: 'templateContent', + title: '模版内容', + minWidth: 200, + }, + { + field: 'templateParams', + title: '模版参数', + minWidth: 180, + formatter: ({ cellValue }) => { + try { + return JSON.stringify(cellValue); + } catch { + return ''; + } + }, + }, + { + field: 'templateType', + title: '模版类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, + }, + }, + { + field: 'readStatus', + title: '是否已读', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'readTime', + title: '阅读时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '编号', + }, + { + field: 'userType', + label: '用户类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: val, + }); + }, + }, + { + field: 'userId', + label: '用户编号', + }, + { + field: 'templateId', + label: '模版编号', + }, + { + field: 'templateCode', + label: '模板编码', + }, + { + field: 'templateNickname', + label: '发送人名称', + }, + { + field: 'templateContent', + label: '模版内容', + }, + { + field: 'templateParams', + label: '模版参数', + render: (val) => { + try { + return JSON.stringify(val); + } catch { + return ''; + } + }, + }, + { + field: 'templateType', + label: '模版类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + value: val, + }); + }, + }, + { + field: 'readStatus', + label: '是否已读', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_BOOLEAN_STRING, + value: val, + }); + }, + }, + { + field: 'readTime', + label: '阅读时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/notify/message/index.vue b/apps/web-ele/src/views/system/notify/message/index.vue new file mode 100644 index 000000000..fcac93249 --- /dev/null +++ b/apps/web-ele/src/views/system/notify/message/index.vue @@ -0,0 +1,84 @@ + + + diff --git a/apps/web-ele/src/views/system/notify/message/modules/detail.vue b/apps/web-ele/src/views/system/notify/message/modules/detail.vue new file mode 100644 index 000000000..f7dbc8f58 --- /dev/null +++ b/apps/web-ele/src/views/system/notify/message/modules/detail.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/system/notify/my/data.ts b/apps/web-ele/src/views/system/notify/my/data.ts new file mode 100644 index 000000000..21f051d6d --- /dev/null +++ b/apps/web-ele/src/views/system/notify/my/data.ts @@ -0,0 +1,137 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'readStatus', + label: '是否已读', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + clearable: true, + placeholder: '请选择是否已读', + }, + }, + { + fieldName: 'createTime', + label: '发送时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + title: '', + width: 40, + type: 'checkbox', + }, + { + field: 'templateNickname', + title: '发送人', + minWidth: 180, + }, + { + field: 'createTime', + title: '发送时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'templateType', + title: '类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, + }, + }, + { + field: 'templateContent', + title: '消息内容', + minWidth: 300, + }, + { + field: 'readStatus', + title: '是否已读', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'readTime', + title: '阅读时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'templateNickname', + label: '发送人', + }, + { + field: 'createTime', + label: '发送时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'templateType', + label: '消息类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + value: val, + }); + }, + }, + { + field: 'readStatus', + label: '是否已读', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_BOOLEAN_STRING, + value: val, + }); + }, + }, + { + field: 'readTime', + label: '阅读时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'templateContent', + label: '消息内容', + }, + ]; +} diff --git a/apps/web-ele/src/views/system/notify/my/index.vue b/apps/web-ele/src/views/system/notify/my/index.vue new file mode 100644 index 000000000..9c37b5254 --- /dev/null +++ b/apps/web-ele/src/views/system/notify/my/index.vue @@ -0,0 +1,191 @@ + + diff --git a/apps/web-ele/src/views/system/notify/my/modules/detail.vue b/apps/web-ele/src/views/system/notify/my/modules/detail.vue new file mode 100644 index 000000000..f7dbc8f58 --- /dev/null +++ b/apps/web-ele/src/views/system/notify/my/modules/detail.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/system/notify/template/data.ts b/apps/web-ele/src/views/system/notify/template/data.ts new file mode 100644 index 000000000..5aedc89dc --- /dev/null +++ b/apps/web-ele/src/views/system/notify/template/data.ts @@ -0,0 +1,286 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE, UserTypeEnum } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + placeholder: '请输入模板编码', + }, + rules: 'required', + }, + { + fieldName: 'nickname', + label: '发送人名称', + component: 'Input', + componentProps: { + placeholder: '请输入发送人名称', + }, + rules: 'required', + }, + { + fieldName: 'content', + label: '模板内容', + component: 'Textarea', + componentProps: { + placeholder: '请输入模板内容', + }, + rules: 'required', + }, + { + fieldName: 'type', + label: '模板类型', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + 'number', + ), + placeholder: '请选择模板类型', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板名称', + }, + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板编码', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + clearable: true, + placeholder: '请选择状态', + }, + }, + { + fieldName: 'type', + label: '模板类型', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE, + 'number', + ), + clearable: true, + placeholder: '请选择模板类型', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 发送站内信表单 */ +export function useSendNotifyFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'content', + label: '模板内容', + component: 'Textarea', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'templateCode', + label: '模板编码', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + }, + rules: z.number().default(UserTypeEnum.MEMBER), + }, + { + fieldName: 'userId', + label: '接收人 ID', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + }, + dependencies: { + show(values) { + return values.userType === UserTypeEnum.MEMBER; + }, + triggerFields: ['userType'], + }, + rules: 'required', + }, + { + fieldName: 'userId', + label: '接收人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + placeholder: '请选择接收人', + }, + dependencies: { + show(values) { + return values.userType === UserTypeEnum.ADMIN; + }, + triggerFields: ['userType'], + }, + rules: 'required', + }, + { + fieldName: 'templateParams', + label: '模板参数', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '模板名称', + minWidth: 120, + }, + { + field: 'code', + title: '模板编码', + minWidth: 120, + }, + { + field: 'nickname', + title: '发送人名称', + minWidth: 120, + }, + { + field: 'content', + title: '模板内容', + minWidth: 200, + }, + { + field: 'type', + title: '模板类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_NOTIFY_TEMPLATE_TYPE }, + }, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/notify/template/index.vue b/apps/web-ele/src/views/system/notify/template/index.vue new file mode 100644 index 000000000..33c2bf83a --- /dev/null +++ b/apps/web-ele/src/views/system/notify/template/index.vue @@ -0,0 +1,207 @@ + + + diff --git a/apps/web-ele/src/views/system/notify/template/modules/form.vue b/apps/web-ele/src/views/system/notify/template/modules/form.vue new file mode 100644 index 000000000..3ce13ff2f --- /dev/null +++ b/apps/web-ele/src/views/system/notify/template/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/system/notify/template/modules/send-form.vue b/apps/web-ele/src/views/system/notify/template/modules/send-form.vue new file mode 100644 index 000000000..0ed7404ad --- /dev/null +++ b/apps/web-ele/src/views/system/notify/template/modules/send-form.vue @@ -0,0 +1,112 @@ + + + diff --git a/apps/web-ele/src/views/system/oauth2/client/data.ts b/apps/web-ele/src/views/system/oauth2/client/data.ts new file mode 100644 index 000000000..5705cb06c --- /dev/null +++ b/apps/web-ele/src/views/system/oauth2/client/data.ts @@ -0,0 +1,261 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'clientId', + label: '客户端编号', + component: 'Input', + componentProps: { + placeholder: '请输入客户端编号', + }, + rules: 'required', + }, + { + fieldName: 'secret', + label: '客户端密钥', + component: 'Input', + componentProps: { + placeholder: '请输入客户端密钥', + }, + rules: 'required', + }, + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + }, + rules: 'required', + }, + { + fieldName: 'logo', + label: '应用图标', + component: 'ImageUpload', + rules: 'required', + }, + { + fieldName: 'description', + label: '应用描述', + component: 'Textarea', + componentProps: { + placeholder: '请输入应用描述', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'accessTokenValiditySeconds', + label: '访问令牌的有效期', + component: 'InputNumber', + componentProps: { + placeholder: '请输入访问令牌的有效期,单位:秒', + min: 0, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'refreshTokenValiditySeconds', + label: '刷新令牌的有效期', + component: 'InputNumber', + componentProps: { + placeholder: '请输入刷新令牌的有效期,单位:秒', + min: 0, + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'authorizedGrantTypes', + label: '授权类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_OAUTH2_GRANT_TYPE), + multiple: true, + placeholder: '请输入授权类型', + }, + rules: 'required', + }, + { + fieldName: 'scopes', + label: '授权范围', + component: 'InputTag', + componentProps: { + placeholder: '请输入授权范围', + }, + }, + { + fieldName: 'autoApproveScopes', + label: '自动授权范围', + component: 'Select', + componentProps: { + placeholder: '请输入自动授权范围', + multiple: true, + options: [], + }, + dependencies: { + triggerFields: ['scopes'], + componentProps: (values) => ({ + options: values.scopes + ? values.scopes.map((scope: string) => ({ + label: scope, + value: scope, + })) + : [], + }), + }, + }, + { + fieldName: 'redirectUris', + label: '可重定向的 URI 地址', + component: 'InputTag', + componentProps: { + placeholder: '请输入可重定向的 URI 地址', + }, + rules: 'required', + }, + { + fieldName: 'authorities', + label: '权限', + component: 'InputTag', + componentProps: { + placeholder: '请输入权限', + }, + }, + { + fieldName: 'resourceIds', + label: '资源', + component: 'InputTag', + componentProps: { + placeholder: '请输入资源', + }, + }, + { + fieldName: 'additionalInformation', + label: '附加信息', + component: 'Textarea', + componentProps: { + placeholder: '请输入附加信息,JSON 格式数据', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + clearable: true, + placeholder: '请输入状态', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'clientId', + title: '客户端编号', + minWidth: 120, + }, + { + field: 'secret', + title: '客户端密钥', + minWidth: 120, + }, + { + field: 'name', + title: '应用名', + minWidth: 120, + }, + { + field: 'logo', + title: '应用图标', + minWidth: 100, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'status', + title: '状态', + minWidth: 80, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'accessTokenValiditySeconds', + title: '访问令牌的有效期', + minWidth: 150, + formatter: ({ cellValue }) => `${cellValue} 秒`, + }, + { + field: 'refreshTokenValiditySeconds', + title: '刷新令牌的有效期', + minWidth: 150, + formatter: ({ cellValue }) => `${cellValue} 秒`, + }, + { + field: 'authorizedGrantTypes', + title: '授权类型', + minWidth: 100, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/oauth2/client/index.vue b/apps/web-ele/src/views/system/oauth2/client/index.vue new file mode 100644 index 000000000..2ee404796 --- /dev/null +++ b/apps/web-ele/src/views/system/oauth2/client/index.vue @@ -0,0 +1,176 @@ + + + diff --git a/apps/web-ele/src/views/system/oauth2/client/modules/form.vue b/apps/web-ele/src/views/system/oauth2/client/modules/form.vue new file mode 100644 index 000000000..927e42331 --- /dev/null +++ b/apps/web-ele/src/views/system/oauth2/client/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/system/oauth2/token/data.ts b/apps/web-ele/src/views/system/oauth2/token/data.ts new file mode 100644 index 000000000..51a9d59c8 --- /dev/null +++ b/apps/web-ele/src/views/system/oauth2/token/data.ts @@ -0,0 +1,93 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + placeholder: '请输入用户编号', + clearable: true, + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + placeholder: '请选择用户类型', + clearable: true, + }, + }, + { + fieldName: 'clientId', + label: '客户端编号', + component: 'Input', + componentProps: { + placeholder: '请输入客户端编号', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'accessToken', + title: '访问令牌', + minWidth: 300, + }, + { + field: 'refreshToken', + title: '刷新令牌', + minWidth: 300, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'clientId', + title: '客户端编号', + minWidth: 120, + }, + { + field: 'expiresTime', + title: '过期时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/oauth2/token/index.vue b/apps/web-ele/src/views/system/oauth2/token/index.vue new file mode 100644 index 000000000..e2af94403 --- /dev/null +++ b/apps/web-ele/src/views/system/oauth2/token/index.vue @@ -0,0 +1,143 @@ + + + diff --git a/apps/web-ele/src/views/system/operatelog/data.ts b/apps/web-ele/src/views/system/operatelog/data.ts new file mode 100644 index 000000000..67b321fe2 --- /dev/null +++ b/apps/web-ele/src/views/system/operatelog/data.ts @@ -0,0 +1,191 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { formatDateTime } from '@vben/utils'; + +import { getSimpleUserList } from '#/api/system/user'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '操作人', + component: 'ApiSelect', + componentProps: { + api: getSimpleUserList, + labelField: 'nickname', + valueField: 'id', + clearable: true, + placeholder: '请选择操作人员', + }, + }, + { + fieldName: 'type', + label: '操作模块', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入操作模块', + }, + }, + { + fieldName: 'subType', + label: '操作名', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入操作名', + }, + }, + { + fieldName: 'action', + label: '操作内容', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入操作内容', + }, + }, + { + fieldName: 'createTime', + label: '操作时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'bizId', + label: '业务编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入业务编号', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'userName', + title: '操作人', + minWidth: 120, + }, + { + field: 'type', + title: '操作模块', + minWidth: 120, + }, + { + field: 'subType', + title: '操作名', + minWidth: 160, + }, + { + field: 'action', + title: '操作内容', + minWidth: 200, + }, + { + field: 'createTime', + title: '操作时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'bizId', + title: '业务编号', + minWidth: 120, + }, + { + field: 'userIp', + title: '操作 IP', + minWidth: 120, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'traceId', + label: '链路追踪', + show: (val) => !val, + }, + { + field: 'userId', + label: '操作人编号', + }, + { + field: 'userName', + label: '操作人名字', + }, + { + field: 'userIp', + label: '操作人 IP', + }, + { + field: 'userAgent', + label: '操作人 UA', + }, + { + field: 'type', + label: '操作模块', + }, + { + field: 'subType', + label: '操作名', + }, + { + field: 'action', + label: '操作内容', + }, + { + field: 'extra', + label: '操作拓展参数', + show: (val) => !val, + }, + { + field: 'requestUrl', + label: '请求 URL', + render: (val, data) => { + if (data?.requestMethod && val) { + return `${data.requestMethod} ${val}`; + } + return ''; + }, + }, + { + field: 'createTime', + label: '操作时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'bizId', + label: '业务编号', + }, + ]; +} diff --git a/apps/web-ele/src/views/system/operatelog/index.vue b/apps/web-ele/src/views/system/operatelog/index.vue new file mode 100644 index 000000000..a67735d32 --- /dev/null +++ b/apps/web-ele/src/views/system/operatelog/index.vue @@ -0,0 +1,104 @@ + + + diff --git a/apps/web-ele/src/views/system/operatelog/modules/detail.vue b/apps/web-ele/src/views/system/operatelog/modules/detail.vue new file mode 100644 index 000000000..f4d8ae922 --- /dev/null +++ b/apps/web-ele/src/views/system/operatelog/modules/detail.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/system/post/data.ts b/apps/web-ele/src/views/system/post/data.ts new file mode 100644 index 000000000..d9c94a363 --- /dev/null +++ b/apps/web-ele/src/views/system/post/data.ts @@ -0,0 +1,155 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'Input', + fieldName: 'name', + label: '岗位名称', + componentProps: { + placeholder: '请输入岗位名称', + }, + rules: 'required', + }, + { + component: 'Input', + fieldName: 'code', + label: '岗位编码', + componentProps: { + placeholder: '请输入岗位编码', + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '岗位状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '岗位备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入岗位备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '岗位名称', + component: 'Input', + componentProps: { + placeholder: '请输入岗位名称', + clearable: true, + }, + }, + { + fieldName: 'code', + label: '岗位编码', + component: 'Input', + componentProps: { + placeholder: '请输入岗位编码', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '岗位状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择岗位状态', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '岗位编号', + minWidth: 200, + }, + { + field: 'name', + title: '岗位名称', + minWidth: 200, + }, + { + field: 'code', + title: '岗位编码', + minWidth: 200, + }, + { + field: 'sort', + title: '显示顺序', + minWidth: 100, + }, + { + field: 'remark', + title: '岗位备注', + minWidth: 200, + }, + { + field: 'status', + title: '岗位状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/post/index.vue b/apps/web-ele/src/views/system/post/index.vue new file mode 100644 index 000000000..1d0c826b8 --- /dev/null +++ b/apps/web-ele/src/views/system/post/index.vue @@ -0,0 +1,183 @@ + + + diff --git a/apps/web-ele/src/views/system/post/modules/form.vue b/apps/web-ele/src/views/system/post/modules/form.vue new file mode 100644 index 000000000..87453a218 --- /dev/null +++ b/apps/web-ele/src/views/system/post/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/system/role/data.ts b/apps/web-ele/src/views/system/role/data.ts new file mode 100644 index 000000000..2d1ecb04d --- /dev/null +++ b/apps/web-ele/src/views/system/role/data.ts @@ -0,0 +1,264 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { + CommonStatusEnum, + DICT_TYPE, + SystemDataScopeEnum, +} from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + componentProps: { + placeholder: '请输入角色名称', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '角色标识', + component: 'Input', + componentProps: { + placeholder: '请输入角色标识', + }, + rules: 'required', + }, + { + fieldName: 'sort', + label: '显示顺序', + component: 'InputNumber', + componentProps: { + min: 0, + placeholder: '请输入显示顺序', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '角色状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '角色备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入角色备注', + }, + }, + ]; +} + +/** 分配数据权限的表单 */ +export function useAssignDataPermissionFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + component: 'Input', + fieldName: 'code', + label: '角色标识', + componentProps: { + disabled: true, + }, + }, + { + component: 'Select', + fieldName: 'dataScope', + label: '权限范围', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE, 'number'), + }, + }, + { + fieldName: 'dataScopeDeptIds', + label: '部门范围', + component: 'Input', + formItemClass: 'items-start', + dependencies: { + triggerFields: ['dataScope'], + show: (values) => { + return values.dataScope === SystemDataScopeEnum.DEPT_CUSTOM; + }, + }, + }, + ]; +} + +/** 分配菜单的表单 */ +export function useAssignMenuFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'code', + label: '角色标识', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'menuIds', + label: '菜单权限', + component: 'Input', + formItemClass: 'items-start', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '角色名称', + component: 'Input', + componentProps: { + placeholder: '请输入角色名称', + clearable: true, + }, + }, + { + fieldName: 'code', + label: '角色标识', + component: 'Input', + componentProps: { + placeholder: '请输入角色标识', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '角色状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择角色状态', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '角色编号', + minWidth: 100, + }, + { + field: 'name', + title: '角色名称', + minWidth: 200, + }, + { + field: 'type', + title: '角色类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_ROLE_TYPE }, + }, + }, + { + field: 'code', + title: '角色标识', + minWidth: 200, + }, + { + field: 'sort', + title: '显示顺序', + minWidth: 100, + }, + { + field: 'remark', + title: '角色备注', + minWidth: 100, + }, + { + field: 'status', + title: '角色状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 240, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/role/index.vue b/apps/web-ele/src/views/system/role/index.vue new file mode 100644 index 000000000..de79b9933 --- /dev/null +++ b/apps/web-ele/src/views/system/role/index.vue @@ -0,0 +1,232 @@ + + + diff --git a/apps/web-ele/src/views/system/role/modules/assign-data-permission-form.vue b/apps/web-ele/src/views/system/role/modules/assign-data-permission-form.vue new file mode 100644 index 000000000..c78f904b0 --- /dev/null +++ b/apps/web-ele/src/views/system/role/modules/assign-data-permission-form.vue @@ -0,0 +1,167 @@ + + + diff --git a/apps/web-ele/src/views/system/role/modules/assign-menu-form.vue b/apps/web-ele/src/views/system/role/modules/assign-menu-form.vue new file mode 100644 index 000000000..463a16a30 --- /dev/null +++ b/apps/web-ele/src/views/system/role/modules/assign-menu-form.vue @@ -0,0 +1,169 @@ + + + diff --git a/apps/web-ele/src/views/system/role/modules/form.vue b/apps/web-ele/src/views/system/role/modules/form.vue new file mode 100644 index 000000000..90522e39a --- /dev/null +++ b/apps/web-ele/src/views/system/role/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/system/sms/channel/data.ts b/apps/web-ele/src/views/system/sms/channel/data.ts new file mode 100644 index 000000000..f244c49a8 --- /dev/null +++ b/apps/web-ele/src/views/system/sms/channel/data.ts @@ -0,0 +1,193 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'signature', + label: '短信签名', + component: 'Input', + componentProps: { + placeholder: '请输入短信签名', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '渠道编码', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE, 'string'), + placeholder: '请选择短信渠道', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '启用状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + { + fieldName: 'apiKey', + label: '短信 API 的账号', + component: 'Input', + componentProps: { + placeholder: '请输入短信 API 的账号', + }, + rules: 'required', + }, + { + fieldName: 'apiSecret', + label: '短信 API 的密钥', + component: 'Input', + componentProps: { + placeholder: '请输入短信 API 的密钥', + }, + }, + { + fieldName: 'callbackUrl', + label: '短信发送回调 URL', + component: 'Input', + componentProps: { + placeholder: '请输入短信发送回调 URL', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'signature', + label: '短信签名', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入短信签名', + }, + }, + { + fieldName: 'code', + label: '渠道编码', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE, 'string'), + placeholder: '请选择短信渠道', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'signature', + title: '短信签名', + minWidth: 120, + }, + { + field: 'code', + title: '渠道编码', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE }, + }, + }, + { + field: 'status', + title: '启用状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'apiKey', + title: '短信 API 的账号', + minWidth: 180, + }, + { + field: 'apiSecret', + title: '短信 API 的密钥', + minWidth: 180, + }, + { + field: 'callbackUrl', + title: '短信发送回调 URL', + minWidth: 180, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'remark', + title: '备注', + minWidth: 120, + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/sms/channel/index.vue b/apps/web-ele/src/views/system/sms/channel/index.vue new file mode 100644 index 000000000..b4400a21f --- /dev/null +++ b/apps/web-ele/src/views/system/sms/channel/index.vue @@ -0,0 +1,173 @@ + + + diff --git a/apps/web-ele/src/views/system/sms/channel/modules/form.vue b/apps/web-ele/src/views/system/sms/channel/modules/form.vue new file mode 100644 index 000000000..2e11fc874 --- /dev/null +++ b/apps/web-ele/src/views/system/sms/channel/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-ele/src/views/system/sms/log/data.ts b/apps/web-ele/src/views/system/sms/log/data.ts new file mode 100644 index 000000000..3657516b6 --- /dev/null +++ b/apps/web-ele/src/views/system/sms/log/data.ts @@ -0,0 +1,265 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { getSimpleSmsChannelList } from '#/api/system/sms/channel'; +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'mobile', + label: '手机号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入手机号', + }, + }, + { + fieldName: 'channelId', + label: '短信渠道', + component: 'ApiSelect', + componentProps: { + api: getSimpleSmsChannelList, + labelField: 'signature', + valueField: 'id', + clearable: true, + placeholder: '请选择短信渠道', + }, + }, + { + fieldName: 'templateId', + label: '模板编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板编号', + }, + }, + { + fieldName: 'sendStatus', + label: '发送状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_SEND_STATUS, 'number'), + clearable: true, + placeholder: '请选择发送状态', + }, + }, + { + fieldName: 'sendTime', + label: '发送时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'receiveStatus', + label: '接收状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS, 'number'), + clearable: true, + placeholder: '请选择接收状态', + }, + }, + { + fieldName: 'receiveTime', + label: '接收时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'mobile', + title: '手机号', + minWidth: 120, + }, + { + field: 'templateContent', + title: '短信内容', + minWidth: 300, + }, + { + field: 'sendStatus', + title: '发送状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_SEND_STATUS }, + }, + }, + { + field: 'sendTime', + title: '发送时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'receiveStatus', + title: '接收状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS }, + }, + }, + { + field: 'receiveTime', + title: '接收时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'channelCode', + title: '短信渠道', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE }, + }, + }, + { + field: 'templateId', + title: '模板编号', + minWidth: 100, + }, + { + field: 'templateType', + title: '短信类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'createTime', + label: '创建时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'mobile', + label: '手机号', + }, + { + field: 'channelCode', + label: '短信渠道', + }, + { + field: 'templateId', + label: '模板编号', + }, + { + field: 'templateType', + label: '模板类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE, + value: val, + }); + }, + }, + { + field: 'templateContent', + label: '短信内容', + }, + { + field: 'sendStatus', + label: '发送状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_SMS_SEND_STATUS, + value: val, + }); + }, + }, + { + field: 'sendTime', + label: '发送时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'apiSendCode', + label: 'API 发送编码', + }, + { + field: 'apiSendMsg', + label: 'API 发送消息', + }, + { + field: 'receiveStatus', + label: '接收状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_SMS_RECEIVE_STATUS, + value: val, + }); + }, + }, + { + field: 'receiveTime', + label: '接收时间', + render: (val) => formatDateTime(val) as string, + }, + { + field: 'apiReceiveCode', + label: 'API 接收编码', + }, + { + field: 'apiReceiveMsg', + label: 'API 接收消息', + span: 2, + }, + { + field: 'apiRequestId', + label: 'API 请求 ID', + }, + { + field: 'apiSerialNo', + label: 'API 序列号', + }, + ]; +} diff --git a/apps/web-ele/src/views/system/sms/log/index.vue b/apps/web-ele/src/views/system/sms/log/index.vue new file mode 100644 index 000000000..0bd3a9272 --- /dev/null +++ b/apps/web-ele/src/views/system/sms/log/index.vue @@ -0,0 +1,104 @@ + + + diff --git a/apps/web-ele/src/views/system/sms/log/modules/detail.vue b/apps/web-ele/src/views/system/sms/log/modules/detail.vue new file mode 100644 index 000000000..37af10f7e --- /dev/null +++ b/apps/web-ele/src/views/system/sms/log/modules/detail.vue @@ -0,0 +1,50 @@ + + + diff --git a/apps/web-ele/src/views/system/sms/template/data.ts b/apps/web-ele/src/views/system/sms/template/data.ts new file mode 100644 index 000000000..0a3d3e814 --- /dev/null +++ b/apps/web-ele/src/views/system/sms/template/data.ts @@ -0,0 +1,272 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getSimpleSmsChannelList } from '#/api/system/sms/channel'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'type', + label: '短信类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE, 'number'), + placeholder: '请选择短信类型', + }, + rules: 'required', + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + placeholder: '请输入模板名称', + }, + rules: 'required', + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + placeholder: '请输入模板编码', + }, + rules: 'required', + }, + { + fieldName: 'channelId', + label: '短信渠道', + component: 'ApiSelect', + componentProps: { + api: getSimpleSmsChannelList, + labelField: 'signature', + valueField: 'id', + placeholder: '请选择短信渠道', + }, + rules: 'required', + }, + { + fieldName: 'status', + label: '开启状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'content', + label: '模板内容', + component: 'Textarea', + componentProps: { + placeholder: '请输入模板内容', + rows: 4, + }, + rules: 'required', + }, + { + fieldName: 'apiTemplateId', + label: '短信 API 的模板编号', + component: 'Input', + componentProps: { + placeholder: '请输入短信 API 的模板编号', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'type', + label: '短信类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE, 'number'), + clearable: true, + placeholder: '请选择短信类型', + }, + }, + { + fieldName: 'status', + label: '开启状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + clearable: true, + placeholder: '请选择开启状态', + }, + }, + { + fieldName: 'code', + label: '模板编码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板编码', + }, + }, + { + fieldName: 'name', + label: '模板名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入模板名称', + }, + }, + { + fieldName: 'channelId', + label: '短信渠道', + component: 'ApiSelect', + componentProps: { + api: getSimpleSmsChannelList, + labelField: 'signature', + valueField: 'id', + clearable: true, + placeholder: '请选择短信渠道', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 发送短信表单 */ +export function useSendSmsFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'content', + label: '模板内容', + component: 'Textarea', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + }, + rules: 'required', + }, + { + fieldName: 'templateParams', + label: '模板参数', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'type', + title: '短信类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_TEMPLATE_TYPE }, + }, + }, + { + field: 'name', + title: '模板名称', + minWidth: 120, + }, + { + field: 'code', + title: '模板编码', + minWidth: 120, + }, + { + field: 'content', + title: '模板内容', + minWidth: 200, + }, + { + field: 'status', + title: '开启状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'apiTemplateId', + title: '短信 API 的模板编号', + minWidth: 180, + }, + { + field: 'channelCode', + title: '短信渠道', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SMS_CHANNEL_CODE }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'remark', + title: '备注', + minWidth: 120, + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/sms/template/index.vue b/apps/web-ele/src/views/system/sms/template/index.vue new file mode 100644 index 000000000..0d340c240 --- /dev/null +++ b/apps/web-ele/src/views/system/sms/template/index.vue @@ -0,0 +1,207 @@ + + + diff --git a/apps/web-ele/src/views/system/sms/template/modules/send-form.vue b/apps/web-ele/src/views/system/sms/template/modules/send-form.vue new file mode 100644 index 000000000..a02b8e0c6 --- /dev/null +++ b/apps/web-ele/src/views/system/sms/template/modules/send-form.vue @@ -0,0 +1,109 @@ + + + diff --git a/apps/web-ele/src/views/system/social/client/data.ts b/apps/web-ele/src/views/system/social/client/data.ts new file mode 100644 index 000000000..f7f15d6b3 --- /dev/null +++ b/apps/web-ele/src/views/system/social/client/data.ts @@ -0,0 +1,208 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { + CommonStatusEnum, + DICT_TYPE, + SystemUserSocialTypeEnum, +} from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + }, + rules: 'required', + }, + { + fieldName: 'socialType', + label: '社交平台', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SOCIAL_TYPE, 'number'), + placeholder: '请选择社交平台', + }, + rules: 'required', + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + }, + rules: 'required', + }, + { + fieldName: 'clientId', + label: '客户端编号', + component: 'Input', + componentProps: { + placeholder: '请输入客户端编号,对应各平台的 appKey', + }, + rules: 'required', + }, + { + fieldName: 'clientSecret', + label: '客户端密钥', + component: 'Input', + componentProps: { + placeholder: '请输入客户端密钥,对应各平台的 appSecret', + }, + rules: 'required', + }, + { + fieldName: 'agentId', + label: 'agentId', + component: 'Input', + componentProps: { + placeholder: '授权方的网页应用 ID,有则填', + }, + dependencies: { + triggerFields: ['socialType'], + show: (values) => + values.socialType === SystemUserSocialTypeEnum.WECHAT_ENTERPRISE.type, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '应用名', + component: 'Input', + componentProps: { + placeholder: '请输入应用名', + clearable: true, + }, + }, + { + fieldName: 'socialType', + label: '社交平台', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SOCIAL_TYPE, 'number'), + placeholder: '请选择社交平台', + clearable: true, + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + placeholder: '请选择用户类型', + clearable: true, + }, + }, + { + fieldName: 'clientId', + label: '客户端编号', + component: 'Input', + componentProps: { + placeholder: '请输入客户端编号', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + placeholder: '请选择状态', + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 100, + }, + { + field: 'name', + title: '应用名', + minWidth: 120, + }, + { + field: 'socialType', + title: '社交平台', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SOCIAL_TYPE }, + }, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'clientId', + title: '客户端编号', + minWidth: 180, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/social/client/index.vue b/apps/web-ele/src/views/system/social/client/index.vue new file mode 100644 index 000000000..9c7b733b5 --- /dev/null +++ b/apps/web-ele/src/views/system/social/client/index.vue @@ -0,0 +1,173 @@ + + + diff --git a/apps/web-ele/src/views/system/social/client/modules/form.vue b/apps/web-ele/src/views/system/social/client/modules/form.vue new file mode 100644 index 000000000..47bd5fdb3 --- /dev/null +++ b/apps/web-ele/src/views/system/social/client/modules/form.vue @@ -0,0 +1,89 @@ + + + diff --git a/apps/web-ele/src/views/system/social/user/data.ts b/apps/web-ele/src/views/system/social/user/data.ts new file mode 100644 index 000000000..77847d0a0 --- /dev/null +++ b/apps/web-ele/src/views/system/social/user/data.ts @@ -0,0 +1,162 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { ElImage } from 'element-plus'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'type', + label: '社交平台', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_SOCIAL_TYPE, 'number'), + placeholder: '请选择社交平台', + clearable: true, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + placeholder: '请输入用户昵称', + clearable: true, + }, + }, + { + fieldName: 'openid', + label: '社交 openid', + component: 'Input', + componentProps: { + placeholder: '请输入社交 openid', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'type', + title: '社交平台', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_SOCIAL_TYPE }, + }, + }, + { + field: 'openid', + title: '社交 openid', + minWidth: 180, + }, + { + field: 'nickname', + title: '用户昵称', + minWidth: 120, + }, + { + field: 'avatar', + title: '用户头像', + minWidth: 100, + cellRender: { + name: 'CellImage', + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 120, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'type', + label: '社交平台', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.SYSTEM_SOCIAL_TYPE, + value: val, + }); + }, + }, + { + field: 'nickname', + label: '用户昵称', + }, + { + field: 'avatar', + label: '用户头像', + render: (val) => { + if (val) { + return h(ElImage, { + src: val, + previewSrcList: [val], + class: 'w-10 h-10 cursor-pointer', + previewTeleported: true, + }); + } + return '无'; + }, + }, + { + field: 'token', + label: '社交 token', + }, + { + field: 'rawTokenInfo', + label: '原始 Token 数据', + }, + { + field: 'rawUserInfo', + label: '原始 User 数据', + }, + { + field: 'code', + label: '最后一次的认证 code', + }, + { + field: 'state', + label: '最后一次的认证 state', + }, + ]; +} diff --git a/apps/web-ele/src/views/system/social/user/index.vue b/apps/web-ele/src/views/system/social/user/index.vue new file mode 100644 index 000000000..11dc331ed --- /dev/null +++ b/apps/web-ele/src/views/system/social/user/index.vue @@ -0,0 +1,79 @@ + + + diff --git a/apps/web-ele/src/views/system/social/user/modules/detail.vue b/apps/web-ele/src/views/system/social/user/modules/detail.vue new file mode 100644 index 000000000..a4edf0018 --- /dev/null +++ b/apps/web-ele/src/views/system/social/user/modules/detail.vue @@ -0,0 +1,53 @@ + + + diff --git a/apps/web-ele/src/views/system/tenant/data.ts b/apps/web-ele/src/views/system/tenant/data.ts new file mode 100644 index 000000000..54ac87792 --- /dev/null +++ b/apps/web-ele/src/views/system/tenant/data.ts @@ -0,0 +1,259 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemTenantPackageApi } from '#/api/system/tenant-package'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getTenantPackageList } from '#/api/system/tenant-package'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 关联数据 */ +let tenantPackageList: SystemTenantPackageApi.TenantPackage[] = []; +getTenantPackageList().then((data) => (tenantPackageList = data)); + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '租户名称', + component: 'Input', + componentProps: { + placeholder: '请输入租户名称', + }, + rules: 'required', + }, + { + fieldName: 'packageId', + label: '租户套餐', + component: 'ApiSelect', + componentProps: { + api: getTenantPackageList, + labelField: 'name', + valueField: 'id', + placeholder: '请选择租户套餐', + }, + rules: 'required', + }, + { + fieldName: 'contactName', + label: '联系人', + component: 'Input', + componentProps: { + placeholder: '请输入联系人', + }, + rules: 'required', + }, + { + fieldName: 'contactMobile', + label: '联系手机', + component: 'Input', + componentProps: { + placeholder: '请输入联系手机', + }, + rules: 'mobile', + }, + { + label: '用户名称', + fieldName: 'username', + component: 'Input', + componentProps: { + placeholder: '请输入用户名称', + }, + rules: 'required', + dependencies: { + triggerFields: ['id'], + show: (values) => !values.id, + }, + }, + { + label: '用户密码', + fieldName: 'password', + component: 'Input', + componentProps: { + showPassword: true, + }, + rules: 'required', + dependencies: { + triggerFields: ['id'], + show: (values) => !values.id, + }, + }, + { + label: '账号额度', + fieldName: 'accountCount', + component: 'InputNumber', + componentProps: { + placeholder: '请输入账号额度', + controlsPosition: 'right', + class: '!w-full', + }, + rules: 'required', + }, + { + label: '过期时间', + fieldName: 'expireTime', + component: 'DatePicker', + componentProps: { + format: 'YYYY-MM-DD', + valueFormat: 'x', + placeholder: '请选择过期时间', + class: '!w-full', + }, + rules: 'required', + }, + { + label: '绑定域名', + fieldName: 'websites', + component: 'InputTag', + componentProps: { + placeholder: '请输入绑定域名', + }, + }, + { + fieldName: 'status', + label: '租户状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '租户名', + component: 'Input', + componentProps: { + placeholder: '请输入租户名', + clearable: true, + }, + }, + { + fieldName: 'contactName', + label: '联系人', + component: 'Input', + componentProps: { + placeholder: '请输入联系人', + clearable: true, + }, + }, + { + fieldName: 'contactMobile', + label: '联系手机', + component: 'Input', + componentProps: { + placeholder: '请输入联系手机', + clearable: true, + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + placeholder: '请选择状态', + clearable: true, + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '租户编号', + minWidth: 100, + }, + { + field: 'name', + title: '租户名', + minWidth: 180, + }, + { + field: 'packageId', + title: '租户套餐', + minWidth: 180, + formatter: ({ cellValue }) => { + return cellValue === 0 + ? '系统租户' + : tenantPackageList.find((pkg) => pkg.id === cellValue)?.name || '-'; + }, + }, + { + field: 'contactName', + title: '联系人', + minWidth: 100, + }, + { + field: 'contactMobile', + title: '联系手机', + minWidth: 180, + }, + { + field: 'accountCount', + title: '账号额度', + minWidth: 100, + }, + { + field: 'expireTime', + title: '过期时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'websites', + title: '绑定域名', + minWidth: 180, + }, + { + field: 'status', + title: '租户状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 130, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/tenant/index.vue b/apps/web-ele/src/views/system/tenant/index.vue new file mode 100644 index 000000000..25a1be37c --- /dev/null +++ b/apps/web-ele/src/views/system/tenant/index.vue @@ -0,0 +1,186 @@ + + diff --git a/apps/web-ele/src/views/system/tenant/modules/form.vue b/apps/web-ele/src/views/system/tenant/modules/form.vue new file mode 100644 index 000000000..ccdd99ff2 --- /dev/null +++ b/apps/web-ele/src/views/system/tenant/modules/form.vue @@ -0,0 +1,80 @@ + + diff --git a/apps/web-ele/src/views/system/tenantPackage/data.ts b/apps/web-ele/src/views/system/tenantPackage/data.ts new file mode 100644 index 000000000..01f165e52 --- /dev/null +++ b/apps/web-ele/src/views/system/tenantPackage/data.ts @@ -0,0 +1,131 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { z } from '#/adapter/form'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'Input', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '套餐名称', + component: 'Input', + componentProps: { + placeholder: '请输入套餐名称', + }, + rules: 'required', + }, + { + fieldName: 'menuIds', + label: '菜单权限', + component: 'Input', + formItemClass: 'items-start', + }, + { + fieldName: 'status', + label: '状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '套餐名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入套餐名称', + }, + }, + { + fieldName: 'status', + label: '状态', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + clearable: true, + placeholder: '请选择状态', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '套餐编号', + minWidth: 100, + }, + { + field: 'name', + title: '套餐名称', + minWidth: 180, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.COMMON_STATUS }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/tenantPackage/index.vue b/apps/web-ele/src/views/system/tenantPackage/index.vue new file mode 100644 index 000000000..4a76c12d4 --- /dev/null +++ b/apps/web-ele/src/views/system/tenantPackage/index.vue @@ -0,0 +1,173 @@ + + + diff --git a/apps/web-ele/src/views/system/tenantPackage/modules/form.vue b/apps/web-ele/src/views/system/tenantPackage/modules/form.vue new file mode 100644 index 000000000..c789d6417 --- /dev/null +++ b/apps/web-ele/src/views/system/tenantPackage/modules/form.vue @@ -0,0 +1,161 @@ + + + diff --git a/apps/web-ele/src/views/system/user/components/index.ts b/apps/web-ele/src/views/system/user/components/index.ts new file mode 100644 index 000000000..9cb1c93fd --- /dev/null +++ b/apps/web-ele/src/views/system/user/components/index.ts @@ -0,0 +1 @@ +export { default as UserSelectModal } from './user-select-modal.vue'; diff --git a/apps/web-ele/src/views/system/user/components/user-select-modal.vue b/apps/web-ele/src/views/system/user/components/user-select-modal.vue new file mode 100644 index 000000000..a9c2b1b16 --- /dev/null +++ b/apps/web-ele/src/views/system/user/components/user-select-modal.vue @@ -0,0 +1,518 @@ + + + + + diff --git a/apps/web-ele/src/views/system/user/data.ts b/apps/web-ele/src/views/system/user/data.ts new file mode 100644 index 000000000..98cf580dc --- /dev/null +++ b/apps/web-ele/src/views/system/user/data.ts @@ -0,0 +1,352 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { SystemUserApi } from '#/api/system/user'; + +import { CommonStatusEnum, DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { $t } from '@vben/locales'; +import { handleTree } from '@vben/utils'; + +import { z } from '#/adapter/form'; +import { getDeptList } from '#/api/system/dept'; +import { getSimplePostList } from '#/api/system/post'; +import { getSimpleRoleList } from '#/api/system/role'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'username', + label: '用户名称', + component: 'Input', + componentProps: { + placeholder: '请输入用户名称', + }, + rules: 'required', + }, + { + label: '用户密码', + fieldName: 'password', + component: 'Input', + componentProps: { + showPassword: true, + }, + rules: 'required', + dependencies: { + triggerFields: ['id'], + show: (values) => !values.id, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + placeholder: '请输入用户昵称', + }, + rules: 'required', + }, + { + fieldName: 'deptId', + label: '归属部门', + component: 'ApiTreeSelect', + componentProps: { + api: async () => { + const data = await getDeptList(); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择归属部门', + treeDefaultExpandAll: true, + }, + }, + { + fieldName: 'postIds', + label: '岗位', + component: 'ApiSelect', + componentProps: { + api: getSimplePostList, + labelField: 'name', + valueField: 'id', + multiple: true, + placeholder: '请选择岗位', + }, + }, + { + fieldName: 'email', + label: '邮箱', + component: 'Input', + rules: z.string().email('邮箱格式不正确').or(z.literal('')).optional(), + componentProps: { + placeholder: '请输入邮箱', + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + }, + }, + { + fieldName: 'sex', + label: '用户性别', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + rules: z.number().default(1), + }, + { + fieldName: 'status', + label: '用户状态', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.COMMON_STATUS, 'number'), + }, + rules: z.number().default(CommonStatusEnum.ENABLE), + }, + { + fieldName: 'remark', + label: '备注', + component: 'Textarea', + componentProps: { + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 重置密码的表单 */ +export function useResetPasswordFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + component: 'VbenInputPassword', + componentProps: { + passwordStrength: true, + placeholder: '请输入新密码', + }, + dependencies: { + rules(values) { + return z + .string({ message: '请输入新密码' }) + .min(5, '密码长度不能少于 5 个字符') + .max(20, '密码长度不能超过 20 个字符') + .refine( + (value) => value !== values.oldPassword, + '新旧密码不能相同', + ); + }, + triggerFields: ['newPassword', 'oldPassword'], + }, + fieldName: 'newPassword', + label: '新密码', + rules: 'required', + }, + { + component: 'VbenInputPassword', + componentProps: { + passwordStrength: true, + placeholder: $t('authentication.confirmPassword'), + }, + dependencies: { + rules(values) { + return z + .string({ message: '请输入确认密码' }) + .min(5, '密码长度不能少于 5 个字符') + .max(20, '密码长度不能超过 20 个字符') + .refine( + (value) => value === values.newPassword, + '新密码和确认密码不一致', + ); + }, + triggerFields: ['newPassword', 'confirmPassword'], + }, + fieldName: 'confirmPassword', + label: '确认密码', + rules: 'required', + }, + ]; +} + +/** 分配角色的表单 */ +export function useAssignRoleFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'username', + label: '用户名称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'nickname', + label: '用户昵称', + component: 'Input', + componentProps: { + disabled: true, + }, + }, + { + fieldName: 'roleIds', + label: '角色', + component: 'ApiSelect', + componentProps: { + api: getSimpleRoleList, + labelField: 'name', + valueField: 'id', + multiple: true, + placeholder: '请选择角色', + }, + }, + ]; +} + +/** 用户导入的表单 */ +export function useImportFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'file', + label: '用户数据', + component: 'Upload', + rules: 'required', + help: '仅允许导入 xls、xlsx 格式文件', + }, + { + fieldName: 'updateSupport', + label: '是否覆盖', + component: 'Switch', + componentProps: { + checkedChildren: '是', + unCheckedChildren: '否', + }, + rules: z.boolean().default(false), + help: '是否更新已经存在的用户数据', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'username', + label: '用户名称', + component: 'Input', + componentProps: { + placeholder: '请输入用户名称', + clearable: true, + }, + }, + { + fieldName: 'mobile', + label: '手机号码', + component: 'Input', + componentProps: { + placeholder: '请输入手机号码', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'RangePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + onStatusChange?: ( + newStatus: number, + row: SystemUserApi.User, + ) => PromiseLike, +): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '用户编号', + minWidth: 100, + }, + { + field: 'username', + title: '用户名称', + minWidth: 120, + }, + { + field: 'nickname', + title: '用户昵称', + minWidth: 120, + }, + { + field: 'deptName', + title: '部门', + minWidth: 120, + }, + { + field: 'mobile', + title: '手机号码', + minWidth: 120, + }, + { + field: 'status', + title: '状态', + minWidth: 100, + align: 'center', + cellRender: { + attrs: { beforeChange: onStatusChange }, + name: 'CellSwitch', + props: { + activeValue: CommonStatusEnum.ENABLE, + inactiveValue: CommonStatusEnum.DISABLE, + }, + }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 180, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/system/user/index.vue b/apps/web-ele/src/views/system/user/index.vue new file mode 100644 index 000000000..e56a89878 --- /dev/null +++ b/apps/web-ele/src/views/system/user/index.vue @@ -0,0 +1,296 @@ + + + diff --git a/apps/web-ele/src/views/system/user/modules/assign-role-form.vue b/apps/web-ele/src/views/system/user/modules/assign-role-form.vue new file mode 100644 index 000000000..9d3e0d868 --- /dev/null +++ b/apps/web-ele/src/views/system/user/modules/assign-role-form.vue @@ -0,0 +1,78 @@ + + + diff --git a/apps/web-ele/src/views/system/user/modules/dept-tree.vue b/apps/web-ele/src/views/system/user/modules/dept-tree.vue new file mode 100644 index 000000000..9c4d5a8bf --- /dev/null +++ b/apps/web-ele/src/views/system/user/modules/dept-tree.vue @@ -0,0 +1,81 @@ + + + diff --git a/apps/web-ele/src/views/system/user/modules/form.vue b/apps/web-ele/src/views/system/user/modules/form.vue new file mode 100644 index 000000000..e20eb82b8 --- /dev/null +++ b/apps/web-ele/src/views/system/user/modules/form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-ele/src/views/system/user/modules/import-form.vue b/apps/web-ele/src/views/system/user/modules/import-form.vue new file mode 100644 index 000000000..6222bf437 --- /dev/null +++ b/apps/web-ele/src/views/system/user/modules/import-form.vue @@ -0,0 +1,82 @@ + + + diff --git a/apps/web-naive/.env b/apps/web-naive/.env new file mode 100644 index 000000000..063ed61c1 --- /dev/null +++ b/apps/web-naive/.env @@ -0,0 +1,35 @@ +# 应用标题 +VITE_APP_TITLE=芋道管理系统 + +# 应用命名空间,用于缓存、store等功能的前缀,确保隔离 +VITE_APP_NAMESPACE=yudao-vben-naive + +# 对store进行加密的密钥,在将store持久化到localStorage时会使用该密钥进行加密 +VITE_APP_STORE_SECURE_KEY=please-replace-me-with-your-own-key + +# 是否开启模拟数据 +VITE_NITRO_MOCK=false + +# 租户开关 +VITE_APP_TENANT_ENABLE=true + +# 验证码的开关 +VITE_APP_CAPTCHA_ENABLE=false + +# 文档地址的开关 +VITE_APP_DOCALERT_ENABLE=true + +# 百度统计 +VITE_APP_BAIDU_CODE = e98f2eab6ceb8688bc6d8fc5332ff093 + +# GoView域名 +VITE_GOVIEW_URL='http://127.0.0.1:3000' + +# API 加解密 +VITE_APP_API_ENCRYPT_ENABLE = true +VITE_APP_API_ENCRYPT_HEADER = X-Api-Encrypt +VITE_APP_API_ENCRYPT_ALGORITHM = AES +VITE_APP_API_ENCRYPT_REQUEST_KEY = 52549111389893486934626385991395 +VITE_APP_API_ENCRYPT_RESPONSE_KEY = 96103715984234343991809655248883 +# VITE_APP_API_ENCRYPT_REQUEST_KEY = MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCls2rIpnGdYnLFgz1XU13GbNQ5DloyPpvW00FPGjqn5Z6JpK+kDtVlnkhwR87iRrE5Vf2WNqRX6vzbLSgveIQY8e8oqGCb829myjf1MuI+ZzN4ghf/7tEYhZJGPI9AbfxFqBUzm+kR3/HByAI22GLT96WM26QiMK8n3tIP/yiLswIDAQAB +# VITE_APP_API_ENCRYPT_RESPONSE_KEY = MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOH8IfIFxL/MR9XIg1UDv5z1fGXQI93E8wrU4iPFovL/sEt9uSgSkjyidC2O7N+m7EKtoN6b1u7cEwXSkwf3kfK0jdWLSQaNpX5YshqXCBzbDfugDaxuyYrNA4/tIMs7mzZAk0APuRXB35Dmupou7Yw7TFW/7QhQmGfzeEKULQvnAgMBAAECgYAw8LqlQGyQoPv5p3gRxEMOCfgL0JzD3XBJKztiRd35RDh40Nx1ejgjW4dPioFwGiVWd2W8cAGHLzALdcQT2KDJh+T/tsd4SPmI6uSBBK6Ff2DkO+kFFcuYvfclQQKqxma5CaZOSqhgenacmgTMFeg2eKlY3symV6JlFNu/IKU42QJBAOhxAK/Eq3e61aYQV2JSguhMR3b8NXJJRroRs/QHEanksJtl+M+2qhkC9nQVXBmBkndnkU/l2tYcHfSBlAyFySMCQQD445tgm/J2b6qMQmuUGQAYDN8FIkHjeKmha+l/fv0igWm8NDlBAem91lNDIPBUzHL1X1+pcts5bjmq99YdOnhtAkAg2J8dN3B3idpZDiQbC8fd5bGPmdI/pSUudAP27uzLEjr2qrE/QPPGdwm2m7IZFJtK7kK1hKio6u48t/bg0iL7AkEAuUUs94h+v702Fnym+jJ2CHEkXvz2US8UDs52nWrZYiM1o1y4tfSHm8H8bv8JCAa9GHyriEawfBraILOmllFdLQJAQSRZy4wmlaG48MhVXodB85X+VZ9krGXZ2TLhz7kz9iuToy53l9jTkESt6L5BfBDCVdIwcXLYgK+8KFdHN5W7HQ== diff --git a/apps/web-naive/package.json b/apps/web-naive/package.json new file mode 100644 index 000000000..c167effda --- /dev/null +++ b/apps/web-naive/package.json @@ -0,0 +1,50 @@ +{ + "name": "@vben/web-naive", + "version": "5.5.9", + "homepage": "https://vben.pro", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "apps/web-naive" + }, + "license": "MIT", + "author": { + "name": "vben", + "email": "ann.vben@gmail.com", + "url": "https://github.com/anncwb" + }, + "type": "module", + "scripts": { + "build": "pnpm vite build --mode production", + "build:analyze": "pnpm vite build --mode analyze", + "dev": "pnpm vite --mode development", + "preview": "vite preview", + "typecheck": "vue-tsc --noEmit --skipLibCheck" + }, + "imports": { + "#/*": "./src/*" + }, + "dependencies": { + "@vben/access": "workspace:*", + "@vben/common-ui": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/layouts": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/plugins": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/request": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/styles": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "dayjs": "catalog:", + "naive-ui": "catalog:", + "pinia": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/apps/web-naive/public/wx-xingyu.png b/apps/web-naive/public/wx-xingyu.png new file mode 100644 index 000000000..da2e45a34 Binary files /dev/null and b/apps/web-naive/public/wx-xingyu.png differ diff --git a/apps/web-naive/src/adapter/component/index.ts b/apps/web-naive/src/adapter/component/index.ts new file mode 100644 index 000000000..6d32364f1 --- /dev/null +++ b/apps/web-naive/src/adapter/component/index.ts @@ -0,0 +1,236 @@ +/** + * 通用组件共同的使用的基础组件,原先放在 adapter/form 内部,限制了使用范围,这里提取出来,方便其他地方使用 + * 可用于 vben-form、vben-modal、vben-drawer 等组件使用, + */ + +import type { Component } from 'vue'; + +import type { BaseFormComponentType } from '@vben/common-ui'; +import type { Recordable } from '@vben/types'; + +import { defineAsyncComponent, defineComponent, h, ref } from 'vue'; + +import { ApiComponent, globalShareState, IconPicker } from '@vben/common-ui'; +import { $t } from '@vben/locales'; + +import { message } from '#/adapter/naive'; +import { FileUpload, ImageUpload } from '#/components/upload'; + +const NButton = defineAsyncComponent(() => + import('naive-ui/es/button').then((res) => res.NButton), +); +const NCheckbox = defineAsyncComponent(() => + import('naive-ui/es/checkbox').then((res) => res.NCheckbox), +); +const NCheckboxGroup = defineAsyncComponent(() => + import('naive-ui/es/checkbox').then((res) => res.NCheckboxGroup), +); +const NDatePicker = defineAsyncComponent(() => + import('naive-ui/es/date-picker').then((res) => res.NDatePicker), +); +const NDivider = defineAsyncComponent(() => + import('naive-ui/es/divider').then((res) => res.NDivider), +); +const NInput = defineAsyncComponent(() => + import('naive-ui/es/input').then((res) => res.NInput), +); +const NInputNumber = defineAsyncComponent(() => + import('naive-ui/es/input-number').then((res) => res.NInputNumber), +); +const NRadio = defineAsyncComponent(() => + import('naive-ui/es/radio').then((res) => res.NRadio), +); +const NRadioButton = defineAsyncComponent(() => + import('naive-ui/es/radio').then((res) => res.NRadioButton), +); +const NRadioGroup = defineAsyncComponent(() => + import('naive-ui/es/radio').then((res) => res.NRadioGroup), +); +const NSelect = defineAsyncComponent(() => + import('naive-ui/es/select').then((res) => res.NSelect), +); +const NSpace = defineAsyncComponent(() => + import('naive-ui/es/space').then((res) => res.NSpace), +); +const NSwitch = defineAsyncComponent(() => + import('naive-ui/es/switch').then((res) => res.NSwitch), +); +const NTimePicker = defineAsyncComponent(() => + import('naive-ui/es/time-picker').then((res) => res.NTimePicker), +); +const NTreeSelect = defineAsyncComponent(() => + import('naive-ui/es/tree-select').then((res) => res.NTreeSelect), +); +const NUpload = defineAsyncComponent(() => + import('naive-ui/es/upload').then((res) => res.NUpload), +); + +const withDefaultPlaceholder = ( + component: T, + type: 'input' | 'select', + componentProps: Recordable = {}, +) => { + return defineComponent({ + name: component.name, + inheritAttrs: false, + setup: (props: any, { attrs, expose, slots }) => { + const placeholder = + props?.placeholder || + attrs?.placeholder || + $t(`ui.placeholder.${type}`); + // 透传组件暴露的方法 + const innerRef = ref(); + expose( + new Proxy( + {}, + { + get: (_target, key) => innerRef.value?.[key], + has: (_target, key) => key in (innerRef.value || {}), + }, + ), + ); + return () => + h( + component, + { ...componentProps, placeholder, ...props, ...attrs, ref: innerRef }, + slots, + ); + }, + }); +}; + +// 这里需要自行根据业务组件库进行适配,需要用到的组件都需要在这里类型说明 +export type ComponentType = + | 'ApiSelect' + | 'ApiTreeSelect' + | 'Checkbox' + | 'CheckboxGroup' + | 'DatePicker' + | 'Divider' + | 'FileUpload' + | 'IconPicker' + | 'ImageUpload' + | 'Input' + | 'InputNumber' + | 'RadioGroup' + | 'Select' + | 'Space' + | 'Switch' + | 'TimePicker' + | 'TreeSelect' + | 'Upload' + | BaseFormComponentType; + +async function initComponentAdapter() { + const components: Partial> = { + // 如果你的组件体积比较大,可以使用异步加载 + // Button: () => + // import('xxx').then((res) => res.Button), + + ApiSelect: withDefaultPlaceholder( + { + ...ApiComponent, + name: 'ApiSelect', + }, + 'select', + { + component: NSelect, + modelPropName: 'value', + }, + ), + ApiTreeSelect: withDefaultPlaceholder( + { + ...ApiComponent, + name: 'ApiTreeSelect', + }, + 'select', + { + component: NTreeSelect, + nodeKey: 'value', + loadingSlot: 'arrow', + keyField: 'value', + modelPropName: 'value', + optionsPropName: 'options', + visibleEvent: 'onVisibleChange', + }, + ), + Checkbox: NCheckbox, + CheckboxGroup: (props, { attrs, slots }) => { + let defaultSlot; + if (Reflect.has(slots, 'default')) { + defaultSlot = slots.default; + } else { + const { options } = attrs; + if (Array.isArray(options)) { + defaultSlot = () => options.map((option) => h(NCheckbox, option)); + } + } + return h( + NCheckboxGroup, + { ...props, ...attrs }, + { default: defaultSlot }, + ); + }, + DatePicker: NDatePicker, + // 自定义默认按钮 + DefaultButton: (props, { attrs, slots }) => { + return h(NButton, { ...props, attrs, type: 'default' }, slots); + }, + // 自定义主要按钮 + PrimaryButton: (props, { attrs, slots }) => { + return h(NButton, { ...props, attrs, type: 'primary' }, slots); + }, + Divider: NDivider, + IconPicker: withDefaultPlaceholder(IconPicker, 'select', { + iconSlot: 'suffix', + inputComponent: NInput, + }), + Input: withDefaultPlaceholder(NInput, 'input'), + InputNumber: withDefaultPlaceholder(NInputNumber, 'input'), + RadioGroup: (props, { attrs, slots }) => { + let defaultSlot; + if (Reflect.has(slots, 'default')) { + defaultSlot = slots.default; + } else { + const { options } = attrs; + if (Array.isArray(options)) { + defaultSlot = () => + options.map((option) => + h(attrs.isButton ? NRadioButton : NRadio, option), + ); + } + } + const groupRender = h( + NRadioGroup, + { ...props, ...attrs }, + { default: defaultSlot }, + ); + return attrs.isButton + ? h(NSpace, { vertical: true }, () => groupRender) + : groupRender; + }, + Select: withDefaultPlaceholder(NSelect, 'select'), + Space: NSpace, + Switch: NSwitch, + TimePicker: NTimePicker, + TreeSelect: withDefaultPlaceholder(NTreeSelect, 'select'), + Upload: NUpload, + FileUpload, + ImageUpload, + }; + + // 将组件注册到全局共享状态中 + globalShareState.setComponents(components); + + // 定义全局共享状态中的消息提示 + globalShareState.defineMessage({ + // 复制成功消息提示 + copyPreferencesSuccess: (title, content) => { + message.success(content || title, { + duration: 0, + }); + }, + }); +} + +export { initComponentAdapter }; diff --git a/apps/web-naive/src/adapter/form.ts b/apps/web-naive/src/adapter/form.ts new file mode 100644 index 000000000..6d6ea0070 --- /dev/null +++ b/apps/web-naive/src/adapter/form.ts @@ -0,0 +1,66 @@ +import type { + VbenFormSchema as FormSchema, + VbenFormProps, +} from '@vben/common-ui'; + +import type { ComponentType } from './component'; + +import { setupVbenForm, useVbenForm as useForm, z } from '@vben/common-ui'; +import { $t } from '@vben/locales'; +import { isMobile } from '@vben/utils'; + +async function initSetupVbenForm() { + setupVbenForm({ + config: { + // naive-ui组件的空值为null,不能是undefined,否则重置表单时不生效 + emptyStateValue: null, + baseModelPropName: 'value', + modelPropNameMap: { + Checkbox: 'checked', + Radio: 'checked', + Upload: 'fileList', + DatePicker: 'formatted-value', + }, + }, + defineRules: { + required: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.required', [ctx.label]); + } + return true; + }, + selectRequired: (value, _params, ctx) => { + if (value === undefined || value === null) { + return $t('ui.formRules.selectRequired', [ctx.label]); + } + return true; + }, + // 手机号非必填 + mobile: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return true; + } else if (!isMobile(value)) { + return $t('ui.formRules.mobile', [ctx.label]); + } + return true; + }, + // 手机号必填 + mobileRequired: (value, _params, ctx) => { + if (value === undefined || value === null || value.length === 0) { + return $t('ui.formRules.required', [ctx.label]); + } + if (!isMobile(value)) { + return $t('ui.formRules.mobile', [ctx.label]); + } + return true; + }, + }, + }); +} + +const useVbenForm = useForm; + +export { initSetupVbenForm, useVbenForm, z }; + +export type VbenFormSchema = FormSchema; +export type { VbenFormProps }; diff --git a/apps/web-naive/src/adapter/vxe-table.ts b/apps/web-naive/src/adapter/vxe-table.ts new file mode 100644 index 000000000..db032db3b --- /dev/null +++ b/apps/web-naive/src/adapter/vxe-table.ts @@ -0,0 +1,219 @@ +import type { VxeTableGridOptions } from '@vben/plugins/vxe-table'; + +import { h } from 'vue'; + +import { setupVbenVxeTable, useVbenVxeGrid } from '@vben/plugins/vxe-table'; +import { + erpCountInputFormatter, + erpNumberFormatter, + fenToYuan, + formatFileSize, + formatPast2, +} from '@vben/utils'; + +import { NButton, NImage, NImageGroup, NSwitch, NTag } from 'naive-ui'; + +import { $t } from '#/locales'; + +import DictTag from '../components/dict-tag/dict-tag.vue'; +import { useVbenForm } from './form'; + +setupVbenVxeTable({ + configVxeTable: (vxeUI) => { + vxeUI.setConfig({ + grid: { + align: 'center', + border: false, + columnConfig: { + resizable: true, + }, + minHeight: 180, + formConfig: { + // 全局禁用vxe-table的表单配置,使用formOptions + enabled: false, + }, + toolbarConfig: { + import: false, // 是否导入 + export: false, // 是否导出 + refresh: true, // 是否刷新 + print: false, // 是否打印 + zoom: true, // 是否缩放 + custom: true, // 是否自定义配置 + }, + customConfig: { + mode: 'modal', + }, + proxyConfig: { + autoLoad: true, + response: { + result: 'list', + total: 'total', + }, + showActiveMsg: true, + showResponseMsg: false, + }, + pagerConfig: { + enabled: true, + }, + sortConfig: { + multiple: true, + }, + round: true, + showOverflow: true, + size: 'small', + } as VxeTableGridOptions, + }); + + // 表格配置项可以用 cellRender: { name: 'CellImage' }, + vxeUI.renderer.add('CellImage', { + renderTableDefault(_renderOpts, params) { + const { column, row } = params; + return h(NImage, { src: row[column.field] }); + }, + }); + + vxeUI.renderer.add('CellImages', { + renderTableDefault(_renderOpts, params) { + const { column, row } = params; + if (column && column.field && row[column.field]) { + return h(NImageGroup, { srcList: row[column.field] }); + } + return ''; + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellLink' }, + vxeUI.renderer.add('CellLink', { + renderTableDefault(renderOpts) { + const { props } = renderOpts; + return h( + NButton, + { size: 'small', type: 'primary', quaternary: true }, + { default: () => props?.text }, + ); + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellTag' }, + vxeUI.renderer.add('CellTag', { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; + const { column, row } = params; + return h(NTag, { color: props?.color }, () => row[column.field]); + }, + }); + + vxeUI.renderer.add('CellTags', { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; + const { column, row } = params; + if (!row[column.field] || !Array.isArray(row[column.field])) { + return ''; + } + return h( + 'div', + { class: 'flex items-center justify-center' }, + { + default: () => + row[column.field].map((item: any) => + h(NTag, { color: props?.color }, { default: () => item }), + ), + }, + ); + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellDict', props:{dictType: ''} }, + vxeUI.renderer.add('CellDict', { + renderTableDefault(renderOpts, params) { + const { props } = renderOpts; + const { column, row } = params; + if (!props) { + return ''; + } + // 使用 DictTag 组件替代原来的实现 + return h(DictTag, { + type: props.type, + value: row[column.field]?.toString(), + }); + }, + }); + + // 表格配置项可以用 cellRender: { name: 'CellSwitch', props: { beforeChange: () => {} } }, + // add by 芋艿:from https://github.com/vbenjs/vue-vben-admin/blob/main/playground/src/adapter/vxe-table.ts#L97-L123 + vxeUI.renderer.add('CellSwitch', { + renderTableDefault({ attrs, props }, { column, row }) { + const loadingKey = `__loading_${column.field}`; + const finallyProps = { + inlinePrompt: true, + checkedValue: 0, + uncheckedValue: 1, + ...props, + value: row[column.field], + loading: row[loadingKey] ?? false, + 'onUpdate:value': onChange, + }; + + async function onChange(newVal: any) { + row[loadingKey] = true; + try { + const result = await attrs?.beforeChange?.(newVal, row); + if (result !== false) { + row[column.field] = newVal; + } + } finally { + row[loadingKey] = false; + } + } + + return h(NSwitch, finallyProps, { + checked: () => h('p', props?.checkedChildren ?? $t('common.enabled')), + unchecked: () => + h('p', props?.uncheckedChildren ?? $t('common.disabled')), + }); + }, + }); + + // 这里可以自行扩展 vxe-table 的全局配置,比如自定义格式化 + // vxeUI.formats.add + vxeUI.formats.add('formatPast2', { + tableCellFormatMethod({ cellValue }) { + return formatPast2(cellValue); + }, + }); + + // add by 星语:数量格式化,保留 3 位 + vxeUI.formats.add('formatAmount3', { + tableCellFormatMethod({ cellValue }) { + if (cellValue === null || cellValue === undefined) { + return ''; + } + return erpCountInputFormatter(cellValue); + }, + }); + // add by 星语:数量格式化,保留 2 位 + vxeUI.formats.add('formatAmount2', { + tableCellFormatMethod({ cellValue }, digits = 2) { + return `${erpNumberFormatter(cellValue, digits)}`; + }, + }); + + vxeUI.formats.add('formatFenToYuanAmount', { + tableCellFormatMethod({ cellValue }, digits = 2) { + return `${erpNumberFormatter(fenToYuan(cellValue), digits)}`; + }, + }); + + vxeUI.formats.add('formatFileSize', { + tableCellFormatMethod({ cellValue }, digits = 2) { + return formatFileSize(cellValue, digits); + }, + }); + }, + useVbenForm, +}); + +export { useVbenVxeGrid }; + +export * from '#/components/table-action'; +export type * from '@vben/plugins/vxe-table'; diff --git a/apps/web-naive/src/api/core/auth.ts b/apps/web-naive/src/api/core/auth.ts new file mode 100644 index 000000000..604644a1f --- /dev/null +++ b/apps/web-naive/src/api/core/auth.ts @@ -0,0 +1,161 @@ +import type { AuthPermissionInfo } from '@vben/types'; + +import { baseRequestClient, requestClient } from '#/api/request'; + +export namespace AuthApi { + /** 登录接口参数 */ + export interface LoginParams { + password?: string; + username?: string; + captchaVerification?: string; + // 绑定社交登录时,需要传递如下参数 + socialType?: number; + socialCode?: string; + socialState?: string; + } + + /** 登录接口返回值 */ + export interface LoginResult { + accessToken: string; + refreshToken: string; + userId: number; + expiresTime: number; + } + + /** 租户信息返回值 */ + export interface TenantResult { + id: number; + name: string; + } + + /** 手机验证码获取接口参数 */ + export interface SmsCodeParams { + mobile: string; + scene: number; + } + + /** 手机验证码登录接口参数 */ + export interface SmsLoginParams { + mobile: string; + code: string; + } + + /** 注册接口参数 */ + export interface RegisterParams { + username: string; + password: string; + captchaVerification: string; + } + + /** 重置密码接口参数 */ + export interface ResetPasswordParams { + password: string; + mobile: string; + code: string; + } + + /** 社交快捷登录接口参数 */ + export interface SocialLoginParams { + type: number; + code: string; + state: string; + } +} + +/** 登录 */ +export async function loginApi(data: AuthApi.LoginParams) { + return requestClient.post('/system/auth/login', data, { + headers: { + isEncrypt: false, + }, + }); +} + +/** 刷新 accessToken */ +export async function refreshTokenApi(refreshToken: string) { + return baseRequestClient.post( + `/system/auth/refresh-token?refreshToken=${refreshToken}`, + ); +} + +/** 退出登录 */ +export async function logoutApi(accessToken: string) { + return baseRequestClient.post( + '/system/auth/logout', + {}, + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }, + ); +} + +/** 获取权限信息 */ +export async function getAuthPermissionInfoApi() { + return requestClient.get( + '/system/auth/get-permission-info', + ); +} + +/** 获取租户列表 */ +export async function getTenantSimpleList() { + return requestClient.get( + `/system/tenant/simple-list`, + ); +} + +/** 使用租户域名,获得租户信息 */ +export async function getTenantByWebsite(website: string) { + return requestClient.get( + `/system/tenant/get-by-website?website=${website}`, + ); +} + +/** 获取验证码 */ +export async function getCaptcha(data: any) { + return baseRequestClient.post('/system/captcha/get', data); +} + +/** 校验验证码 */ +export async function checkCaptcha(data: any) { + return baseRequestClient.post('/system/captcha/check', data); +} + +/** 获取登录验证码 */ +export async function sendSmsCode(data: AuthApi.SmsCodeParams) { + return requestClient.post('/system/auth/send-sms-code', data); +} + +/** 短信验证码登录 */ +export async function smsLogin(data: AuthApi.SmsLoginParams) { + return requestClient.post('/system/auth/sms-login', data); +} + +/** 注册 */ +export async function register(data: AuthApi.RegisterParams) { + return requestClient.post('/system/auth/register', data); +} + +/** 通过短信重置密码 */ +export async function smsResetPassword(data: AuthApi.ResetPasswordParams) { + return requestClient.post('/system/auth/reset-password', data); +} + +/** 社交授权的跳转 */ +export async function socialAuthRedirect(type: number, redirectUri: string) { + return requestClient.get('/system/auth/social-auth-redirect', { + params: { + type, + redirectUri, + }, + }); +} + +/** 社交快捷登录 */ +export async function socialLogin(data: AuthApi.SocialLoginParams) { + return requestClient.post( + '/system/auth/social-login', + data, + ); +} diff --git a/apps/web-naive/src/api/infra/codegen/index.ts b/apps/web-naive/src/api/infra/codegen/index.ts new file mode 100644 index 000000000..992d41d2d --- /dev/null +++ b/apps/web-naive/src/api/infra/codegen/index.ts @@ -0,0 +1,168 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace InfraCodegenApi { + /** 代码生成表定义 */ + export interface CodegenTable { + id: number; + tableId: number; + isParentMenuIdValid: boolean; + dataSourceConfigId: number; + scene: number; + tableName: string; + tableComment: string; + remark: string; + moduleName: string; + businessName: string; + className: string; + classComment: string; + author: string; + createTime: Date; + updateTime: Date; + templateType: number; + parentMenuId: number; + } + + /** 代码生成字段定义 */ + export interface CodegenColumn { + id: number; + tableId: number; + columnName: string; + dataType: string; + columnComment: string; + nullable: number; + primaryKey: number; + ordinalPosition: number; + javaType: string; + javaField: string; + dictType: string; + example: string; + createOperation: number; + updateOperation: number; + listOperation: number; + listOperationCondition: string; + listOperationResult: number; + htmlType: string; + } + + /** 数据库表定义 */ + export interface DatabaseTable { + name: string; + comment: string; + } + + /** 代码生成详情 */ + export interface CodegenDetail { + table: CodegenTable; + columns: CodegenColumn[]; + } + + /** 代码预览 */ + export interface CodegenPreview { + filePath: string; + code: string; + } + + /** 更新代码生成请求 */ + export interface CodegenUpdateReqVO { + table: any | CodegenTable; + columns: CodegenColumn[]; + } + + /** 创建代码生成请求 */ + export interface CodegenCreateListReqVO { + dataSourceConfigId?: number; + tableNames: string[]; + } +} + +/** 查询列表代码生成表定义 */ +export function getCodegenTableList(dataSourceConfigId: number) { + return requestClient.get( + '/infra/codegen/table/list?', + { + params: { dataSourceConfigId }, + }, + ); +} + +/** 查询列表代码生成表定义 */ +export function getCodegenTablePage(params: PageParam) { + return requestClient.get>( + '/infra/codegen/table/page', + { params }, + ); +} + +/** 查询详情代码生成表定义 */ +export function getCodegenTable(tableId: number) { + return requestClient.get( + '/infra/codegen/detail', + { + params: { tableId }, + }, + ); +} + +/** 修改代码生成表定义 */ +export function updateCodegenTable(data: InfraCodegenApi.CodegenUpdateReqVO) { + return requestClient.put('/infra/codegen/update', data); +} + +/** 基于数据库的表结构,同步数据库的表和字段定义 */ +export function syncCodegenFromDB(tableId: number) { + return requestClient.put( + '/infra/codegen/sync-from-db', + {}, + { + params: { tableId }, + }, + ); +} + +/** 预览生成代码 */ +export function previewCodegen(tableId: number) { + return requestClient.get( + '/infra/codegen/preview', + { + params: { tableId }, + }, + ); +} + +/** 下载生成代码 */ +export function downloadCodegen(tableId: number) { + return requestClient.download('/infra/codegen/download', { + params: { tableId }, + }); +} + +/** 获得表定义 */ +export function getSchemaTableList(params: any) { + return requestClient.get( + '/infra/codegen/db/table/list', + { params }, + ); +} + +/** 基于数据库的表结构,创建代码生成器的表定义 */ +export function createCodegenList( + data: InfraCodegenApi.CodegenCreateListReqVO, +) { + return requestClient.post('/infra/codegen/create-list', data); +} + +/** 删除代码生成表定义 */ +export function deleteCodegenTable(tableId: number) { + return requestClient.delete('/infra/codegen/delete', { + params: { tableId }, + }); +} + +/** 批量删除代码生成表定义 */ +export function deleteCodegenTableList(tableIds: number[]) { + return requestClient.delete( + `/infra/codegen/delete-list?tableIds=${tableIds.join(',')}`, + ); +} diff --git a/apps/web-naive/src/api/infra/config/index.ts b/apps/web-naive/src/api/infra/config/index.ts new file mode 100644 index 000000000..aa80bcbed --- /dev/null +++ b/apps/web-naive/src/api/infra/config/index.ts @@ -0,0 +1,67 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace InfraConfigApi { + /** 参数配置信息 */ + export interface Config { + id?: number; + category: string; + name: string; + key: string; + value: string; + type: number; + visible: boolean; + remark: string; + createTime?: Date; + } +} + +/** 查询参数列表 */ +export function getConfigPage(params: PageParam) { + return requestClient.get>( + '/infra/config/page', + { + params, + }, + ); +} + +/** 查询参数详情 */ +export function getConfig(id: number) { + return requestClient.get(`/infra/config/get?id=${id}`); +} + +/** 根据参数键名查询参数值 */ +export function getConfigKey(configKey: string) { + return requestClient.get( + `/infra/config/get-value-by-key?key=${configKey}`, + ); +} + +/** 新增参数 */ +export function createConfig(data: InfraConfigApi.Config) { + return requestClient.post('/infra/config/create', data); +} + +/** 修改参数 */ +export function updateConfig(data: InfraConfigApi.Config) { + return requestClient.put('/infra/config/update', data); +} + +/** 删除参数 */ +export function deleteConfig(id: number) { + return requestClient.delete(`/infra/config/delete?id=${id}`); +} + +/** 批量删除参数 */ +export function deleteConfigList(ids: number[]) { + return requestClient.delete(`/infra/config/delete-list?ids=${ids.join(',')}`); +} + +/** 导出参数 */ +export function exportConfig(params: any) { + return requestClient.download('/infra/config/export-excel', { + params, + }); +} diff --git a/apps/web-naive/src/api/infra/data-source-config/index.ts b/apps/web-naive/src/api/infra/data-source-config/index.ts new file mode 100644 index 000000000..81a3b0e3d --- /dev/null +++ b/apps/web-naive/src/api/infra/data-source-config/index.ts @@ -0,0 +1,53 @@ +import { requestClient } from '#/api/request'; + +export namespace InfraDataSourceConfigApi { + /** 数据源配置信息 */ + export interface DataSourceConfig { + id?: number; + name: string; + url: string; + username: string; + password: string; + createTime?: Date; + } +} + +/** 查询数据源配置列表 */ +export function getDataSourceConfigList() { + return requestClient.get( + '/infra/data-source-config/list', + ); +} + +/** 查询数据源配置详情 */ +export function getDataSourceConfig(id: number) { + return requestClient.get( + `/infra/data-source-config/get?id=${id}`, + ); +} + +/** 新增数据源配置 */ +export function createDataSourceConfig( + data: InfraDataSourceConfigApi.DataSourceConfig, +) { + return requestClient.post('/infra/data-source-config/create', data); +} + +/** 修改数据源配置 */ +export function updateDataSourceConfig( + data: InfraDataSourceConfigApi.DataSourceConfig, +) { + return requestClient.put('/infra/data-source-config/update', data); +} + +/** 删除数据源配置 */ +export function deleteDataSourceConfig(id: number) { + return requestClient.delete(`/infra/data-source-config/delete?id=${id}`); +} + +/** 批量删除数据源配置 */ +export function deleteDataSourceConfigList(ids: number[]) { + return requestClient.delete( + `/infra/data-source-config/delete-list?ids=${ids.join(',')}`, + ); +} diff --git a/apps/web-naive/src/api/infra/demo/demo01/index.ts b/apps/web-naive/src/api/infra/demo/demo01/index.ts new file mode 100644 index 000000000..e180a76c4 --- /dev/null +++ b/apps/web-naive/src/api/infra/demo/demo01/index.ts @@ -0,0 +1,61 @@ +import type { Dayjs } from 'dayjs'; + +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace Demo01ContactApi { + /** 示例联系人信息 */ + export interface Demo01Contact { + id: number; // 编号 + name?: string; // 名字 + sex?: number; // 性别 + birthday?: Dayjs | string; // 出生年 + description?: string; // 简介 + avatar: string; // 头像 + } +} + +/** 查询示例联系人分页 */ +export function getDemo01ContactPage(params: PageParam) { + return requestClient.get>( + '/infra/demo01-contact/page', + { params }, + ); +} + +/** 查询示例联系人详情 */ +export function getDemo01Contact(id: number) { + return requestClient.get( + `/infra/demo01-contact/get?id=${id}`, + ); +} + +/** 新增示例联系人 */ +export function createDemo01Contact(data: Demo01ContactApi.Demo01Contact) { + return requestClient.post('/infra/demo01-contact/create', data); +} + +/** 修改示例联系人 */ +export function updateDemo01Contact(data: Demo01ContactApi.Demo01Contact) { + return requestClient.put('/infra/demo01-contact/update', data); +} + +/** 删除示例联系人 */ +export function deleteDemo01Contact(id: number) { + return requestClient.delete(`/infra/demo01-contact/delete?id=${id}`); +} + +/** 批量删除示例联系人 */ +export function deleteDemo01ContactList(ids: number[]) { + return requestClient.delete( + `/infra/demo01-contact/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出示例联系人 */ +export function exportDemo01Contact(params: any) { + return requestClient.download('/infra/demo01-contact/export-excel', { + params, + }); +} diff --git a/apps/web-naive/src/api/infra/demo/demo02/index.ts b/apps/web-naive/src/api/infra/demo/demo02/index.ts new file mode 100644 index 000000000..b4edd746e --- /dev/null +++ b/apps/web-naive/src/api/infra/demo/demo02/index.ts @@ -0,0 +1,48 @@ +import { requestClient } from '#/api/request'; + +export namespace Demo02CategoryApi { + /** 示例分类信息 */ + export interface Demo02Category { + id: number; // 编号 + name?: string; // 名字 + parentId?: number; // 父级编号 + children?: Demo02Category[]; + } +} + +/** 查询示例分类列表 */ +export function getDemo02CategoryList(params: any) { + return requestClient.get( + '/infra/demo02-category/list', + { params }, + ); +} + +/** 查询示例分类详情 */ +export function getDemo02Category(id: number) { + return requestClient.get( + `/infra/demo02-category/get?id=${id}`, + ); +} + +/** 新增示例分类 */ +export function createDemo02Category(data: Demo02CategoryApi.Demo02Category) { + return requestClient.post('/infra/demo02-category/create', data); +} + +/** 修改示例分类 */ +export function updateDemo02Category(data: Demo02CategoryApi.Demo02Category) { + return requestClient.put('/infra/demo02-category/update', data); +} + +/** 删除示例分类 */ +export function deleteDemo02Category(id: number) { + return requestClient.delete(`/infra/demo02-category/delete?id=${id}`); +} + +/** 导出示例分类 */ +export function exportDemo02Category(params: any) { + return requestClient.download('/infra/demo02-category/export-excel', { + params, + }); +} diff --git a/apps/web-naive/src/api/infra/demo/demo03/erp/index.ts b/apps/web-naive/src/api/infra/demo/demo03/erp/index.ts new file mode 100644 index 000000000..6dc7e352a --- /dev/null +++ b/apps/web-naive/src/api/infra/demo/demo03/erp/index.ts @@ -0,0 +1,168 @@ +import type { Dayjs } from 'dayjs'; + +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace Demo03StudentApi { + /** 学生课程信息 */ + export interface Demo03Course { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + score?: number; // 分数 + } + + /** 学生班级信息 */ + export interface Demo03Grade { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + teacher?: string; // 班主任 + } + + /** 学生信息 */ + export interface Demo03Student { + id: number; // 编号 + name?: string; // 名字 + sex?: number; // 性别 + birthday?: Dayjs | string; // 出生日期 + description?: string; // 简介 + } +} + +/** 查询学生分页 */ +export function getDemo03StudentPage(params: PageParam) { + return requestClient.get>( + '/infra/demo03-student-erp/page', + { params }, + ); +} + +/** 查询学生详情 */ +export function getDemo03Student(id: number) { + return requestClient.get( + `/infra/demo03-student-erp/get?id=${id}`, + ); +} + +/** 新增学生 */ +export function createDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.post('/infra/demo03-student-erp/create', data); +} + +/** 修改学生 */ +export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.put('/infra/demo03-student-erp/update', data); +} + +/** 删除学生 */ +export function deleteDemo03Student(id: number) { + return requestClient.delete(`/infra/demo03-student-erp/delete?id=${id}`); +} + +/** 批量删除学生 */ +export function deleteDemo03StudentList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-erp/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出学生 */ +export function exportDemo03Student(params: any) { + return requestClient.download('/infra/demo03-student-erp/export-excel', { + params, + }); +} + +// ==================== 子表(学生课程) ==================== + +/** 获得学生课程分页 */ +export function getDemo03CoursePage(params: PageParam) { + return requestClient.get>( + `/infra/demo03-student-erp/demo03-course/page`, + { params }, + ); +} +/** 新增学生课程 */ +export function createDemo03Course(data: Demo03StudentApi.Demo03Course) { + return requestClient.post( + `/infra/demo03-student-erp/demo03-course/create`, + data, + ); +} + +/** 修改学生课程 */ +export function updateDemo03Course(data: Demo03StudentApi.Demo03Course) { + return requestClient.put( + `/infra/demo03-student-erp/demo03-course/update`, + data, + ); +} + +/** 删除学生课程 */ +export function deleteDemo03Course(id: number) { + return requestClient.delete( + `/infra/demo03-student-erp/demo03-course/delete?id=${id}`, + ); +} + +/** 批量删除学生课程 */ +export function deleteDemo03CourseList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-erp/demo03-course/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 获得学生课程 */ +export function getDemo03Course(id: number) { + return requestClient.get( + `/infra/demo03-student-erp/demo03-course/get?id=${id}`, + ); +} + +// ==================== 子表(学生班级) ==================== + +/** 获得学生班级分页 */ +export function getDemo03GradePage(params: PageParam) { + return requestClient.get>( + `/infra/demo03-student-erp/demo03-grade/page`, + { params }, + ); +} +/** 新增学生班级 */ +export function createDemo03Grade(data: Demo03StudentApi.Demo03Grade) { + return requestClient.post( + `/infra/demo03-student-erp/demo03-grade/create`, + data, + ); +} + +/** 修改学生班级 */ +export function updateDemo03Grade(data: Demo03StudentApi.Demo03Grade) { + return requestClient.put( + `/infra/demo03-student-erp/demo03-grade/update`, + data, + ); +} + +/** 删除学生班级 */ +export function deleteDemo03Grade(id: number) { + return requestClient.delete( + `/infra/demo03-student-erp/demo03-grade/delete?id=${id}`, + ); +} + +/** 批量删除学生班级 */ +export function deleteDemo03GradeList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-erp/demo03-grade/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 获得学生班级 */ +export function getDemo03Grade(id: number) { + return requestClient.get( + `/infra/demo03-student-erp/demo03-grade/get?id=${id}`, + ); +} diff --git a/apps/web-naive/src/api/infra/demo/demo03/inner/index.ts b/apps/web-naive/src/api/infra/demo/demo03/inner/index.ts new file mode 100644 index 000000000..02bfbcc66 --- /dev/null +++ b/apps/web-naive/src/api/infra/demo/demo03/inner/index.ts @@ -0,0 +1,96 @@ +import type { Dayjs } from 'dayjs'; + +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace Demo03StudentApi { + /** 学生课程信息 */ + export interface Demo03Course { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + score?: number; // 分数 + } + + /** 学生班级信息 */ + export interface Demo03Grade { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + teacher?: string; // 班主任 + } + + /** 学生信息 */ + export interface Demo03Student { + id: number; // 编号 + name?: string; // 名字 + sex?: number; // 性别 + birthday?: Dayjs | string; // 出生日期 + description?: string; // 简介 + demo03courses?: Demo03Course[]; + demo03grade?: Demo03Grade; + } +} + +/** 查询学生分页 */ +export function getDemo03StudentPage(params: PageParam) { + return requestClient.get>( + '/infra/demo03-student-inner/page', + { params }, + ); +} + +/** 查询学生详情 */ +export function getDemo03Student(id: number) { + return requestClient.get( + `/infra/demo03-student-inner/get?id=${id}`, + ); +} + +/** 新增学生 */ +export function createDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.post('/infra/demo03-student-inner/create', data); +} + +/** 修改学生 */ +export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.put('/infra/demo03-student-inner/update', data); +} + +/** 删除学生 */ +export function deleteDemo03Student(id: number) { + return requestClient.delete(`/infra/demo03-student-inner/delete?id=${id}`); +} + +/** 批量删除学生 */ +export function deleteDemo03StudentList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-inner/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出学生 */ +export function exportDemo03Student(params: any) { + return requestClient.download('/infra/demo03-student-inner/export-excel', { + params, + }); +} + +// ==================== 子表(学生课程) ==================== + +/** 获得学生课程列表 */ +export function getDemo03CourseListByStudentId(studentId: number) { + return requestClient.get( + `/infra/demo03-student-inner/demo03-course/list-by-student-id?studentId=${studentId}`, + ); +} + +// ==================== 子表(学生班级) ==================== + +/** 获得学生班级 */ +export function getDemo03GradeByStudentId(studentId: number) { + return requestClient.get( + `/infra/demo03-student-inner/demo03-grade/get-by-student-id?studentId=${studentId}`, + ); +} diff --git a/apps/web-naive/src/api/infra/demo/demo03/normal/index.ts b/apps/web-naive/src/api/infra/demo/demo03/normal/index.ts new file mode 100644 index 000000000..2a085a153 --- /dev/null +++ b/apps/web-naive/src/api/infra/demo/demo03/normal/index.ts @@ -0,0 +1,96 @@ +import type { Dayjs } from 'dayjs'; + +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace Demo03StudentApi { + /** 学生课程信息 */ + export interface Demo03Course { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + score?: number; // 分数 + } + + /** 学生班级信息 */ + export interface Demo03Grade { + id: number; // 编号 + studentId?: number; // 学生编号 + name?: string; // 名字 + teacher?: string; // 班主任 + } + + /** 学生信息 */ + export interface Demo03Student { + id: number; // 编号 + name?: string; // 名字 + sex?: number; // 性别 + birthday?: Dayjs | string; // 出生日期 + description?: string; // 简介 + demo03courses?: Demo03Course[]; + demo03grade?: Demo03Grade; + } +} + +/** 查询学生分页 */ +export function getDemo03StudentPage(params: PageParam) { + return requestClient.get>( + '/infra/demo03-student-normal/page', + { params }, + ); +} + +/** 查询学生详情 */ +export function getDemo03Student(id: number) { + return requestClient.get( + `/infra/demo03-student-normal/get?id=${id}`, + ); +} + +/** 新增学生 */ +export function createDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.post('/infra/demo03-student-normal/create', data); +} + +/** 修改学生 */ +export function updateDemo03Student(data: Demo03StudentApi.Demo03Student) { + return requestClient.put('/infra/demo03-student-normal/update', data); +} + +/** 删除学生 */ +export function deleteDemo03Student(id: number) { + return requestClient.delete(`/infra/demo03-student-normal/delete?id=${id}`); +} + +/** 批量删除学生 */ +export function deleteDemo03StudentList(ids: number[]) { + return requestClient.delete( + `/infra/demo03-student-normal/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出学生 */ +export function exportDemo03Student(params: any) { + return requestClient.download('/infra/demo03-student-normal/export-excel', { + params, + }); +} + +// ==================== 子表(学生课程) ==================== + +/** 获得学生课程列表 */ +export function getDemo03CourseListByStudentId(studentId: number) { + return requestClient.get( + `/infra/demo03-student-normal/demo03-course/list-by-student-id?studentId=${studentId}`, + ); +} + +// ==================== 子表(学生班级) ==================== + +/** 获得学生班级 */ +export function getDemo03GradeByStudentId(studentId: number) { + return requestClient.get( + `/infra/demo03-student-normal/demo03-grade/get-by-student-id?studentId=${studentId}`, + ); +} diff --git a/apps/web-naive/src/api/infra/file-config/index.ts b/apps/web-naive/src/api/infra/file-config/index.ts new file mode 100644 index 000000000..1a2fb7707 --- /dev/null +++ b/apps/web-naive/src/api/infra/file-config/index.ts @@ -0,0 +1,83 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace InfraFileConfigApi { + /** 文件客户端配置 */ + export interface FileClientConfig { + basePath: string; + host?: string; + port?: number; + username?: string; + password?: string; + mode?: string; + endpoint?: string; + bucket?: string; + accessKey?: string; + accessSecret?: string; + pathStyle?: boolean; + enablePublicAccess?: boolean; + domain: string; + } + + /** 文件配置信息 */ + export interface FileConfig { + id?: number; + name: string; + storage?: number; + master: boolean; + visible: boolean; + config: FileClientConfig; + remark: string; + createTime?: Date; + } +} + +/** 查询文件配置列表 */ +export function getFileConfigPage(params: PageParam) { + return requestClient.get>( + '/infra/file-config/page', + { + params, + }, + ); +} + +/** 查询文件配置详情 */ +export function getFileConfig(id: number) { + return requestClient.get( + `/infra/file-config/get?id=${id}`, + ); +} + +/** 更新文件配置为主配置 */ +export function updateFileConfigMaster(id: number) { + return requestClient.put(`/infra/file-config/update-master?id=${id}`); +} + +/** 新增文件配置 */ +export function createFileConfig(data: InfraFileConfigApi.FileConfig) { + return requestClient.post('/infra/file-config/create', data); +} + +/** 修改文件配置 */ +export function updateFileConfig(data: InfraFileConfigApi.FileConfig) { + return requestClient.put('/infra/file-config/update', data); +} + +/** 删除文件配置 */ +export function deleteFileConfig(id: number) { + return requestClient.delete(`/infra/file-config/delete?id=${id}`); +} + +/** 批量删除文件配置 */ +export function deleteFileConfigList(ids: number[]) { + return requestClient.delete( + `/infra/file-config/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 测试文件配置 */ +export function testFileConfig(id: number) { + return requestClient.get(`/infra/file-config/test?id=${id}`); +} diff --git a/apps/web-naive/src/api/infra/file/index.ts b/apps/web-naive/src/api/infra/file/index.ts new file mode 100644 index 000000000..edf06933b --- /dev/null +++ b/apps/web-naive/src/api/infra/file/index.ts @@ -0,0 +1,78 @@ +import type { AxiosRequestConfig, PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +/** Axios 上传进度事件 */ +export type AxiosProgressEvent = AxiosRequestConfig['onUploadProgress']; + +export namespace InfraFileApi { + /** 文件信息 */ + export interface File { + id?: number; + configId?: number; + path: string; + name?: string; + url?: string; + size?: number; + type?: string; + createTime?: Date; + } + + /** 文件预签名地址 */ + export interface FilePresignedUrlRespVO { + configId: number; // 文件配置编号 + uploadUrl: string; // 文件上传 URL + url: string; // 文件 URL + path: string; // 文件路径 + } + + /** 上传文件 */ + export interface FileUploadReqVO { + file: globalThis.File; + directory?: string; + } +} + +/** 查询文件列表 */ +export function getFilePage(params: PageParam) { + return requestClient.get>('/infra/file/page', { + params, + }); +} + +/** 删除文件 */ +export function deleteFile(id: number) { + return requestClient.delete(`/infra/file/delete?id=${id}`); +} + +/** 批量删除文件 */ +export function deleteFileList(ids: number[]) { + return requestClient.delete(`/infra/file/delete-list?ids=${ids.join(',')}`); +} + +/** 获取文件预签名地址 */ +export function getFilePresignedUrl(name: string, directory?: string) { + return requestClient.get( + '/infra/file/presigned-url', + { + params: { name, directory }, + }, + ); +} + +/** 创建文件 */ +export function createFile(data: InfraFileApi.File) { + return requestClient.post('/infra/file/create', data); +} + +/** 上传文件 */ +export function uploadFile( + data: InfraFileApi.FileUploadReqVO, + onUploadProgress?: AxiosProgressEvent, +) { + // 特殊:由于 upload 内部封装,即使 directory 为 undefined,也会传递给后端 + if (!data.directory) { + delete data.directory; + } + return requestClient.upload('/infra/file/upload', data, { onUploadProgress }); +} diff --git a/apps/web-naive/src/api/infra/job/index.ts b/apps/web-naive/src/api/infra/job/index.ts new file mode 100644 index 000000000..04848dd8b --- /dev/null +++ b/apps/web-naive/src/api/infra/job/index.ts @@ -0,0 +1,77 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace InfraJobApi { + /** 任务信息 */ + export interface Job { + id?: number; + name: string; + status: number; + handlerName: string; + handlerParam: string; + cronExpression: string; + retryCount: number; + retryInterval: number; + monitorTimeout: number; + createTime?: Date; + nextTimes?: Date[]; + } +} + +/** 查询任务列表 */ +export function getJobPage(params: PageParam) { + return requestClient.get>('/infra/job/page', { + params, + }); +} + +/** 查询任务详情 */ +export function getJob(id: number) { + return requestClient.get(`/infra/job/get?id=${id}`); +} + +/** 新增任务 */ +export function createJob(data: InfraJobApi.Job) { + return requestClient.post('/infra/job/create', data); +} + +/** 修改定时任务调度 */ +export function updateJob(data: InfraJobApi.Job) { + return requestClient.put('/infra/job/update', data); +} + +/** 删除定时任务调度 */ +export function deleteJob(id: number) { + return requestClient.delete(`/infra/job/delete?id=${id}`); +} + +/** 批量删除定时任务调度 */ +export function deleteJobList(ids: number[]) { + return requestClient.delete(`/infra/job/delete-list?ids=${ids.join(',')}`); +} + +/** 导出定时任务调度 */ +export function exportJob(params: any) { + return requestClient.download('/infra/job/export-excel', { params }); +} + +/** 任务状态修改 */ +export function updateJobStatus(id: number, status: number) { + return requestClient.put('/infra/job/update-status', undefined, { + params: { + id, + status, + }, + }); +} + +/** 定时任务立即执行一次 */ +export function runJob(id: number) { + return requestClient.put(`/infra/job/trigger?id=${id}`); +} + +/** 获得定时任务的下 n 次执行时间 */ +export function getJobNextTimes(id: number) { + return requestClient.get(`/infra/job/get_next_times?id=${id}`); +} diff --git a/apps/web-naive/src/api/request.ts b/apps/web-naive/src/api/request.ts new file mode 100644 index 000000000..44f7587b4 --- /dev/null +++ b/apps/web-naive/src/api/request.ts @@ -0,0 +1,186 @@ +/** + * 该文件可自行根据业务逻辑进行调整 + */ +import type { RequestClientOptions } from '@vben/request'; + +import { isTenantEnable, useAppConfig } from '@vben/hooks'; +import { preferences } from '@vben/preferences'; +import { + authenticateResponseInterceptor, + defaultResponseInterceptor, + errorMessageResponseInterceptor, + RequestClient, +} from '@vben/request'; +import { useAccessStore } from '@vben/stores'; +import { createApiEncrypt } from '@vben/utils'; + +import { message } from '#/adapter/naive'; +import { useAuthStore } from '#/store'; + +import { refreshTokenApi } from './core'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); +const tenantEnable = isTenantEnable(); +const apiEncrypt = createApiEncrypt(import.meta.env); + +function createRequestClient(baseURL: string, options?: RequestClientOptions) { + const client = new RequestClient({ + ...options, + baseURL, + }); + + /** + * 重新认证逻辑 + */ + async function doReAuthenticate() { + console.warn('Access token or refresh token is invalid or expired. '); + const accessStore = useAccessStore(); + const authStore = useAuthStore(); + accessStore.setAccessToken(null); + if ( + preferences.app.loginExpiredMode === 'modal' && + accessStore.isAccessChecked + ) { + accessStore.setLoginExpired(true); + } else { + await authStore.logout(); + } + } + + /** + * 刷新token逻辑 + */ + async function doRefreshToken() { + const accessStore = useAccessStore(); + const refreshToken = accessStore.refreshToken as string; + if (!refreshToken) { + throw new Error('Refresh token is null!'); + } + const resp = await refreshTokenApi(refreshToken); + const newToken = resp?.data?.data?.accessToken; + // add by 芋艿:这里一定要抛出 resp.data,从而触发 authenticateResponseInterceptor 中,刷新令牌失败!!! + if (!newToken) { + throw resp.data; + } + accessStore.setAccessToken(newToken); + return newToken; + } + + function formatToken(token: null | string) { + return token ? `Bearer ${token}` : null; + } + + // 请求头处理 + client.addRequestInterceptor({ + fulfilled: async (config) => { + const accessStore = useAccessStore(); + + config.headers.Authorization = formatToken(accessStore.accessToken); + config.headers['Accept-Language'] = preferences.app.locale; + // 添加租户编号 + config.headers['tenant-id'] = tenantEnable + ? accessStore.tenantId + : undefined; + // 只有登录时,才设置 visit-tenant-id 访问租户 + config.headers['visit-tenant-id'] = tenantEnable + ? accessStore.visitTenantId + : undefined; + + // 是否 API 加密 + if ((config.headers || {}).isEncrypt) { + try { + // 加密请求数据 + if (config.data) { + config.data = apiEncrypt.encryptRequest(config.data); + // 设置加密标识头 + config.headers[apiEncrypt.getEncryptHeader()] = 'true'; + } + } catch (error) { + console.error('请求数据加密失败:', error); + throw error; + } + } + return config; + }, + }); + + // API 解密响应拦截器 + client.addResponseInterceptor({ + fulfilled: (response) => { + // 检查是否需要解密响应数据 + const encryptHeader = apiEncrypt.getEncryptHeader(); + const isEncryptResponse = + response.headers[encryptHeader] === 'true' || + response.headers[encryptHeader.toLowerCase()] === 'true'; + if (isEncryptResponse && typeof response.data === 'string') { + try { + // 解密响应数据 + response.data = apiEncrypt.decryptResponse(response.data); + } catch (error) { + console.error('响应数据解密失败:', error); + throw new Error(`响应数据解密失败: ${(error as Error).message}`); + } + } + return response; + }, + }); + + // 处理返回的响应数据格式 + client.addResponseInterceptor( + defaultResponseInterceptor({ + codeField: 'code', + dataField: 'data', + successCode: 0, + }), + ); + + // token过期的处理 + client.addResponseInterceptor( + authenticateResponseInterceptor({ + client, + doReAuthenticate, + doRefreshToken, + enableRefreshToken: preferences.app.enableRefreshToken, + formatToken, + }), + ); + + // 通用的错误处理,如果没有进入上面的错误处理逻辑,就会进入这里 + client.addResponseInterceptor( + errorMessageResponseInterceptor((msg: string, error) => { + // 这里可以根据业务进行定制,你可以拿到 error 内的信息进行定制化处理,根据不同的 code 做不同的提示,而不是直接使用 message.error 提示 msg + // 当前mock接口返回的错误字段是 error 或者 message + const responseData = error?.response?.data ?? {}; + const errorMessage = + responseData?.error ?? responseData?.message ?? responseData.msg ?? ''; + // add by 芋艿:特殊:避免 401 “账号未登录”,重复提示。因为,此时会跳转到登录界面,只需提示一次!!! + if (error?.data?.code === 401) { + return; + } + // 如果没有错误信息,则会根据状态码进行提示 + message.error(errorMessage || msg); + }), + ); + + return client; +} + +export const requestClient = createRequestClient(apiURL, { + responseReturn: 'data', +}); + +export const baseRequestClient = new RequestClient({ baseURL: apiURL }); +baseRequestClient.addRequestInterceptor({ + fulfilled: (config) => { + const accessStore = useAccessStore(); + // 添加租户编号 + config.headers['tenant-id'] = tenantEnable + ? accessStore.tenantId + : undefined; + // 只有登录时,才设置 visit-tenant-id 访问租户 + config.headers['visit-tenant-id'] = tenantEnable + ? accessStore.visitTenantId + : undefined; + return config; + }, +}); diff --git a/apps/web-naive/src/api/system/dept/index.ts b/apps/web-naive/src/api/system/dept/index.ts new file mode 100644 index 000000000..d6403d72a --- /dev/null +++ b/apps/web-naive/src/api/system/dept/index.ts @@ -0,0 +1,52 @@ +import { requestClient } from '#/api/request'; + +export namespace SystemDeptApi { + /** 部门信息 */ + export interface Dept { + id?: number; + name: string; + parentId?: number; + status: number; + sort: number; + leaderUserId: number; + phone: string; + email: string; + createTime: Date; + children?: Dept[]; + } +} + +/** 查询部门(精简)列表 */ +export async function getSimpleDeptList() { + return requestClient.get('/system/dept/simple-list'); +} + +/** 查询部门列表 */ +export async function getDeptList() { + return requestClient.get('/system/dept/list'); +} + +/** 查询部门详情 */ +export async function getDept(id: number) { + return requestClient.get(`/system/dept/get?id=${id}`); +} + +/** 新增部门 */ +export async function createDept(data: SystemDeptApi.Dept) { + return requestClient.post('/system/dept/create', data); +} + +/** 修改部门 */ +export async function updateDept(data: SystemDeptApi.Dept) { + return requestClient.put('/system/dept/update', data); +} + +/** 删除部门 */ +export async function deleteDept(id: number) { + return requestClient.delete(`/system/dept/delete?id=${id}`); +} + +/** 批量删除部门 */ +export async function deleteDeptList(ids: number[]) { + return requestClient.delete(`/system/dept/delete-list?ids=${ids.join(',')}`); +} diff --git a/apps/web-naive/src/api/system/dict/data/index.ts b/apps/web-naive/src/api/system/dict/data/index.ts new file mode 100644 index 000000000..28f94d504 --- /dev/null +++ b/apps/web-naive/src/api/system/dict/data/index.ts @@ -0,0 +1,68 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemDictDataApi { + /** 字典数据 */ + export type DictData = { + colorType: string; + createTime: Date; + cssClass: string; + dictType: string; + id?: number; + label: string; + remark: string; + sort?: number; + status: number; + value: string; + }; +} + +// 查询字典数据(精简)列表 +export function getSimpleDictDataList() { + return requestClient.get( + '/system/dict-data/simple-list', + ); +} + +// 查询字典数据列表 +export function getDictDataPage(params: PageParam) { + return requestClient.get>( + '/system/dict-data/page', + { params }, + ); +} + +// 查询字典数据详情 +export function getDictData(id: number) { + return requestClient.get( + `/system/dict-data/get?id=${id}`, + ); +} + +// 新增字典数据 +export function createDictData(data: SystemDictDataApi.DictData) { + return requestClient.post('/system/dict-data/create', data); +} + +// 修改字典数据 +export function updateDictData(data: SystemDictDataApi.DictData) { + return requestClient.put('/system/dict-data/update', data); +} + +// 删除字典数据 +export function deleteDictData(id: number) { + return requestClient.delete(`/system/dict-data/delete?id=${id}`); +} + +// 批量删除字典数据 +export function deleteDictDataList(ids: number[]) { + return requestClient.delete( + `/system/dict-data/delete-list?ids=${ids.join(',')}`, + ); +} + +// 导出字典类型数据 +export function exportDictData(params: any) { + return requestClient.download('/system/dict-data/export-excel', { params }); +} diff --git a/apps/web-naive/src/api/system/dict/type/index.ts b/apps/web-naive/src/api/system/dict/type/index.ts new file mode 100644 index 000000000..7fb426337 --- /dev/null +++ b/apps/web-naive/src/api/system/dict/type/index.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemDictTypeApi { + /** 字典类型 */ + export type DictType = { + createTime: Date; + id?: number; + name: string; + remark: string; + status: number; + type: string; + }; +} + +// 查询字典(精简)列表 +export function getSimpleDictTypeList() { + return requestClient.get( + '/system/dict-type/list-all-simple', + ); +} + +// 查询字典列表 +export function getDictTypePage(params: PageParam) { + return requestClient.get>( + '/system/dict-type/page', + { params }, + ); +} + +// 查询字典详情 +export function getDictType(id: number) { + return requestClient.get( + `/system/dict-type/get?id=${id}`, + ); +} + +// 新增字典 +export function createDictType(data: SystemDictTypeApi.DictType) { + return requestClient.post('/system/dict-type/create', data); +} + +// 修改字典 +export function updateDictType(data: SystemDictTypeApi.DictType) { + return requestClient.put('/system/dict-type/update', data); +} + +// 删除字典 +export function deleteDictType(id: number) { + return requestClient.delete(`/system/dict-type/delete?id=${id}`); +} + +// 批量删除字典 +export function deleteDictTypeList(ids: number[]) { + return requestClient.delete( + `/system/dict-type/delete-list?ids=${ids.join(',')}`, + ); +} + +// 导出字典类型 +export function exportDictType(params: any) { + return requestClient.download('/system/dict-type/export-excel', { params }); +} diff --git a/apps/web-naive/src/api/system/mail/account/index.ts b/apps/web-naive/src/api/system/mail/account/index.ts new file mode 100644 index 000000000..7f506fd87 --- /dev/null +++ b/apps/web-naive/src/api/system/mail/account/index.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemMailAccountApi { + /** 邮箱账号 */ + export interface MailAccount { + id: number; + mail: string; + username: string; + password: string; + host: string; + port: number; + sslEnable: boolean; + starttlsEnable: boolean; + status: number; + createTime: Date; + remark: string; + } +} + +/** 查询邮箱账号列表 */ +export function getMailAccountPage(params: PageParam) { + return requestClient.get>( + '/system/mail-account/page', + { params }, + ); +} + +/** 查询邮箱账号详情 */ +export function getMailAccount(id: number) { + return requestClient.get( + `/system/mail-account/get?id=${id}`, + ); +} + +/** 新增邮箱账号 */ +export function createMailAccount(data: SystemMailAccountApi.MailAccount) { + return requestClient.post('/system/mail-account/create', data); +} + +/** 修改邮箱账号 */ +export function updateMailAccount(data: SystemMailAccountApi.MailAccount) { + return requestClient.put('/system/mail-account/update', data); +} + +/** 删除邮箱账号 */ +export function deleteMailAccount(id: number) { + return requestClient.delete(`/system/mail-account/delete?id=${id}`); +} + +/** 批量删除邮箱账号 */ +export function deleteMailAccountList(ids: number[]) { + return requestClient.delete( + `/system/mail-account/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 获得邮箱账号精简列表 */ +export function getSimpleMailAccountList() { + return requestClient.get( + '/system/mail-account/simple-list', + ); +} diff --git a/apps/web-naive/src/api/system/mail/log/index.ts b/apps/web-naive/src/api/system/mail/log/index.ts new file mode 100644 index 000000000..c32b790e1 --- /dev/null +++ b/apps/web-naive/src/api/system/mail/log/index.ts @@ -0,0 +1,36 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemMailLogApi { + /** 邮件日志 */ + export interface MailLog { + id: number; + userId: number; + userType: number; + toMails: string[]; + ccMails?: string[]; + bccMails?: string[]; + accountId: number; + fromMail: string; + templateId: number; + templateCode: string; + templateNickname: string; + templateTitle: string; + templateContent: string; + templateParams: string; + sendStatus: number; + sendTime: string; + sendMessageId: string; + sendException: string; + createTime: string; + } +} + +/** 查询邮件日志列表 */ +export function getMailLogPage(params: PageParam) { + return requestClient.get>( + '/system/mail-log/page', + { params }, + ); +} diff --git a/apps/web-naive/src/api/system/mail/template/index.ts b/apps/web-naive/src/api/system/mail/template/index.ts new file mode 100644 index 000000000..9e2a5a78d --- /dev/null +++ b/apps/web-naive/src/api/system/mail/template/index.ts @@ -0,0 +1,71 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemMailTemplateApi { + /** 邮件模版信息 */ + export interface MailTemplate { + id: number; + name: string; + code: string; + accountId: number; + nickname: string; + title: string; + content: string; + params: string[]; + status: number; + remark: string; + createTime: Date; + } + + /** 邮件发送信息 */ + export interface MailSendReqVO { + toMails: string[]; + ccMails?: string[]; + bccMails?: string[]; + templateCode: string; + templateParams: Record; + } +} + +/** 查询邮件模版列表 */ +export function getMailTemplatePage(params: PageParam) { + return requestClient.get>( + '/system/mail-template/page', + { params }, + ); +} + +/** 查询邮件模版详情 */ +export function getMailTemplate(id: number) { + return requestClient.get( + `/system/mail-template/get?id=${id}`, + ); +} + +/** 新增邮件模版 */ +export function createMailTemplate(data: SystemMailTemplateApi.MailTemplate) { + return requestClient.post('/system/mail-template/create', data); +} + +/** 修改邮件模版 */ +export function updateMailTemplate(data: SystemMailTemplateApi.MailTemplate) { + return requestClient.put('/system/mail-template/update', data); +} + +/** 删除邮件模版 */ +export function deleteMailTemplate(id: number) { + return requestClient.delete(`/system/mail-template/delete?id=${id}`); +} + +/** 批量删除邮件模版 */ +export function deleteMailTemplateList(ids: number[]) { + return requestClient.delete( + `/system/mail-template/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 发送邮件 */ +export function sendMail(data: SystemMailTemplateApi.MailSendReqVO) { + return requestClient.post('/system/mail-template/send-mail', data); +} diff --git a/apps/web-naive/src/api/system/menu/index.ts b/apps/web-naive/src/api/system/menu/index.ts new file mode 100644 index 000000000..b296d815b --- /dev/null +++ b/apps/web-naive/src/api/system/menu/index.ts @@ -0,0 +1,59 @@ +import { requestClient } from '#/api/request'; + +export namespace SystemMenuApi { + /** 菜单信息 */ + export interface Menu { + id: number; + name: string; + permission: string; + type: number; + sort: number; + parentId: number; + path: string; + icon: string; + component: string; + componentName?: string; + status: number; + visible: boolean; + keepAlive: boolean; + alwaysShow?: boolean; + createTime: Date; + } +} + +/** 查询菜单(精简)列表 */ +export async function getSimpleMenusList() { + return requestClient.get('/system/menu/simple-list'); +} + +/** 查询菜单列表 */ +export async function getMenuList(params?: Record) { + return requestClient.get('/system/menu/list', { + params, + }); +} + +/** 获取菜单详情 */ +export async function getMenu(id: number) { + return requestClient.get(`/system/menu/get?id=${id}`); +} + +/** 新增菜单 */ +export async function createMenu(data: SystemMenuApi.Menu) { + return requestClient.post('/system/menu/create', data); +} + +/** 修改菜单 */ +export async function updateMenu(data: SystemMenuApi.Menu) { + return requestClient.put('/system/menu/update', data); +} + +/** 删除菜单 */ +export async function deleteMenu(id: number) { + return requestClient.delete(`/system/menu/delete?id=${id}`); +} + +/** 批量删除菜单 */ +export async function deleteMenuList(ids: number[]) { + return requestClient.delete(`/system/menu/delete-list?ids=${ids.join(',')}`); +} diff --git a/apps/web-naive/src/api/system/notice/index.ts b/apps/web-naive/src/api/system/notice/index.ts new file mode 100644 index 000000000..5ae888538 --- /dev/null +++ b/apps/web-naive/src/api/system/notice/index.ts @@ -0,0 +1,59 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemNoticeApi { + /** 公告信息 */ + export interface Notice { + id?: number; + title: string; + type: number; + content: string; + status: number; + remark: string; + creator?: string; + createTime?: Date; + } +} + +/** 查询公告列表 */ +export function getNoticePage(params: PageParam) { + return requestClient.get>( + '/system/notice/page', + { params }, + ); +} + +/** 查询公告详情 */ +export function getNotice(id: number) { + return requestClient.get( + `/system/notice/get?id=${id}`, + ); +} + +/** 新增公告 */ +export function createNotice(data: SystemNoticeApi.Notice) { + return requestClient.post('/system/notice/create', data); +} + +/** 修改公告 */ +export function updateNotice(data: SystemNoticeApi.Notice) { + return requestClient.put('/system/notice/update', data); +} + +/** 删除公告 */ +export function deleteNotice(id: number) { + return requestClient.delete(`/system/notice/delete?id=${id}`); +} + +/** 批量删除公告 */ +export function deleteNoticeList(ids: number[]) { + return requestClient.delete( + `/system/notice/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 推送公告 */ +export function pushNotice(id: number) { + return requestClient.post(`/system/notice/push?id=${id}`); +} diff --git a/apps/web-naive/src/api/system/notify/template/index.ts b/apps/web-naive/src/api/system/notify/template/index.ts new file mode 100644 index 000000000..dd19f4b8f --- /dev/null +++ b/apps/web-naive/src/api/system/notify/template/index.ts @@ -0,0 +1,79 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemNotifyTemplateApi { + /** 站内信模板信息 */ + export interface NotifyTemplate { + id?: number; + name: string; + nickname: string; + code: string; + content: string; + type?: number; + params: string[]; + status: number; + remark: string; + } + + /** 发送站内信请求 */ + export interface NotifySendReqVO { + userId: number; + userType: number; + templateCode: string; + templateParams: Record; + } +} + +/** 查询站内信模板列表 */ +export function getNotifyTemplatePage(params: PageParam) { + return requestClient.get>( + '/system/notify-template/page', + { params }, + ); +} + +/** 查询站内信模板详情 */ +export function getNotifyTemplate(id: number) { + return requestClient.get( + `/system/notify-template/get?id=${id}`, + ); +} + +/** 新增站内信模板 */ +export function createNotifyTemplate( + data: SystemNotifyTemplateApi.NotifyTemplate, +) { + return requestClient.post('/system/notify-template/create', data); +} + +/** 修改站内信模板 */ +export function updateNotifyTemplate( + data: SystemNotifyTemplateApi.NotifyTemplate, +) { + return requestClient.put('/system/notify-template/update', data); +} + +/** 删除站内信模板 */ +export function deleteNotifyTemplate(id: number) { + return requestClient.delete(`/system/notify-template/delete?id=${id}`); +} + +/** 批量删除站内信模板 */ +export function deleteNotifyTemplateList(ids: number[]) { + return requestClient.delete( + `/system/notify-template/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出站内信模板 */ +export function exportNotifyTemplate(params: any) { + return requestClient.download('/system/notify-template/export-excel', { + params, + }); +} + +/** 发送站内信 */ +export function sendNotify(data: SystemNotifyTemplateApi.NotifySendReqVO) { + return requestClient.post('/system/notify-template/send-notify', data); +} diff --git a/apps/web-naive/src/api/system/oauth2/client/index.ts b/apps/web-naive/src/api/system/oauth2/client/index.ts new file mode 100644 index 000000000..5817b858b --- /dev/null +++ b/apps/web-naive/src/api/system/oauth2/client/index.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemOAuth2ClientApi { + /** OAuth2.0 客户端信息 */ + export interface OAuth2Client { + id?: number; + clientId: string; + secret: string; + name: string; + logo: string; + description: string; + status: number; + accessTokenValiditySeconds: number; + refreshTokenValiditySeconds: number; + redirectUris: string[]; + autoApprove: boolean; + authorizedGrantTypes: string[]; + scopes: string[]; + authorities: string[]; + resourceIds: string[]; + additionalInformation: string; + isAdditionalInformationJson: boolean; + createTime?: Date; + } +} + +/** 查询 OAuth2.0 客户端列表 */ +export function getOAuth2ClientPage(params: PageParam) { + return requestClient.get>( + '/system/oauth2-client/page', + { params }, + ); +} + +/** 查询 OAuth2.0 客户端详情 */ +export function getOAuth2Client(id: number) { + return requestClient.get( + `/system/oauth2-client/get?id=${id}`, + ); +} + +/** 新增 OAuth2.0 客户端 */ +export function createOAuth2Client(data: SystemOAuth2ClientApi.OAuth2Client) { + return requestClient.post('/system/oauth2-client/create', data); +} + +/** 修改 OAuth2.0 客户端 */ +export function updateOAuth2Client(data: SystemOAuth2ClientApi.OAuth2Client) { + return requestClient.put('/system/oauth2-client/update', data); +} + +/** 删除 OAuth2.0 客户端 */ +export function deleteOAuth2Client(id: number) { + return requestClient.delete(`/system/oauth2-client/delete?id=${id}`); +} + +/** 批量删除 OAuth2.0 客户端 */ +export function deleteOAuth2ClientList(ids: number[]) { + return requestClient.delete( + `/system/oauth2-client/delete-list?ids=${ids.join(',')}`, + ); +} diff --git a/apps/web-naive/src/api/system/post/index.ts b/apps/web-naive/src/api/system/post/index.ts new file mode 100644 index 000000000..8b32ad218 --- /dev/null +++ b/apps/web-naive/src/api/system/post/index.ts @@ -0,0 +1,63 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemPostApi { + /** 岗位信息 */ + export interface Post { + id?: number; + name: string; + code: string; + sort: number; + status: number; + remark: string; + createTime?: Date; + } +} + +/** 查询岗位列表 */ +export function getPostPage(params: PageParam) { + return requestClient.get>( + '/system/post/page', + { + params, + }, + ); +} + +/** 获取岗位精简信息列表 */ +export function getSimplePostList() { + return requestClient.get('/system/post/simple-list'); +} + +/** 查询岗位详情 */ +export function getPost(id: number) { + return requestClient.get(`/system/post/get?id=${id}`); +} + +/** 新增岗位 */ +export function createPost(data: SystemPostApi.Post) { + return requestClient.post('/system/post/create', data); +} + +/** 修改岗位 */ +export function updatePost(data: SystemPostApi.Post) { + return requestClient.put('/system/post/update', data); +} + +/** 删除岗位 */ +export function deletePost(id: number) { + return requestClient.delete(`/system/post/delete?id=${id}`); +} + +/** 批量删除岗位 */ +export function deletePostList(ids: number[]) { + return requestClient.delete(`/system/post/delete-list?ids=${ids.join(',')}`); +} + +/** 导出岗位 */ +export function exportPost(params: any) { + return requestClient.download('/system/post/export-excel', { + params, + }); +} diff --git a/apps/web-naive/src/api/system/role/index.ts b/apps/web-naive/src/api/system/role/index.ts new file mode 100644 index 000000000..23d5ade98 --- /dev/null +++ b/apps/web-naive/src/api/system/role/index.ts @@ -0,0 +1,63 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemRoleApi { + /** 角色信息 */ + export interface Role { + id?: number; + name: string; + code: string; + sort: number; + status: number; + type: number; + dataScope: number; + dataScopeDeptIds: number[]; + createTime?: Date; + } +} + +/** 查询角色列表 */ +export function getRolePage(params: PageParam) { + return requestClient.get>( + '/system/role/page', + { params }, + ); +} + +/** 查询角色(精简)列表 */ +export function getSimpleRoleList() { + return requestClient.get('/system/role/simple-list'); +} + +/** 查询角色详情 */ +export function getRole(id: number) { + return requestClient.get(`/system/role/get?id=${id}`); +} + +/** 新增角色 */ +export function createRole(data: SystemRoleApi.Role) { + return requestClient.post('/system/role/create', data); +} + +/** 修改角色 */ +export function updateRole(data: SystemRoleApi.Role) { + return requestClient.put('/system/role/update', data); +} + +/** 删除角色 */ +export function deleteRole(id: number) { + return requestClient.delete(`/system/role/delete?id=${id}`); +} + +/** 批量删除角色 */ +export function deleteRoleList(ids: number[]) { + return requestClient.delete(`/system/role/delete-list?ids=${ids.join(',')}`); +} + +/** 导出角色 */ +export function exportRole(params: any) { + return requestClient.download('/system/role/export-excel', { + params, + }); +} diff --git a/apps/web-naive/src/api/system/sms/channel/index.ts b/apps/web-naive/src/api/system/sms/channel/index.ts new file mode 100644 index 000000000..df32aad87 --- /dev/null +++ b/apps/web-naive/src/api/system/sms/channel/index.ts @@ -0,0 +1,67 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemSmsChannelApi { + /** 短信渠道信息 */ + export interface SmsChannel { + id?: number; + code: string; + status: number; + signature: string; + remark: string; + apiKey: string; + apiSecret: string; + callbackUrl: string; + createTime?: Date; + } +} + +/** 查询短信渠道列表 */ +export function getSmsChannelPage(params: PageParam) { + return requestClient.get>( + '/system/sms-channel/page', + { params }, + ); +} + +/** 获得短信渠道精简列表 */ +export function getSimpleSmsChannelList() { + return requestClient.get( + '/system/sms-channel/simple-list', + ); +} + +/** 查询短信渠道详情 */ +export function getSmsChannel(id: number) { + return requestClient.get( + `/system/sms-channel/get?id=${id}`, + ); +} + +/** 新增短信渠道 */ +export function createSmsChannel(data: SystemSmsChannelApi.SmsChannel) { + return requestClient.post('/system/sms-channel/create', data); +} + +/** 修改短信渠道 */ +export function updateSmsChannel(data: SystemSmsChannelApi.SmsChannel) { + return requestClient.put('/system/sms-channel/update', data); +} + +/** 删除短信渠道 */ +export function deleteSmsChannel(id: number) { + return requestClient.delete(`/system/sms-channel/delete?id=${id}`); +} + +/** 批量删除短信渠道 */ +export function deleteSmsChannelList(ids: number[]) { + return requestClient.delete( + `/system/sms-channel/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出短信渠道 */ +export function exportSmsChannel(params: any) { + return requestClient.download('/system/sms-channel/export-excel', { params }); +} diff --git a/apps/web-naive/src/api/system/sms/template/index.ts b/apps/web-naive/src/api/system/sms/template/index.ts new file mode 100644 index 000000000..eccfb911e --- /dev/null +++ b/apps/web-naive/src/api/system/sms/template/index.ts @@ -0,0 +1,77 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemSmsTemplateApi { + /** 短信模板信息 */ + export interface SmsTemplate { + id?: number; + type?: number; + status: number; + code: string; + name: string; + content: string; + remark: string; + apiTemplateId: string; + channelId?: number; + channelCode?: string; + params?: string[]; + createTime?: Date; + } + + /** 发送短信请求 */ + export interface SmsSendReqVO { + mobile: string; + templateCode: string; + templateParams: Record; + } +} + +/** 查询短信模板列表 */ +export function getSmsTemplatePage(params: PageParam) { + return requestClient.get>( + '/system/sms-template/page', + { params }, + ); +} + +/** 查询短信模板详情 */ +export function getSmsTemplate(id: number) { + return requestClient.get( + `/system/sms-template/get?id=${id}`, + ); +} + +/** 新增短信模板 */ +export function createSmsTemplate(data: SystemSmsTemplateApi.SmsTemplate) { + return requestClient.post('/system/sms-template/create', data); +} + +/** 修改短信模板 */ +export function updateSmsTemplate(data: SystemSmsTemplateApi.SmsTemplate) { + return requestClient.put('/system/sms-template/update', data); +} + +/** 删除短信模板 */ +export function deleteSmsTemplate(id: number) { + return requestClient.delete(`/system/sms-template/delete?id=${id}`); +} + +/** 批量删除短信模板 */ +export function deleteSmsTemplateList(ids: number[]) { + return requestClient.delete( + `/system/sms-template/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出短信模板 */ +export function exportSmsTemplate(params: any) { + return requestClient.download('/system/sms-template/export-excel', { + params, + }); +} + +/** 发送短信 */ +export function sendSms(data: SystemSmsTemplateApi.SmsSendReqVO) { + return requestClient.post('/system/sms-template/send-sms', data); +} diff --git a/apps/web-naive/src/api/system/social/client/index.ts b/apps/web-naive/src/api/system/social/client/index.ts new file mode 100644 index 000000000..181cdf86b --- /dev/null +++ b/apps/web-naive/src/api/system/social/client/index.ts @@ -0,0 +1,55 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemSocialClientApi { + /** 社交客户端信息 */ + export interface SocialClient { + id?: number; + name: string; + socialType: number; + userType: number; + clientId: string; + clientSecret: string; + agentId?: string; + status: number; + createTime?: Date; + } +} + +/** 查询社交客户端列表 */ +export function getSocialClientPage(params: PageParam) { + return requestClient.get>( + '/system/social-client/page', + { params }, + ); +} + +/** 查询社交客户端详情 */ +export function getSocialClient(id: number) { + return requestClient.get( + `/system/social-client/get?id=${id}`, + ); +} + +/** 新增社交客户端 */ +export function createSocialClient(data: SystemSocialClientApi.SocialClient) { + return requestClient.post('/system/social-client/create', data); +} + +/** 修改社交客户端 */ +export function updateSocialClient(data: SystemSocialClientApi.SocialClient) { + return requestClient.put('/system/social-client/update', data); +} + +/** 删除社交客户端 */ +export function deleteSocialClient(id: number) { + return requestClient.delete(`/system/social-client/delete?id=${id}`); +} + +/** 批量删除社交客户端 */ +export function deleteSocialClientList(ids: number[]) { + return requestClient.delete( + `/system/social-client/delete-list?ids=${ids.join(',')}`, + ); +} diff --git a/apps/web-naive/src/api/system/tenant-package/index.ts b/apps/web-naive/src/api/system/tenant-package/index.ts new file mode 100644 index 000000000..53be78f73 --- /dev/null +++ b/apps/web-naive/src/api/system/tenant-package/index.ts @@ -0,0 +1,64 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemTenantPackageApi { + /** 租户套餐信息 */ + export interface TenantPackage { + id: number; + name: string; + status: number; + remark: string; + creator: string; + updater: string; + updateTime: string; + menuIds: number[]; + createTime: Date; + } +} + +/** 租户套餐列表 */ +export function getTenantPackagePage(params: PageParam) { + return requestClient.get>( + '/system/tenant-package/page', + { params }, + ); +} + +/** 查询租户套餐详情 */ +export function getTenantPackage(id: number) { + return requestClient.get(`/system/tenant-package/get?id=${id}`); +} + +/** 新增租户套餐 */ +export function createTenantPackage( + data: SystemTenantPackageApi.TenantPackage, +) { + return requestClient.post('/system/tenant-package/create', data); +} + +/** 修改租户套餐 */ +export function updateTenantPackage( + data: SystemTenantPackageApi.TenantPackage, +) { + return requestClient.put('/system/tenant-package/update', data); +} + +/** 删除租户套餐 */ +export function deleteTenantPackage(id: number) { + return requestClient.delete(`/system/tenant-package/delete?id=${id}`); +} + +/** 批量删除租户套餐 */ +export function deleteTenantPackageList(ids: number[]) { + return requestClient.delete( + `/system/tenant-package/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 获取租户套餐精简信息列表 */ +export function getTenantPackageList() { + return requestClient.get( + '/system/tenant-package/get-simple-list', + ); +} diff --git a/apps/web-naive/src/api/system/tenant/index.ts b/apps/web-naive/src/api/system/tenant/index.ts new file mode 100644 index 000000000..6ee52e1d6 --- /dev/null +++ b/apps/web-naive/src/api/system/tenant/index.ts @@ -0,0 +1,76 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemTenantApi { + /** 租户信息 */ + export interface Tenant { + id?: number; + name: string; + packageId: number; + contactName: string; + contactMobile: string; + accountCount: number; + expireTime: Date; + websites: string[]; + status: number; + } +} + +/** 租户列表 */ +export function getTenantPage(params: PageParam) { + return requestClient.get>( + '/system/tenant/page', + { params }, + ); +} + +/** 获取租户精简信息列表 */ +export function getSimpleTenantList() { + return requestClient.get( + '/system/tenant/simple-list', + ); +} + +/** 查询租户详情 */ +export function getTenant(id: number) { + return requestClient.get( + `/system/tenant/get?id=${id}`, + ); +} + +/** 获取租户精简信息列表 */ +export function getTenantList() { + return requestClient.get( + '/system/tenant/simple-list', + ); +} + +/** 新增租户 */ +export function createTenant(data: SystemTenantApi.Tenant) { + return requestClient.post('/system/tenant/create', data); +} + +/** 修改租户 */ +export function updateTenant(data: SystemTenantApi.Tenant) { + return requestClient.put('/system/tenant/update', data); +} + +/** 删除租户 */ +export function deleteTenant(id: number) { + return requestClient.delete(`/system/tenant/delete?id=${id}`); +} + +/** 批量删除租户 */ +export function deleteTenantList(ids: number[]) { + return requestClient.delete( + `/system/tenant/delete-list?ids=${ids.join(',')}`, + ); +} + +/** 导出租户 */ +export function exportTenant(params: any) { + return requestClient.download('/system/tenant/export-excel', { + params, + }); +} diff --git a/apps/web-naive/src/api/system/user/index.ts b/apps/web-naive/src/api/system/user/index.ts new file mode 100644 index 000000000..c9f23a403 --- /dev/null +++ b/apps/web-naive/src/api/system/user/index.ts @@ -0,0 +1,88 @@ +import type { PageParam, PageResult } from '@vben/request'; + +import { requestClient } from '#/api/request'; + +export namespace SystemUserApi { + /** 用户信息 */ + export interface User { + id?: number; + username: string; + nickname: string; + deptId: number; + postIds: string[]; + email: string; + mobile: string; + sex: number; + avatar: string; + loginIp: string; + status: number; + remark: string; + createTime?: Date; + } +} + +/** 查询用户管理列表 */ +export function getUserPage(params: PageParam) { + return requestClient.get>( + '/system/user/page', + { params }, + ); +} + +/** 查询用户详情 */ +export function getUser(id: number) { + return requestClient.get(`/system/user/get?id=${id}`); +} + +/** 新增用户 */ +export function createUser(data: SystemUserApi.User) { + return requestClient.post('/system/user/create', data); +} + +/** 修改用户 */ +export function updateUser(data: SystemUserApi.User) { + return requestClient.put('/system/user/update', data); +} + +/** 删除用户 */ +export function deleteUser(id: number) { + return requestClient.delete(`/system/user/delete?id=${id}`); +} + +/** 批量删除用户 */ +export function deleteUserList(ids: number[]) { + return requestClient.delete(`/system/user/delete-list?ids=${ids.join(',')}`); +} + +/** 导出用户 */ +export function exportUser(params: any) { + return requestClient.download('/system/user/export-excel', { params }); +} + +/** 下载用户导入模板 */ +export function importUserTemplate() { + return requestClient.download('/system/user/get-import-template'); +} + +/** 导入用户 */ +export function importUser(file: File, updateSupport: boolean) { + return requestClient.upload('/system/user/import', { + file, + updateSupport, + }); +} + +/** 用户密码重置 */ +export function resetUserPassword(id: number, password: string) { + return requestClient.put('/system/user/update-password', { id, password }); +} + +/** 用户状态修改 */ +export function updateUserStatus(id: number, status: number) { + return requestClient.put('/system/user/update-status', { id, status }); +} + +/** 获取用户精简信息列表 */ +export function getSimpleUserList() { + return requestClient.get('/system/user/simple-list'); +} diff --git a/apps/web-naive/src/components/cron-tab/cron-tab.vue b/apps/web-naive/src/components/cron-tab/cron-tab.vue new file mode 100644 index 000000000..9125ba184 --- /dev/null +++ b/apps/web-naive/src/components/cron-tab/cron-tab.vue @@ -0,0 +1,955 @@ + + + + + diff --git a/apps/web-naive/src/components/cron-tab/index.ts b/apps/web-naive/src/components/cron-tab/index.ts new file mode 100644 index 000000000..8f4baae59 --- /dev/null +++ b/apps/web-naive/src/components/cron-tab/index.ts @@ -0,0 +1 @@ +export { default as CronTab } from './cron-tab.vue'; diff --git a/apps/web-naive/src/components/cron-tab/types.ts b/apps/web-naive/src/components/cron-tab/types.ts new file mode 100644 index 000000000..2adf942b7 --- /dev/null +++ b/apps/web-naive/src/components/cron-tab/types.ts @@ -0,0 +1,266 @@ +export interface ShortcutsType { + text: string; + value: string; +} + +export interface CronRange { + start: number | string | undefined; + end: number | string | undefined; +} + +export interface CronLoop { + start: number | string | undefined; + end: number | string | undefined; +} + +export interface CronItem { + type: string; + range: CronRange; + loop: CronLoop; + appoint: string[]; + last?: string; +} + +export interface CronValue { + second: CronItem; + minute: CronItem; + hour: CronItem; + day: CronItem; + month: CronItem; + week: CronItem & { last: string }; + year: CronItem; +} + +export interface WeekOption { + value: string; + label: string; +} + +export interface CronData { + second: string[]; + minute: string[]; + hour: string[]; + day: string[]; + month: string[]; + week: WeekOption[]; + year: number[]; +} + +const getYear = (): number[] => { + const v: number[] = []; + const y = new Date().getFullYear(); + for (let i = 0; i < 11; i++) { + v.push(y + i); + } + return v; +}; + +export const CronValueDefault: CronValue = { + second: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + minute: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + hour: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 0, + end: 1, + }, + appoint: [], + }, + day: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 1, + end: 1, + }, + appoint: [], + }, + month: { + type: '0', + range: { + start: 1, + end: 2, + }, + loop: { + start: 1, + end: 1, + }, + appoint: [], + }, + week: { + type: '5', + range: { + start: '2', + end: '3', + }, + loop: { + start: 0, + end: '2', + }, + last: '2', + appoint: [], + }, + year: { + type: '-1', + range: { + start: getYear()[0], + end: getYear()[1], + }, + loop: { + start: getYear()[0], + end: 1, + }, + appoint: [], + }, +}; + +export const CronDataDefault: CronData = { + second: [ + '0', + '5', + '15', + '20', + '25', + '30', + '35', + '40', + '45', + '50', + '55', + '59', + ], + minute: [ + '0', + '5', + '15', + '20', + '25', + '30', + '35', + '40', + '45', + '50', + '55', + '59', + ], + hour: [ + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '20', + '21', + '22', + '23', + ], + day: [ + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '10', + '11', + '12', + '13', + '14', + '15', + '16', + '17', + '18', + '19', + '20', + '21', + '22', + '23', + '24', + '25', + '26', + '27', + '28', + '29', + '30', + '31', + ], + month: ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'], + week: [ + { + value: '1', + label: '周日', + }, + { + value: '2', + label: '周一', + }, + { + value: '3', + label: '周二', + }, + { + value: '4', + label: '周三', + }, + { + value: '5', + label: '周四', + }, + { + value: '6', + label: '周五', + }, + { + value: '7', + label: '周六', + }, + ], + year: getYear(), +}; diff --git a/apps/web-naive/src/components/description/description.vue b/apps/web-naive/src/components/description/description.vue new file mode 100644 index 000000000..51f0df4d3 --- /dev/null +++ b/apps/web-naive/src/components/description/description.vue @@ -0,0 +1,198 @@ + diff --git a/apps/web-naive/src/components/description/index.ts b/apps/web-naive/src/components/description/index.ts new file mode 100644 index 000000000..a707c4865 --- /dev/null +++ b/apps/web-naive/src/components/description/index.ts @@ -0,0 +1,3 @@ +export { default as Description } from './description.vue'; +export * from './typing'; +export { useDescription } from './use-description'; diff --git a/apps/web-naive/src/components/description/typing.ts b/apps/web-naive/src/components/description/typing.ts new file mode 100644 index 000000000..9ee489678 --- /dev/null +++ b/apps/web-naive/src/components/description/typing.ts @@ -0,0 +1,41 @@ +import type { DescriptionsProps as NDescriptionsProps } from 'naive-ui'; +import type { JSX } from 'vue/jsx-runtime'; + +import type { CSSProperties, VNode } from 'vue'; + +import type { Recordable } from '@vben/types'; + +export interface DescriptionItemSchema { + labelMinWidth?: number; + contentMinWidth?: number; + // 自定义标签样式 + labelStyle?: CSSProperties; + // 对应 data 中的字段名 + field: string; + // 内容的描述 + label: JSX.Element | string | VNode; + // 包含列的数量 + span?: number; + // 是否显示 + show?: (...arg: any) => boolean; + // 插槽名称 + slot?: string; + // 自定义需要展示的内容 + render?: ( + val: any, + data?: Recordable, + ) => Element | JSX.Element | number | string | undefined | VNode; +} + +export interface DescriptionProps extends NDescriptionsProps { + // 是否包含卡片组件 + useCard?: boolean; + // 描述项配置 + schema: DescriptionItemSchema[]; + // 数据 + data: Recordable; +} + +export interface DescInstance { + setDescProps(descProps: Partial): void; +} diff --git a/apps/web-naive/src/components/description/use-description.ts b/apps/web-naive/src/components/description/use-description.ts new file mode 100644 index 000000000..fd24920f0 --- /dev/null +++ b/apps/web-naive/src/components/description/use-description.ts @@ -0,0 +1,31 @@ +import type { Component } from 'vue'; + +import type { DescInstance, DescriptionProps } from './typing'; + +import { h, reactive } from 'vue'; + +import Description from './description.vue'; + +export function useDescription(options?: Partial) { + const propsState = reactive>(options || {}); + + const api: DescInstance = { + setDescProps: (descProps: Partial): void => { + Object.assign(propsState, descProps); + }, + }; + + // 创建一个包装组件,将 propsState 合并到 props 中 + const DescriptionWrapper: Component = { + name: 'UseDescription', + inheritAttrs: false, + setup(_props, { attrs, slots }) { + return () => { + // @ts-ignore - 避免类型实例化过深 + return h(Description, { ...propsState, ...attrs }, slots); + }; + }, + }; + + return [DescriptionWrapper, api] as const; +} diff --git a/apps/web-naive/src/components/dict-tag/dict-tag.vue b/apps/web-naive/src/components/dict-tag/dict-tag.vue new file mode 100644 index 000000000..876816124 --- /dev/null +++ b/apps/web-naive/src/components/dict-tag/dict-tag.vue @@ -0,0 +1,83 @@ + + + diff --git a/apps/web-naive/src/components/table-action/icons.ts b/apps/web-naive/src/components/table-action/icons.ts new file mode 100644 index 000000000..88cda5978 --- /dev/null +++ b/apps/web-naive/src/components/table-action/icons.ts @@ -0,0 +1,13 @@ +export const ACTION_ICON = { + DOWNLOAD: 'lucide:download', + UPLOAD: 'lucide:upload', + ADD: 'lucide:plus', + EDIT: 'lucide:edit', + DELETE: 'lucide:trash-2', + REFRESH: 'lucide:refresh-cw', + SEARCH: 'lucide:search', + FILTER: 'lucide:filter', + MORE: 'lucide:ellipsis-vertical', + VIEW: 'lucide:eye', + COPY: 'lucide:copy', +}; diff --git a/apps/web-naive/src/components/table-action/index.ts b/apps/web-naive/src/components/table-action/index.ts new file mode 100644 index 000000000..672c0a533 --- /dev/null +++ b/apps/web-naive/src/components/table-action/index.ts @@ -0,0 +1,4 @@ +export * from './icons'; + +export { default as TableAction } from './table-action.vue'; +export * from './typing'; diff --git a/apps/web-naive/src/components/table-action/table-action.vue b/apps/web-naive/src/components/table-action/table-action.vue new file mode 100644 index 000000000..50fd7f87f --- /dev/null +++ b/apps/web-naive/src/components/table-action/table-action.vue @@ -0,0 +1,221 @@ + + + + diff --git a/apps/web-naive/src/components/table-action/typing.ts b/apps/web-naive/src/components/table-action/typing.ts new file mode 100644 index 000000000..ffe4761bc --- /dev/null +++ b/apps/web-naive/src/components/table-action/typing.ts @@ -0,0 +1,28 @@ +import type { ButtonProps } from 'naive-ui/es/button/src/Button'; +import type { TooltipProps } from 'naive-ui/es/tooltip/src/Tooltip'; + +export interface PopConfirm { + title: string; + okText?: string; + cancelText?: string; + confirm: () => void; + cancel?: () => void; + icon?: string; + disabled?: boolean; +} + +export interface ActionItem extends ButtonProps { + onClick?: () => void; + type?: ButtonProps['type']; + label?: string; + color?: 'error' | 'success' | 'warning'; + icon?: string; + popConfirm?: PopConfirm; + disabled?: boolean; + divider?: boolean; + // 权限编码控制是否显示 + auth?: string[]; + // 业务控制是否显示 + ifShow?: ((action: ActionItem) => boolean) | boolean; + tooltip?: string | TooltipProps; +} diff --git a/apps/web-naive/src/components/upload/README.md b/apps/web-naive/src/components/upload/README.md new file mode 100644 index 000000000..7a64106c6 --- /dev/null +++ b/apps/web-naive/src/components/upload/README.md @@ -0,0 +1,260 @@ +# Upload Components - Naive UI 版本 + +本目录包含已重构为 Naive UI 的上传组件。 + +## 组件列表 + +### 1. ImageUpload - 图片上传组件 + +- **文件**: `image-upload.vue` +- **功能**: 专门用于图片上传的组件,支持图片预览 +- **特性**: + - 支持单图/多图上传 + - 支持图片预览(使用 NModal + NImage) + - 支持拖拽上传 + - 自动校验文件类型和大小 + - 支持自定义上传 API + - 支持进度显示 + +### 2. FileUpload - 文件上传组件 + +- **文件**: `file-upload.vue` +- **功能**: 通用文件上传组件 +- **特性**: + - 支持单文件/多文件上传 + - 支持拖拽上传区域 + - 支持文件预览和下载 + - 自动校验文件类型和大小 + - 支持自定义上传 API + - 支持进度显示 + - 支持返回文本内容(用于配置文件等) + +### 3. InputUpload - 输入框上传组件 + +- **文件**: `input-upload.vue` +- **功能**: 结合输入框和文件上传的组件 +- **特性**: + - 支持文本输入框或文本域 + - 支持通过上传文件自动填充内容 + - 使用 NGrid 布局,响应式设计 + +## 使用示例 + +### ImageUpload 图片上传 + +```vue + + + +``` + +### FileUpload 文件上传 + +```vue + + + +``` + +### InputUpload 输入框上传 + +```vue + + + +``` + +## Props 说明 + +### 通用 Props (FileUploadProps) + +| 属性 | 类型 | 默认值 | 说明 | +| ---------------- | -------------------- | ------- | ------------------ | +| modelValue/value | `string \| string[]` | - | v-model 绑定值 | +| accept | `string[]` | `[]` | 接受的文件类型 | +| maxSize | `number` | `2` | 文件最大大小(MB) | +| maxNumber | `number` | `1` | 最大文件数量 | +| multiple | `boolean` | `false` | 是否支持多选 | +| disabled | `boolean` | `false` | 是否禁用 | +| drag | `boolean` | `false` | 是否支持拖拽上传 | +| directory | `string` | - | 上传目录 | +| api | `Function` | - | 自定义上传 API | +| showDescription | `boolean` | - | 是否显示描述文本 | + +### ImageUpload 特有 Props + +| 属性 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| listType | `string` | `'picture-card'` | 列表类型 | +| accept | `string[]` | `['jpg', 'jpeg', 'png', 'gif', 'webp']` | 接受的图片类型 | +| showDescription | `boolean` | `true` | 是否显示描述文本 | + +### InputUpload 特有 Props + +| 属性 | 类型 | 默认值 | 说明 | +| --------------- | ----------------------- | --------- | ---------------- | +| inputType | `'input' \| 'textarea'` | `'input'` | 输入框类型 | +| inputProps | `InputProps` | - | 输入框属性 | +| fileUploadProps | `FileUploadProps` | - | 文件上传组件属性 | + +## Events + +| 事件名 | 参数 | 说明 | +| --- | --- | --- | +| update:value | `value: string \| string[]` | 值更新事件 | +| update:modelValue | `value: string \| string[]` | v-model 更新事件 | +| change | `value: string \| string[]` | 值变化事件 | +| delete | `file: UploadFileInfo` | 删除文件事件 | +| preview | `file: UploadFileInfo` | 预览文件事件(仅 FileUpload) | +| returnText | `text: string` | 返回文件文本内容(仅 FileUpload) | + +## 辅助工具 + +### useUpload + +- **文件**: `use-upload.ts` +- **功能**: 提供上传相关的工具函数 +- **主要方法**: + - `httpRequest`: 统一的文件上传请求方法 + - `getUploadUrl`: 获取上传 URL + +### useUploadType + +- **功能**: 处理上传类型相关的逻辑 +- **主要方法**: + - `getStringAccept`: 获取 accept 字符串 + - `getHelpText`: 获取帮助文本 + +## 技术栈 + +- **UI 框架**: Naive UI +- **核心组件**: + - NUpload + - NImage + - NImageGroup + - NModal + - NButton + - NGrid + - NInput +- **工具库**: + - @vueuse/core + - @vben/utils + +## 注意事项 + +1. 文件状态使用 Naive UI 的状态值:`'pending' | 'uploading' | 'finished' | 'error' | 'removed'` +2. 所有文件 ID 使用 Naive UI 的 `id` 字段,而不是 `uid` +3. 上传前会自动校验文件类型和大小 +4. 支持两种上传模式: + - 客户端直接上传(S3) + - 通过后端上传 +5. 支持自定义上传 API,如果不提供则使用默认的上传接口 + +## 迁移指南 + +从 Ant Design Vue 迁移到 Naive UI 的主要变化: + +1. **组件导入**: + + ```typescript + // 旧 + import { Upload } from 'ant-design-vue'; + + // 新 + import { NUpload } from 'naive-ui'; + ``` + +2. **文件列表类型**: + + ```typescript + // 旧 + import type { UploadFile } from 'ant-design-vue'; + + // 新 + import type { UploadFileInfo } from 'naive-ui'; + ``` + +3. **状态值**: + + ```typescript + // 旧 + status: 'done'; + + // 新 + status: 'finished'; + ``` + +4. **事件回调**: + + ```typescript + // 旧 + @remove="handleRemove" + function handleRemove(file: UploadFile) { } + + // 新 + @remove="handleRemove" + function handleRemove(options: { file: UploadFileInfo; fileList: UploadFileInfo[] }) { } + ``` + +5. **自定义上传**: + + ```typescript + // 旧 + customRequest(info: UploadRequestOption) { + info.onSuccess!(res); + } + + // 新 + customRequest(options: UploadCustomRequestOptions) { + options.onFinish(); + } + ``` + +## 更新日志 + +### v1.0.0 (2025-01-16) + +- ✅ 将所有上传组件从 Ant Design Vue 重构为 Naive UI +- ✅ 保持原有功能和 API 兼容性 +- ✅ 优化代码结构和类型定义 +- ✅ 修复所有 linter 错误 +- ✅ 添加完整的文档说明 diff --git a/apps/web-naive/src/components/upload/file-upload.vue b/apps/web-naive/src/components/upload/file-upload.vue new file mode 100644 index 000000000..c16f48c7c --- /dev/null +++ b/apps/web-naive/src/components/upload/file-upload.vue @@ -0,0 +1,345 @@ + + + + + diff --git a/apps/web-naive/src/components/upload/image-upload.vue b/apps/web-naive/src/components/upload/image-upload.vue new file mode 100644 index 000000000..fb38c9dab --- /dev/null +++ b/apps/web-naive/src/components/upload/image-upload.vue @@ -0,0 +1,341 @@ + + + + + diff --git a/apps/web-naive/src/components/upload/index.ts b/apps/web-naive/src/components/upload/index.ts new file mode 100644 index 000000000..14e57fede --- /dev/null +++ b/apps/web-naive/src/components/upload/index.ts @@ -0,0 +1,3 @@ +export { default as FileUpload } from './file-upload.vue'; +export { default as ImageUpload } from './image-upload.vue'; +export { default as InputUpload } from './input-upload.vue'; diff --git a/apps/web-naive/src/components/upload/input-upload.vue b/apps/web-naive/src/components/upload/input-upload.vue new file mode 100644 index 000000000..9cfb03456 --- /dev/null +++ b/apps/web-naive/src/components/upload/input-upload.vue @@ -0,0 +1,81 @@ + + + diff --git a/apps/web-naive/src/components/upload/typing.ts b/apps/web-naive/src/components/upload/typing.ts new file mode 100644 index 000000000..ada73d244 --- /dev/null +++ b/apps/web-naive/src/components/upload/typing.ts @@ -0,0 +1,39 @@ +import type { AxiosResponse } from '@vben/request'; + +import type { AxiosProgressEvent } from '#/api/infra/file'; + +export enum UploadResultStatus { + DONE = 'done', + ERROR = 'error', + SUCCESS = 'success', + UPLOADING = 'uploading', +} + +export type UploadListType = 'picture' | 'picture-card' | 'text'; + +export interface FileUploadProps { + // 根据后缀,或者其他 + accept?: string[]; + api?: ( + file: File, + onUploadProgress?: AxiosProgressEvent, + ) => Promise>; + // 上传的目录 + directory?: string; + disabled?: boolean; + drag?: boolean; // 是否支持拖拽上传 + helpText?: string; + listType?: UploadListType; + // 最大数量的文件,Infinity不限制 + maxNumber?: number; + modelValue?: string | string[]; // v-model 支持 + // 文件最大多少MB + maxSize?: number; + // 是否支持多选 + multiple?: boolean; + // support xxx.xxx.xx + resultField?: string; + // 是否显示下面的描述 + showDescription?: boolean; + value?: string | string[]; +} diff --git a/apps/web-naive/src/components/upload/use-upload.ts b/apps/web-naive/src/components/upload/use-upload.ts new file mode 100644 index 000000000..2f46f9e98 --- /dev/null +++ b/apps/web-naive/src/components/upload/use-upload.ts @@ -0,0 +1,168 @@ +import type { Ref } from 'vue'; + +import type { AxiosProgressEvent, InfraFileApi } from '#/api/infra/file'; + +import { computed, unref } from 'vue'; + +import { useAppConfig } from '@vben/hooks'; +import { $t } from '@vben/locales'; + +import { createFile, getFilePresignedUrl, uploadFile } from '#/api/infra/file'; +import { baseRequestClient } from '#/api/request'; + +const { apiURL } = useAppConfig(import.meta.env, import.meta.env.PROD); + +/** + * 上传类型 + */ +enum UPLOAD_TYPE { + // 客户端直接上传(只支持S3服务) + CLIENT = 'client', + // 客户端发送到后端上传 + SERVER = 'server', +} + +export function useUploadType({ + acceptRef, + helpTextRef, + maxNumberRef, + maxSizeRef, +}: { + acceptRef: Ref; + helpTextRef: Ref; + maxNumberRef: Ref; + maxSizeRef: Ref; +}) { + // 文件类型限制 + const getAccept = computed(() => { + const accept = unref(acceptRef); + if (accept && accept.length > 0) { + return accept; + } + return []; + }); + const getStringAccept = computed(() => { + return unref(getAccept) + .map((item) => { + return item.indexOf('/') > 0 || item.startsWith('.') + ? item + : `.${item}`; + }) + .join(','); + }); + + // 支持jpg、jpeg、png格式,不超过2M,最多可选择10张图片,。 + const getHelpText = computed(() => { + const helpText = unref(helpTextRef); + if (helpText) { + return helpText; + } + const helpTexts: string[] = []; + + const accept = unref(acceptRef); + if (accept.length > 0) { + helpTexts.push($t('ui.upload.accept', [accept.join(',')])); + } + + const maxSize = unref(maxSizeRef); + if (maxSize) { + helpTexts.push($t('ui.upload.maxSize', [maxSize])); + } + + const maxNumber = unref(maxNumberRef); + if (maxNumber && maxNumber !== Infinity) { + helpTexts.push($t('ui.upload.maxNumber', [maxNumber])); + } + return helpTexts.join(','); + }); + return { getAccept, getStringAccept, getHelpText }; +} + +// TODO @芋艿:目前保持和 admin-vue3 一致,后续可能重构 +export function useUpload(directory?: string) { + // 后端上传地址 + const uploadUrl = getUploadUrl(); + // 是否使用前端直连上传 + const isClientUpload = + UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE; + // 重写ElUpload上传方法 + async function httpRequest( + file: File, + onUploadProgress?: AxiosProgressEvent, + ) { + // 模式一:前端上传 + if (isClientUpload) { + // 1.1 生成文件名称 + const fileName = await generateFileName(file); + // 1.2 获取文件预签名地址 + const presignedInfo = await getFilePresignedUrl(fileName, directory); + // 1.3 上传文件 + return baseRequestClient + .put(presignedInfo.uploadUrl, file, { + headers: { + 'Content-Type': file.type, + }, + }) + .then(() => { + // 1.4. 记录文件信息到后端(异步) + createFile0(presignedInfo, file); + // 通知成功,数据格式保持与后端上传的返回结果一致 + return { url: presignedInfo.url }; + }); + } else { + // 模式二:后端上传 + return uploadFile({ file, directory }, onUploadProgress); + } + } + + return { + uploadUrl, + httpRequest, + }; +} + +/** + * 获得上传 URL + */ +export function getUploadUrl(): string { + return `${apiURL}/infra/file/upload`; +} + +/** + * 创建文件信息 + * + * @param vo 文件预签名信息 + * @param file 文件 + */ +function createFile0( + vo: InfraFileApi.FilePresignedUrlRespVO, + file: File, +): InfraFileApi.File { + const fileVO = { + configId: vo.configId, + url: vo.url, + path: vo.path, + name: file.name, + type: file.type, + size: file.size, + }; + createFile(fileVO); + return fileVO; +} + +/** + * 生成文件名称(使用算法SHA256) + * + * @param file 要上传的文件 + */ +async function generateFileName(file: File) { + // // 读取文件内容 + // const data = await file.arrayBuffer(); + // const wordArray = CryptoJS.lib.WordArray.create(data); + // // 计算SHA256 + // const sha256 = CryptoJS.SHA256(wordArray).toString(); + // // 拼接后缀 + // const ext = file.name.slice(Math.max(0, file.name.lastIndexOf('.'))); + // return `${sha256}${ext}`; + return file.name; +} diff --git a/apps/web-naive/src/layouts/auth.vue b/apps/web-naive/src/layouts/auth.vue new file mode 100644 index 000000000..8ba66e85a --- /dev/null +++ b/apps/web-naive/src/layouts/auth.vue @@ -0,0 +1,25 @@ + + + diff --git a/apps/web-naive/src/layouts/basic.vue b/apps/web-naive/src/layouts/basic.vue new file mode 100644 index 000000000..256868f4a --- /dev/null +++ b/apps/web-naive/src/layouts/basic.vue @@ -0,0 +1,271 @@ + + + diff --git a/apps/web-naive/src/locales/index.ts b/apps/web-naive/src/locales/index.ts new file mode 100644 index 000000000..d994fb8a3 --- /dev/null +++ b/apps/web-naive/src/locales/index.ts @@ -0,0 +1,78 @@ +import type { App } from 'vue'; + +import type { LocaleSetupOptions, SupportedLanguagesType } from '@vben/locales'; + +import { + $t, + setupI18n as coreSetup, + loadLocalesMapFromDir, +} from '@vben/locales'; +import { preferences } from '@vben/preferences'; + +import dayjs from 'dayjs'; + +const modules = import.meta.glob('./langs/**/*.json'); + +const localesMap = loadLocalesMapFromDir( + /\.\/langs\/([^/]+)\/(.*)\.json$/, + modules, +); + +/** + * 加载应用特有的语言包 + * 这里也可以改造为从服务端获取翻译数据 + * @param lang + */ +async function loadMessages(lang: SupportedLanguagesType) { + const [appLocaleMessages] = await Promise.all([ + localesMap[lang]?.(), + loadThirdPartyMessage(lang), + ]); + return appLocaleMessages?.default; +} + +/** + * 加载第三方组件库的语言包 + * @param lang + */ +async function loadThirdPartyMessage(lang: SupportedLanguagesType) { + await loadDayjsLocale(lang); +} + +/** + * 加载dayjs的语言包 + * @param lang + */ +async function loadDayjsLocale(lang: SupportedLanguagesType) { + let locale; + switch (lang) { + case 'en-US': { + locale = await import('dayjs/locale/en'); + break; + } + case 'zh-CN': { + locale = await import('dayjs/locale/zh-cn'); + break; + } + // 默认使用英语 + default: { + locale = await import('dayjs/locale/en'); + } + } + if (locale) { + dayjs.locale(locale); + } else { + console.error(`Failed to load dayjs locale for ${lang}`); + } +} + +async function setupI18n(app: App, options: LocaleSetupOptions = {}) { + await coreSetup(app, { + defaultLocale: preferences.app.locale, + loadMessages, + missingWarn: !import.meta.env.PROD, + ...options, + }); +} + +export { $t, setupI18n }; diff --git a/apps/web-naive/src/locales/langs/en-US/page.json b/apps/web-naive/src/locales/langs/en-US/page.json new file mode 100644 index 000000000..130cb88ac --- /dev/null +++ b/apps/web-naive/src/locales/langs/en-US/page.json @@ -0,0 +1,34 @@ +{ + "auth": { + "login": "Login", + "register": "Register", + "codeLogin": "Code Login", + "qrcodeLogin": "Qr Code Login", + "forgetPassword": "Forget Password", + "profile": "Profile" + }, + "dashboard": { + "title": "Dashboard", + "analytics": "Analytics", + "workspace": "Workspace" + }, + "action": { + "action": "Action", + "add": "Add", + "edit": "Edit", + "delete": "Delete", + "save": "Save", + "import": "Import", + "export": "Export", + "submit": "Submit", + "cancel": "Cancel", + "confirm": "Confirm", + "reset": "Reset", + "search": "Search", + "more": "More" + }, + "tenant": { + "placeholder": "Please select tenant", + "success": "Switch tenant success" + } +} diff --git a/apps/web-naive/src/locales/langs/zh-CN/page.json b/apps/web-naive/src/locales/langs/zh-CN/page.json new file mode 100644 index 000000000..d2af3b1eb --- /dev/null +++ b/apps/web-naive/src/locales/langs/zh-CN/page.json @@ -0,0 +1,34 @@ +{ + "auth": { + "login": "登录", + "register": "注册", + "codeLogin": "验证码登录", + "qrcodeLogin": "二维码登录", + "forgetPassword": "忘记密码", + "profile": "个人中心" + }, + "dashboard": { + "title": "概览", + "analytics": "分析页", + "workspace": "工作台" + }, + "action": { + "action": "操作", + "add": "新增", + "edit": "编辑", + "delete": "删除", + "save": "保存", + "import": "导入", + "export": "导出", + "submit": "提交", + "cancel": "取消", + "confirm": "确认", + "reset": "重置", + "search": "搜索", + "more": "更多" + }, + "tenant": { + "placeholder": "请选择租户", + "success": "切换租户成功" + } +} diff --git a/apps/web-naive/src/router/guard.ts b/apps/web-naive/src/router/guard.ts new file mode 100644 index 000000000..fa1119e8e --- /dev/null +++ b/apps/web-naive/src/router/guard.ts @@ -0,0 +1,153 @@ +import type { Router } from 'vue-router'; + +import { LOGIN_PATH } from '@vben/constants'; +import { $t } from '@vben/locales'; +import { preferences } from '@vben/preferences'; +import { useAccessStore, useDictStore, useUserStore } from '@vben/stores'; +import { startProgress, stopProgress } from '@vben/utils'; + +import { message } from '#/adapter/naive'; +import { getSimpleDictDataList } from '#/api/system/dict/data'; +import { accessRoutes, coreRouteNames } from '#/router/routes'; +import { useAuthStore } from '#/store'; + +import { generateAccess } from './access'; + +/** + * 通用守卫配置 + * @param router + */ +function setupCommonGuard(router: Router) { + // 记录已经加载的页面 + const loadedPaths = new Set(); + + router.beforeEach((to) => { + to.meta.loaded = loadedPaths.has(to.path); + + // 页面加载进度条 + if (!to.meta.loaded && preferences.transition.progress) { + startProgress(); + } + return true; + }); + + router.afterEach((to) => { + // 记录页面是否加载,如果已经加载,后续的页面切换动画等效果不在重复执行 + + loadedPaths.add(to.path); + + // 关闭页面加载进度条 + if (preferences.transition.progress) { + stopProgress(); + } + }); +} + +/** + * 权限访问守卫配置 + * @param router + */ +function setupAccessGuard(router: Router) { + router.beforeEach(async (to, from) => { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const authStore = useAuthStore(); + const dictStore = useDictStore(); + + // 基本路由,这些路由不需要进入权限拦截 + if (coreRouteNames.includes(to.name as string)) { + if (to.path === LOGIN_PATH && accessStore.accessToken) { + return decodeURIComponent( + (to.query?.redirect as string) || + userStore.userInfo?.homePath || + preferences.app.defaultHomePath, + ); + } + return true; + } + + // accessToken 检查 + if (!accessStore.accessToken) { + // 明确声明忽略权限访问权限,则可以访问 + if (to.meta.ignoreAccess) { + return true; + } + + // 没有访问权限,跳转登录页面 + if (to.fullPath !== LOGIN_PATH) { + return { + path: LOGIN_PATH, + // 如不需要,直接删除 query + query: + to.fullPath === preferences.app.defaultHomePath + ? {} + : { redirect: encodeURIComponent(to.fullPath) }, + // 携带当前跳转的页面,登录后重新跳转该页面 + replace: true, + }; + } + return to; + } + + // 是否已经生成过动态路由 + if (accessStore.isAccessChecked) { + return true; + } + + // 加载字典数据(不阻塞加载) + dictStore.setDictCacheByApi(getSimpleDictDataList); + + // 生成路由表 + // 当前登录用户拥有的角色标识列表 + let userInfo = userStore.userInfo; + if (!userInfo) { + // add by 芋艿:由于 yudao 是 fetchUserInfo 统一加载用户 + 权限信息,所以将 fetchMenuListAsync + const loading = message.loading(`${$t('common.loadingMenu')}...`); + try { + const authPermissionInfo = await authStore.fetchUserInfo(); + if (authPermissionInfo) { + userInfo = authPermissionInfo.user; + } + } finally { + loading.destroy(); + } + } + const userRoles = userStore.userRoles ?? []; + + // 生成菜单和路由 + const { accessibleMenus, accessibleRoutes } = await generateAccess({ + roles: userRoles, + router, + // 则会在菜单中显示,但是访问会被重定向到403 + routes: accessRoutes, + }); + + // 保存菜单信息和路由信息 + accessStore.setAccessMenus(accessibleMenus); + accessStore.setAccessRoutes(accessibleRoutes); + accessStore.setIsAccessChecked(true); + userStore.setUserRoles(userRoles); + const redirectPath = (from.query.redirect ?? + (to.path === preferences.app.defaultHomePath + ? userInfo?.homePath || preferences.app.defaultHomePath + : to.fullPath)) as string; + + return { + ...router.resolve(decodeURIComponent(redirectPath)), + replace: true, + }; + }); +} + +/** + * 项目守卫配置 + * @param router + */ +function createRouterGuard(router: Router) { + /** 通用 */ + setupCommonGuard(router); + /** 权限访问 */ + setupAccessGuard(router); +} + +export { createRouterGuard }; diff --git a/apps/web-naive/src/router/routes/modules/dashboard.ts b/apps/web-naive/src/router/routes/modules/dashboard.ts new file mode 100644 index 000000000..5254dc65d --- /dev/null +++ b/apps/web-naive/src/router/routes/modules/dashboard.ts @@ -0,0 +1,38 @@ +import type { RouteRecordRaw } from 'vue-router'; + +import { $t } from '#/locales'; + +const routes: RouteRecordRaw[] = [ + { + meta: { + icon: 'lucide:layout-dashboard', + order: -1, + title: $t('page.dashboard.title'), + }, + name: 'Dashboard', + path: '/dashboard', + children: [ + { + name: 'Analytics', + path: '/analytics', + component: () => import('#/views/dashboard/analytics/index.vue'), + meta: { + affixTab: true, + icon: 'lucide:area-chart', + title: $t('page.dashboard.analytics'), + }, + }, + { + name: 'Workspace', + path: '/workspace', + component: () => import('#/views/dashboard/workspace/index.vue'), + meta: { + icon: 'carbon:workspace', + title: $t('page.dashboard.workspace'), + }, + }, + ], + }, +]; + +export default routes; diff --git a/apps/web-naive/src/router/routes/modules/infra.ts b/apps/web-naive/src/router/routes/modules/infra.ts new file mode 100644 index 000000000..c99b51bea --- /dev/null +++ b/apps/web-naive/src/router/routes/modules/infra.ts @@ -0,0 +1,30 @@ +import type { RouteRecordRaw } from 'vue-router'; + +const routes: RouteRecordRaw[] = [ + { + path: '/infra/job/log', + component: () => import('#/views/infra/job/logger/index.vue'), + name: 'InfraJobLog', + meta: { + title: '调度日志', + icon: 'ant-design:history-outlined', + activePath: '/infra/job', + keepAlive: false, + hideInMenu: true, + }, + }, + { + path: '/infra/codegen/edit', + component: () => import('#/views/infra/codegen/edit/index.vue'), + name: 'InfraCodegenEdit', + meta: { + title: '生成配置修改', + icon: 'ic:baseline-view-in-ar', + activePath: '/infra/codegen', + keepAlive: true, + hideInMenu: true, + }, + }, +]; + +export default routes; diff --git a/apps/web-naive/src/store/auth.ts b/apps/web-naive/src/store/auth.ts new file mode 100644 index 000000000..561c981ff --- /dev/null +++ b/apps/web-naive/src/store/auth.ts @@ -0,0 +1,157 @@ +import type { AuthPermissionInfo, Recordable, UserInfo } from '@vben/types'; + +import type { AuthApi } from '#/api'; + +import { ref } from 'vue'; +import { useRouter } from 'vue-router'; + +import { LOGIN_PATH } from '@vben/constants'; +import { preferences } from '@vben/preferences'; +import { resetAllStores, useAccessStore, useUserStore } from '@vben/stores'; + +import { defineStore } from 'pinia'; + +import { notification } from '#/adapter/naive'; +import { + getAuthPermissionInfoApi, + loginApi, + logoutApi, + register, + smsLogin, + socialLogin, +} from '#/api'; +import { $t } from '#/locales'; + +export const useAuthStore = defineStore('auth', () => { + const accessStore = useAccessStore(); + const userStore = useUserStore(); + const router = useRouter(); + + const loginLoading = ref(false); + + /** + * 异步处理登录操作 + * Asynchronously handle the login process + * @param type 登录类型 + * @param params 登录表单数据 + * @param onSuccess 登录成功后的回调函数 + */ + async function authLogin( + type: 'mobile' | 'register' | 'social' | 'username', + params: Recordable, + onSuccess?: () => Promise | void, + ) { + // 异步处理用户登录操作并获取 accessToken + let userInfo: null | UserInfo = null; + try { + let loginResult: AuthApi.LoginResult; + loginLoading.value = true; + switch (type) { + case 'mobile': { + loginResult = await smsLogin(params as AuthApi.SmsLoginParams); + break; + } + case 'register': { + loginResult = await register(params as AuthApi.RegisterParams); + break; + } + case 'social': { + loginResult = await socialLogin(params as AuthApi.SocialLoginParams); + break; + } + default: { + loginResult = await loginApi(params); + } + } + const { accessToken, refreshToken } = loginResult; + + // 如果成功获取到 accessToken + if (accessToken) { + accessStore.setAccessToken(accessToken); + accessStore.setRefreshToken(refreshToken); + + // 获取用户信息并存储到 userStore、accessStore 中 + // TODO @芋艿:清理掉 accessCodes 相关的逻辑 + // const [fetchUserInfoResult, accessCodes] = await Promise.all([ + // fetchUserInfo(), + // // getAccessCodesApi(), + // ]); + const fetchUserInfoResult = await fetchUserInfo(); + + userInfo = fetchUserInfoResult.user; + + if (accessStore.loginExpired) { + accessStore.setLoginExpired(false); + } else { + onSuccess + ? await onSuccess?.() + : await router.push( + userInfo.homePath || preferences.app.defaultHomePath, + ); + } + + if (userInfo?.nickname) { + notification.success({ + content: $t('authentication.loginSuccess'), + description: `${$t('authentication.loginSuccessDesc')}:${userInfo?.nickname}`, + duration: 3000, + }); + } + } + } finally { + loginLoading.value = false; + } + + return { + userInfo, + }; + } + + async function logout(redirect: boolean = true) { + try { + const accessToken = accessStore.accessToken as string; + if (accessToken) { + await logoutApi(accessToken); + } + } catch { + // 不做任何处理 + } + resetAllStores(); + accessStore.setLoginExpired(false); + + // 回登录页带上当前路由地址 + await router.replace({ + path: LOGIN_PATH, + query: redirect + ? { + redirect: encodeURIComponent(router.currentRoute.value.fullPath), + } + : {}, + }); + } + + async function fetchUserInfo() { + // 加载 + let authPermissionInfo: AuthPermissionInfo | null = null; + authPermissionInfo = await getAuthPermissionInfoApi(); + // userStore + userStore.setUserInfo(authPermissionInfo.user); + userStore.setUserRoles(authPermissionInfo.roles); + // accessStore + accessStore.setAccessMenus(authPermissionInfo.menus); + accessStore.setAccessCodes(authPermissionInfo.permissions); + return authPermissionInfo; + } + + function $reset() { + loginLoading.value = false; + } + + return { + $reset, + authLogin, + fetchUserInfo, + loginLoading, + logout, + }; +}); diff --git a/apps/web-naive/src/store/index.ts b/apps/web-naive/src/store/index.ts new file mode 100644 index 000000000..269586ee8 --- /dev/null +++ b/apps/web-naive/src/store/index.ts @@ -0,0 +1 @@ +export * from './auth'; diff --git a/apps/web-naive/src/utils/index.ts b/apps/web-naive/src/utils/index.ts new file mode 100644 index 000000000..800284d14 --- /dev/null +++ b/apps/web-naive/src/utils/index.ts @@ -0,0 +1 @@ +export * from './rangePickerProps'; diff --git a/apps/web-naive/src/utils/rangePickerProps.ts b/apps/web-naive/src/utils/rangePickerProps.ts new file mode 100644 index 000000000..a3ffaf324 --- /dev/null +++ b/apps/web-naive/src/utils/rangePickerProps.ts @@ -0,0 +1,52 @@ +import dayjs from 'dayjs'; + +import { $t } from '#/locales'; + +/** 时间段选择器拓展 */ +export function getRangePickerDefaultProps() { + return { + startPlaceholder: $t('utils.rangePicker.beginTime'), + endPlaceholder: $t('utils.rangePicker.endTime'), + type: 'datetimerange', + format: 'YYYY-MM-dd HH:mm:ss', + valueFormat: 'YYYY-MM-dd HH:mm:ss', + defaultTime: ['00:00:00', '23:59:59'], + shortcuts: { + [$t('utils.rangePicker.today')]: () => + [ + dayjs().startOf('day').toDate(), + dayjs().endOf('day').toDate(), + ] as const, + [$t('utils.rangePicker.last7Days')]: () => + [ + dayjs().subtract(7, 'day').startOf('day').toDate(), + dayjs().endOf('day').toDate(), + ] as const, + [$t('utils.rangePicker.last30Days')]: () => + [ + dayjs().subtract(30, 'day').startOf('day').toDate(), + dayjs().endOf('day').toDate(), + ] as const, + [$t('utils.rangePicker.yesterday')]: () => + [ + dayjs().subtract(1, 'day').startOf('day').toDate(), + dayjs().subtract(1, 'day').endOf('day').toDate(), + ] as const, + [$t('utils.rangePicker.thisWeek')]: () => + [ + dayjs().startOf('week').toDate(), + dayjs().endOf('day').toDate(), + ] as const, + [$t('utils.rangePicker.thisMonth')]: () => + [ + dayjs().startOf('month').toDate(), + dayjs().endOf('day').toDate(), + ] as const, + [$t('utils.rangePicker.lastWeek')]: () => + [ + dayjs().subtract(1, 'week').startOf('day').toDate(), + dayjs().endOf('day').toDate(), + ] as const, + }, + }; +} diff --git a/apps/web-naive/src/views/_core/profile/base-setting.vue b/apps/web-naive/src/views/_core/profile/base-setting.vue new file mode 100644 index 000000000..8a113e3e5 --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/base-setting.vue @@ -0,0 +1,101 @@ + + diff --git a/apps/web-naive/src/views/_core/profile/index.vue b/apps/web-naive/src/views/_core/profile/index.vue new file mode 100644 index 000000000..8740894e3 --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/index.vue @@ -0,0 +1,49 @@ + + diff --git a/apps/web-naive/src/views/_core/profile/notification-setting.vue b/apps/web-naive/src/views/_core/profile/notification-setting.vue new file mode 100644 index 000000000..324a4b393 --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/notification-setting.vue @@ -0,0 +1,31 @@ + + diff --git a/apps/web-naive/src/views/_core/profile/password-setting.vue b/apps/web-naive/src/views/_core/profile/password-setting.vue new file mode 100644 index 000000000..1132a51fb --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/password-setting.vue @@ -0,0 +1,66 @@ + + diff --git a/apps/web-naive/src/views/_core/profile/security-setting.vue b/apps/web-naive/src/views/_core/profile/security-setting.vue new file mode 100644 index 000000000..be30db58e --- /dev/null +++ b/apps/web-naive/src/views/_core/profile/security-setting.vue @@ -0,0 +1,43 @@ + + diff --git a/apps/web-naive/src/views/dashboard/analytics/analytics-visits-sales.vue b/apps/web-naive/src/views/dashboard/analytics/analytics-visits-sales.vue new file mode 100644 index 000000000..6ff52086f --- /dev/null +++ b/apps/web-naive/src/views/dashboard/analytics/analytics-visits-sales.vue @@ -0,0 +1,46 @@ + + + diff --git a/apps/web-naive/src/views/infra/apiAccessLog/data.ts b/apps/web-naive/src/views/infra/apiAccessLog/data.ts new file mode 100644 index 000000000..706928895 --- /dev/null +++ b/apps/web-naive/src/views/infra/apiAccessLog/data.ts @@ -0,0 +1,273 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { JsonViewer } from '@vben/common-ui'; +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + clearable: true, + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'applicationName', + label: '应用名', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入应用名', + }, + }, + { + fieldName: 'beginTime', + label: '请求时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'duration', + label: '执行时长', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入执行时长', + }, + }, + { + fieldName: 'resultCode', + label: '结果码', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入结果码', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'applicationName', + title: '应用名', + minWidth: 150, + }, + { + field: 'requestMethod', + title: '请求方法', + minWidth: 80, + }, + { + field: 'requestUrl', + title: '请求地址', + minWidth: 300, + }, + { + field: 'beginTime', + title: '请求时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'duration', + title: '执行时长', + minWidth: 120, + formatter: ({ cellValue }) => `${cellValue} ms`, + }, + { + field: 'resultCode', + title: '操作结果', + minWidth: 150, + formatter: ({ row }) => { + return row.resultCode === 0 ? '成功' : `失败(${row.resultMsg})`; + }, + }, + { + field: 'operateModule', + title: '操作模块', + minWidth: 150, + }, + { + field: 'operateName', + title: '操作名', + minWidth: 220, + }, + { + field: 'operateType', + title: '操作类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_OPERATE_TYPE }, + }, + }, + { + title: '操作', + width: 80, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'traceId', + label: '链路追踪', + }, + { + field: 'applicationName', + label: '应用名', + }, + { + field: 'userId', + label: '用户Id', + }, + { + field: 'userType', + label: '用户类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: val, + }); + }, + }, + { + field: 'userIp', + label: '用户 IP', + }, + { + field: 'userAgent', + label: '用户 UA', + }, + { + field: 'requestMethod', + label: '请求信息', + render: (val, data) => { + if (val && data?.requestUrl) { + return `${val} ${data.requestUrl}`; + } + return ''; + }, + }, + { + field: 'requestParams', + label: '请求参数', + render: (val) => { + if (val) { + return h(JsonViewer, { + value: JSON.parse(val), + previewMode: true, + }); + } + return ''; + }, + }, + { + field: 'responseBody', + label: '请求结果', + }, + { + label: '请求时间', + field: 'beginTime', + render: (val, data) => { + if (val && data?.endTime) { + return `${formatDateTime(val)} ~ ${formatDateTime(data.endTime)}`; + } + return ''; + }, + }, + { + label: '请求耗时', + field: 'duration', + render: (val) => { + return val ? `${val} ms` : ''; + }, + }, + { + label: '操作结果', + field: 'resultCode', + render: (val, data) => { + if (val === 0) { + return '正常'; + } else if (val > 0 && data?.resultMsg) { + return `失败 | ${val} | ${data.resultMsg}`; + } + return ''; + }, + }, + { + field: 'operateModule', + label: '操作模块', + }, + { + field: 'operateName', + label: '操作名', + }, + { + field: 'operateType', + label: '操作类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_OPERATE_TYPE, + value: val, + }); + }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/apiAccessLog/index.vue b/apps/web-naive/src/views/infra/apiAccessLog/index.vue new file mode 100644 index 000000000..af7cc0136 --- /dev/null +++ b/apps/web-naive/src/views/infra/apiAccessLog/index.vue @@ -0,0 +1,107 @@ + + + diff --git a/apps/web-naive/src/views/infra/apiAccessLog/modules/detail.vue b/apps/web-naive/src/views/infra/apiAccessLog/modules/detail.vue new file mode 100644 index 000000000..5fd1e5eeb --- /dev/null +++ b/apps/web-naive/src/views/infra/apiAccessLog/modules/detail.vue @@ -0,0 +1,49 @@ + + + diff --git a/apps/web-naive/src/views/infra/apiErrorLog/data.ts b/apps/web-naive/src/views/infra/apiErrorLog/data.ts new file mode 100644 index 000000000..87316a97e --- /dev/null +++ b/apps/web-naive/src/views/infra/apiErrorLog/data.ts @@ -0,0 +1,249 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { DescriptionItemSchema } from '#/components/description'; + +import { h } from 'vue'; + +import { JsonViewer } from '@vben/common-ui'; +import { DICT_TYPE, InfraApiErrorLogProcessStatusEnum } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { formatDateTime } from '@vben/utils'; + +import { DictTag } from '#/components/dict-tag'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'userId', + label: '用户编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入用户编号', + }, + }, + { + fieldName: 'userType', + label: '用户类型', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.USER_TYPE, 'number'), + clearable: true, + placeholder: '请选择用户类型', + }, + }, + { + fieldName: 'applicationName', + label: '应用名', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入应用名', + }, + }, + { + fieldName: 'exceptionTime', + label: '异常时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + { + fieldName: 'processStatus', + label: '处理状态', + component: 'Select', + componentProps: { + options: getDictOptions( + DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS, + 'number', + ), + clearable: true, + placeholder: '请选择处理状态', + }, + defaultValue: InfraApiErrorLogProcessStatusEnum.INIT, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '日志编号', + minWidth: 100, + }, + { + field: 'userId', + title: '用户编号', + minWidth: 100, + }, + { + field: 'userType', + title: '用户类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.USER_TYPE }, + }, + }, + { + field: 'applicationName', + title: '应用名', + minWidth: 150, + }, + { + field: 'requestMethod', + title: '请求方法', + minWidth: 80, + }, + { + field: 'requestUrl', + title: '请求地址', + minWidth: 200, + }, + { + field: 'exceptionTime', + title: '异常发生时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'exceptionName', + title: '异常名', + minWidth: 180, + }, + { + field: 'processStatus', + title: '处理状态', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS }, + }, + }, + { + title: '操作', + minWidth: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 详情页的字段 */ +export function useDetailSchema(): DescriptionItemSchema[] { + return [ + { + field: 'id', + label: '日志编号', + }, + { + field: 'traceId', + label: '链路追踪', + }, + { + field: 'applicationName', + label: '应用名', + }, + { + field: 'userId', + label: '用户Id', + }, + { + field: 'userType', + label: '用户类型', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.USER_TYPE, + value: val, + }); + }, + }, + { + field: 'userIp', + label: '用户 IP', + }, + { + field: 'userAgent', + label: '用户 UA', + }, + { + field: 'requestMethod', + label: '请求信息', + render: (val, data) => { + if (val && data?.requestUrl) { + return `${val} ${data.requestUrl}`; + } + return ''; + }, + }, + { + field: 'requestParams', + label: '请求参数', + render: (val) => { + if (val) { + return h(JsonViewer, { + value: JSON.parse(val), + previewMode: true, + }); + } + return ''; + }, + }, + { + field: 'exceptionTime', + label: '异常时间', + render: (val) => { + return formatDateTime(val) as string; + }, + }, + { + field: 'exceptionName', + label: '异常名', + }, + { + field: 'exceptionStackTrace', + label: '异常堆栈', + show: (val) => !val, + render: (val) => { + if (val) { + return h('textarea', { + value: val, + style: + 'width: 100%; min-height: 200px; max-height: 400px; resize: vertical;', + readonly: true, + }); + } + return ''; + }, + }, + { + field: 'processStatus', + label: '处理状态', + render: (val) => { + return h(DictTag, { + type: DICT_TYPE.INFRA_API_ERROR_LOG_PROCESS_STATUS, + value: val, + }); + }, + }, + { + field: 'processUserId', + label: '处理人', + show: (val) => !val, + }, + { + field: 'processTime', + label: '处理时间', + show: (val) => !val, + render: (val) => { + return formatDateTime(val) as string; + }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/apiErrorLog/index.vue b/apps/web-naive/src/views/infra/apiErrorLog/index.vue new file mode 100644 index 000000000..ad9748be8 --- /dev/null +++ b/apps/web-naive/src/views/infra/apiErrorLog/index.vue @@ -0,0 +1,155 @@ + + + diff --git a/apps/web-naive/src/views/infra/apiErrorLog/modules/detail.vue b/apps/web-naive/src/views/infra/apiErrorLog/modules/detail.vue new file mode 100644 index 000000000..b46a368e8 --- /dev/null +++ b/apps/web-naive/src/views/infra/apiErrorLog/modules/detail.vue @@ -0,0 +1,49 @@ + + + diff --git a/apps/web-naive/src/views/infra/build/index.vue b/apps/web-naive/src/views/infra/build/index.vue new file mode 100644 index 000000000..a976d982f --- /dev/null +++ b/apps/web-naive/src/views/infra/build/index.vue @@ -0,0 +1,9 @@ + + + diff --git a/apps/web-naive/src/views/infra/codegen/data.ts b/apps/web-naive/src/views/infra/codegen/data.ts new file mode 100644 index 000000000..ed742d84f --- /dev/null +++ b/apps/web-naive/src/views/infra/codegen/data.ts @@ -0,0 +1,553 @@ +import type { Recordable } from '@vben/types'; + +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { InfraCodegenApi } from '#/api/infra/codegen'; +import type { SystemMenuApi } from '#/api/system/menu'; + +import { h } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; +import { IconifyIcon } from '@vben/icons'; +import { handleTree } from '@vben/utils'; + +import { getDataSourceConfigList } from '#/api/infra/data-source-config'; +import { getMenuList } from '#/api/system/menu'; +import { $t } from '#/locales'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 导入数据库表的表单 */ +export function useImportTableFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'dataSourceConfigId', + label: '数据源', + component: 'ApiSelect', + componentProps: { + api: getDataSourceConfigList, + labelField: 'name', + valueField: 'id', + autoSelect: 'first', + placeholder: '请选择数据源', + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '表名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入表名称', + }, + }, + { + fieldName: 'comment', + label: '表描述', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入表描述', + }, + }, + ]; +} + +/** 导入数据库表表格列定义 */ +export function useImportTableColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { field: 'name', title: '表名称', minWidth: 200 }, + { field: 'comment', title: '表描述', minWidth: 200 }, + ]; +} + +/** 基本信息表单的 schema */ +export function useBasicInfoFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'tableName', + label: '表名称', + component: 'Input', + componentProps: { + placeholder: '请输入仓库名称', + }, + rules: 'required', + }, + { + fieldName: 'tableComment', + label: '表描述', + component: 'Input', + componentProps: { + placeholder: '请输入表描述', + }, + rules: 'required', + }, + { + fieldName: 'className', + label: '实体类名称', + component: 'Input', + componentProps: { + placeholder: '请输入实体类名称', + }, + rules: 'required', + help: '默认去除表名的前缀。如果存在重复,则需要手动添加前缀,避免 MyBatis 报 Alias 重复的问题。', + }, + { + fieldName: 'author', + label: '作者', + component: 'Input', + componentProps: { + placeholder: '请输入作者', + }, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + type: 'textarea', + rows: 3, + placeholder: '请输入备注', + }, + formItemClass: 'md:col-span-2', + }, + ]; +} + +/** 生成信息表单基础 schema */ +export function useGenerationInfoBaseFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Select', + fieldName: 'templateType', + label: '生成模板', + componentProps: { + options: getDictOptions( + DICT_TYPE.INFRA_CODEGEN_TEMPLATE_TYPE, + 'number', + ), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'frontType', + label: '前端类型', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_FRONT_TYPE, 'number'), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'scene', + label: '生成场景', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CODEGEN_SCENE, 'number'), + class: 'w-full', + }, + rules: 'selectRequired', + }, + { + fieldName: 'parentMenuId', + label: '上级菜单', + help: '分配到指定菜单下,例如 系统管理', + component: 'ApiTreeSelect', + componentProps: { + clearable: true, + api: async () => { + const data = await getMenuList(); + data.unshift({ + id: 0, + name: '顶级菜单', + } as SystemMenuApi.Menu); + return handleTree(data); + }, + class: 'w-full', + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级菜单', + filterTreeNode(input: string, node: Recordable) { + if (!input || input.length === 0) { + return true; + } + const name: string = node.label ?? ''; + if (!name) return false; + return name.includes(input) || $t(name).includes(input); + }, + showSearch: true, + treeDefaultExpandedKeys: [0], + }, + rules: 'selectRequired', + renderComponentContent() { + return { + title({ label, icon }: { icon: string; label: string }) { + const components = []; + if (!label) return ''; + if (icon) { + components.push(h(IconifyIcon, { class: 'size-4', icon })); + } + components.push(h('span', { class: '' }, $t(label || ''))); + return h('div', { class: 'flex items-center gap-1' }, components); + }, + }; + }, + }, + { + component: 'Input', + fieldName: 'moduleName', + label: '模块名', + help: '模块名,即一级目录,例如 system、infra、tool 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'businessName', + label: '业务名', + help: '业务名,即二级目录,例如 user、permission、dict 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'className', + label: '类名称', + help: '类名称(首字母大写),例如SysUser、SysMenu、SysDictData 等等', + rules: 'required', + }, + { + component: 'Input', + fieldName: 'classComment', + label: '类描述', + help: '用作类描述,例如 用户', + rules: 'required', + }, + ]; +} + +/** 树表信息 schema */ +export function useGenerationInfoTreeFormSchema( + columns: InfraCodegenApi.CodegenColumn[] = [], +): VbenFormSchema[] { + return [ + { + component: 'Divider', + fieldName: 'treeDivider', + label: '', + renderComponentContent: () => { + return { + default: () => ['树表信息'], + }; + }, + formItemClass: 'md:col-span-2', + }, + { + component: 'Select', + fieldName: 'treeParentColumnId', + label: '父编号字段', + help: '树显示的父编码字段名,例如 parent_Id', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择', + options: columns.map((column) => ({ + label: column.columnName, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'treeNameColumnId', + label: '名称字段', + help: '树节点显示的名称字段,一般是 name', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择名称字段', + options: columns.map((column) => ({ + label: column.columnName, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + ]; +} + +/** 主子表信息 schema */ +export function useGenerationInfoSubTableFormSchema( + columns: InfraCodegenApi.CodegenColumn[] = [], + tables: InfraCodegenApi.CodegenTable[] = [], +): VbenFormSchema[] { + return [ + { + component: 'Divider', + fieldName: 'subDivider', + label: '', + renderComponentContent: () => { + return { + default: () => ['主子表信息'], + }; + }, + formItemClass: 'md:col-span-2', + }, + { + component: 'Select', + fieldName: 'masterTableId', + label: '关联的主表', + help: '关联主表(父表)的表名, 如:system_user', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择', + options: tables.map((table) => ({ + label: `${table.tableName}:${table.tableComment}`, + value: table.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'Select', + fieldName: 'subJoinColumnId', + label: '子表关联的字段', + help: '子表关联的字段, 如:user_id', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择', + options: columns.map((column) => ({ + label: `${column.columnName}:${column.columnComment}`, + value: column.id, + })), + }, + rules: 'selectRequired', + }, + { + component: 'RadioGroup', + fieldName: 'subJoinMany', + label: '关联关系', + help: '主表与子表的关联关系', + componentProps: { + class: 'w-full', + clearable: true, + placeholder: '请选择', + options: [ + { + label: '一对多', + value: true, + }, + { + label: '一对一', + value: false, + }, + ], + }, + rules: 'required', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'tableName', + label: '表名称', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入表名称', + }, + }, + { + fieldName: 'tableComment', + label: '表描述', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入表描述', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns( + getDataSourceConfigName?: (dataSourceConfigId: number) => string | undefined, +): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'dataSourceConfigId', + title: '数据源', + minWidth: 120, + formatter: ({ cellValue }) => getDataSourceConfigName?.(cellValue) || '-', + }, + { + field: 'tableName', + title: '表名称', + minWidth: 200, + }, + { + field: 'tableComment', + title: '表描述', + minWidth: 200, + }, + { + field: 'className', + title: '实体', + minWidth: 200, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 代码生成表格列定义 */ +export function useCodegenColumnTableColumns(): VxeTableGridOptions['columns'] { + return [ + { field: 'columnName', title: '字段列名', minWidth: 130 }, + { + field: 'columnComment', + title: '字段描述', + minWidth: 100, + slots: { default: 'columnComment' }, + }, + { field: 'dataType', title: '物理类型', minWidth: 100 }, + { + field: 'javaType', + title: 'Java 类型', + minWidth: 130, + slots: { default: 'javaType' }, + params: { + options: [ + { label: 'Long', value: 'Long' }, + { label: 'String', value: 'String' }, + { label: 'Integer', value: 'Integer' }, + { label: 'Double', value: 'Double' }, + { label: 'BigDecimal', value: 'BigDecimal' }, + { label: 'LocalDateTime', value: 'LocalDateTime' }, + { label: 'Boolean', value: 'Boolean' }, + ], + }, + }, + { + field: 'javaField', + title: 'Java 属性', + minWidth: 100, + slots: { default: 'javaField' }, + }, + { + field: 'createOperation', + title: '插入', + width: 40, + slots: { default: 'createOperation' }, + }, + { + field: 'updateOperation', + title: '编辑', + width: 40, + slots: { default: 'updateOperation' }, + }, + { + field: 'listOperationResult', + title: '列表', + width: 40, + slots: { default: 'listOperationResult' }, + }, + { + field: 'listOperation', + title: '查询', + width: 40, + slots: { default: 'listOperation' }, + }, + { + field: 'listOperationCondition', + title: '查询方式', + minWidth: 100, + slots: { default: 'listOperationCondition' }, + params: { + options: [ + { label: '=', value: '=' }, + { label: '!=', value: '!=' }, + { label: '>', value: '>' }, + { label: '>=', value: '>=' }, + { label: '<', value: '<' }, + { label: '<=', value: '<=' }, + { label: 'LIKE', value: 'LIKE' }, + { label: 'BETWEEN', value: 'BETWEEN' }, + ], + }, + }, + { + field: 'nullable', + title: '允许空', + width: 60, + slots: { default: 'nullable' }, + }, + { + field: 'htmlType', + title: '显示类型', + width: 130, + slots: { default: 'htmlType' }, + params: { + options: [ + { label: '文本框', value: 'input' }, + { label: '文本域', value: 'textarea' }, + { label: '下拉框', value: 'select' }, + { label: '单选框', value: 'radio' }, + { label: '复选框', value: 'checkbox' }, + { label: '日期控件', value: 'datetime' }, + { label: '图片上传', value: 'imageUpload' }, + { label: '文件上传', value: 'fileUpload' }, + { label: '富文本控件', value: 'editor' }, + ], + }, + }, + { + field: 'dictType', + title: '字典类型', + width: 120, + slots: { default: 'dictType' }, + }, + { + field: 'example', + title: '示例', + minWidth: 100, + slots: { default: 'example' }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/codegen/edit/index.vue b/apps/web-naive/src/views/infra/codegen/edit/index.vue new file mode 100644 index 000000000..df0c9e02e --- /dev/null +++ b/apps/web-naive/src/views/infra/codegen/edit/index.vue @@ -0,0 +1,164 @@ + + + diff --git a/apps/web-naive/src/views/infra/codegen/index.vue b/apps/web-naive/src/views/infra/codegen/index.vue new file mode 100644 index 000000000..27cfeca04 --- /dev/null +++ b/apps/web-naive/src/views/infra/codegen/index.vue @@ -0,0 +1,283 @@ + + diff --git a/apps/web-naive/src/views/infra/codegen/modules/column-info.vue b/apps/web-naive/src/views/infra/codegen/modules/column-info.vue new file mode 100644 index 000000000..e9343aff7 --- /dev/null +++ b/apps/web-naive/src/views/infra/codegen/modules/column-info.vue @@ -0,0 +1,153 @@ + + + diff --git a/apps/web-naive/src/views/infra/codegen/modules/generation-info.vue b/apps/web-naive/src/views/infra/codegen/modules/generation-info.vue new file mode 100644 index 000000000..56ffe616f --- /dev/null +++ b/apps/web-naive/src/views/infra/codegen/modules/generation-info.vue @@ -0,0 +1,176 @@ + + + diff --git a/apps/web-naive/src/views/infra/codegen/modules/import-table.vue b/apps/web-naive/src/views/infra/codegen/modules/import-table.vue new file mode 100644 index 000000000..1898bad7e --- /dev/null +++ b/apps/web-naive/src/views/infra/codegen/modules/import-table.vue @@ -0,0 +1,118 @@ + + + diff --git a/apps/web-naive/src/views/infra/codegen/modules/preview-code.vue b/apps/web-naive/src/views/infra/codegen/modules/preview-code.vue new file mode 100644 index 000000000..d5a4e4327 --- /dev/null +++ b/apps/web-naive/src/views/infra/codegen/modules/preview-code.vue @@ -0,0 +1,268 @@ + + + diff --git a/apps/web-naive/src/views/infra/config/data.ts b/apps/web-naive/src/views/infra/config/data.ts new file mode 100644 index 000000000..c5aaf85b8 --- /dev/null +++ b/apps/web-naive/src/views/infra/config/data.ts @@ -0,0 +1,186 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'category', + label: '参数分类', + component: 'Input', + componentProps: { + placeholder: '请输入参数分类', + }, + rules: 'required', + }, + { + fieldName: 'name', + label: '参数名称', + component: 'Input', + componentProps: { + placeholder: '请输入参数名称', + }, + rules: 'required', + }, + { + fieldName: 'key', + label: '参数键名', + component: 'Input', + componentProps: { + placeholder: '请输入参数键名', + }, + rules: 'required', + }, + { + fieldName: 'value', + label: '参数键值', + component: 'Input', + componentProps: { + placeholder: '请输入参数键值', + }, + rules: 'required', + }, + { + fieldName: 'visible', + label: '是否可见', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING, 'boolean'), + }, + defaultValue: true, + rules: 'required', + }, + { + fieldName: 'remark', + label: '备注', + component: 'Input', + componentProps: { + type: 'textarea', + placeholder: '请输入备注', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '参数名称', + component: 'Input', + componentProps: { + placeholder: '请输入参数名称', + clearable: true, + }, + }, + { + fieldName: 'key', + label: '参数键名', + component: 'Input', + componentProps: { + placeholder: '请输入参数键名', + clearable: true, + }, + }, + { + fieldName: 'type', + label: '系统内置', + component: 'Select', + componentProps: { + options: getDictOptions(DICT_TYPE.INFRA_CONFIG_TYPE, 'number'), + placeholder: '请选择系统内置', + clearable: true, + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '参数主键', + minWidth: 100, + }, + { + field: 'category', + title: '参数分类', + minWidth: 120, + }, + { + field: 'name', + title: '参数名称', + minWidth: 200, + }, + { + field: 'key', + title: '参数键名', + minWidth: 200, + }, + { + field: 'value', + title: '参数键值', + minWidth: 150, + }, + { + field: 'visible', + title: '是否可见', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_BOOLEAN_STRING }, + }, + }, + { + field: 'type', + title: '系统内置', + minWidth: 100, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.INFRA_CONFIG_TYPE }, + }, + }, + { + field: 'remark', + title: '备注', + minWidth: 150, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/config/index.vue b/apps/web-naive/src/views/infra/config/index.vue new file mode 100644 index 000000000..e1e5b1c51 --- /dev/null +++ b/apps/web-naive/src/views/infra/config/index.vue @@ -0,0 +1,183 @@ + + + diff --git a/apps/web-naive/src/views/infra/config/modules/form.vue b/apps/web-naive/src/views/infra/config/modules/form.vue new file mode 100644 index 000000000..7a5a63994 --- /dev/null +++ b/apps/web-naive/src/views/infra/config/modules/form.vue @@ -0,0 +1,81 @@ + + + diff --git a/apps/web-naive/src/views/infra/dataSourceConfig/data.ts b/apps/web-naive/src/views/infra/dataSourceConfig/data.ts new file mode 100644 index 000000000..6f4c6027f --- /dev/null +++ b/apps/web-naive/src/views/infra/dataSourceConfig/data.ts @@ -0,0 +1,92 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + component: 'Input', + fieldName: 'id', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '数据源名称', + component: 'Input', + componentProps: { + placeholder: '请输入数据源名称', + }, + rules: 'required', + }, + { + fieldName: 'url', + label: '数据源连接', + component: 'Input', + componentProps: { + placeholder: '请输入数据源连接', + }, + rules: 'required', + }, + { + fieldName: 'username', + label: '用户名', + component: 'Input', + componentProps: { + placeholder: '请输入用户名', + }, + rules: 'required', + }, + { + fieldName: 'password', + label: '密码', + component: 'Input', + componentProps: { + placeholder: '请输入密码', + type: 'password', + }, + rules: 'required', + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '主键编号', + minWidth: 100, + }, + { + field: 'name', + title: '数据源名称', + minWidth: 150, + }, + { + field: 'url', + title: '数据源连接', + minWidth: 300, + }, + { + field: 'username', + title: '用户名', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/dataSourceConfig/index.vue b/apps/web-naive/src/views/infra/dataSourceConfig/index.vue new file mode 100644 index 000000000..866b26b38 --- /dev/null +++ b/apps/web-naive/src/views/infra/dataSourceConfig/index.vue @@ -0,0 +1,159 @@ + + + diff --git a/apps/web-naive/src/views/infra/dataSourceConfig/modules/form.vue b/apps/web-naive/src/views/infra/dataSourceConfig/modules/form.vue new file mode 100644 index 000000000..025707154 --- /dev/null +++ b/apps/web-naive/src/views/infra/dataSourceConfig/modules/form.vue @@ -0,0 +1,88 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo01/data.ts b/apps/web-naive/src/views/infra/demo/demo01/data.ts new file mode 100644 index 000000000..ad3d735e2 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo01/data.ts @@ -0,0 +1,149 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo01ContactApi } from '#/api/infra/demo/demo01'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生年', + rules: 'required', + component: 'DatePicker', + componentProps: { + type: 'datetime', + valueFormat: 'YYYY-MM-dd HH:mm:ss', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + { + fieldName: 'avatar', + label: '头像', + component: 'ImageUpload', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生年', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'avatar', + title: '头像', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 160, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/demo/demo01/index.vue b/apps/web-naive/src/views/infra/demo/demo01/index.vue new file mode 100644 index 000000000..c59c5bcf8 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo01/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo01/modules/form.vue b/apps/web-naive/src/views/infra/demo/demo01/modules/form.vue new file mode 100644 index 000000000..09bfe8fe9 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo01/modules/form.vue @@ -0,0 +1,87 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo02/data.ts b/apps/web-naive/src/views/infra/demo/demo02/data.ts new file mode 100644 index 000000000..202846468 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo02/data.ts @@ -0,0 +1,120 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo02CategoryApi } from '#/api/infra/demo/demo02'; + +import { handleTree } from '@vben/utils'; + +import { getDemo02CategoryList } from '#/api/infra/demo/demo02'; +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'parentId', + label: '上级示例分类', + component: 'ApiTreeSelect', + componentProps: { + clearable: true, + api: async () => { + const data = await getDemo02CategoryList({}); + data.unshift({ + id: 0, + name: '顶级示例分类', + }); + return handleTree(data); + }, + labelField: 'name', + valueField: 'id', + childrenField: 'children', + placeholder: '请选择上级示例分类', + treeDefaultExpandAll: true, + }, + rules: 'selectRequired', + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'parentId', + label: '父级编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入父级编号', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + treeNode: true, + }, + { + field: 'parentId', + title: '父级编号', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 220, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/demo/demo02/index.vue b/apps/web-naive/src/views/infra/demo/demo02/index.vue new file mode 100644 index 000000000..b7823cbfa --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo02/index.vue @@ -0,0 +1,175 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo02/modules/form.vue b/apps/web-naive/src/views/infra/demo/demo02/modules/form.vue new file mode 100644 index 000000000..c400afe33 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo02/modules/form.vue @@ -0,0 +1,90 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/erp/data.ts b/apps/web-naive/src/views/infra/demo/demo03/erp/data.ts new file mode 100644 index 000000000..68ad79d6f --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/erp/data.ts @@ -0,0 +1,378 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/erp'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + type: 'datetime', + valueFormat: 'YYYY-MM-dd HH:mm:ss', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改的表单 */ +export function useDemo03CourseFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'score', + label: '分数', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入分数', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useDemo03CourseGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'studentId', + label: '学生编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入学生编号', + }, + }, + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'score', + label: '分数', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入分数', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03CourseGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useDemo03GradeGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'studentId', + label: '学生编号', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入学生编号', + }, + }, + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入班主任', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03GradeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'teacher', + title: '班主任', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/demo/demo03/erp/index.vue b/apps/web-naive/src/views/infra/demo/demo03/erp/index.vue new file mode 100644 index 000000000..edbc69e04 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/erp/index.vue @@ -0,0 +1,209 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue new file mode 100644 index 000000000..98b7991e2 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-course-form.vue @@ -0,0 +1,91 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue new file mode 100644 index 000000000..09ea3e573 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-course-list.vue @@ -0,0 +1,197 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue new file mode 100644 index 000000000..c120919e0 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-grade-form.vue @@ -0,0 +1,91 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue new file mode 100644 index 000000000..519fac7c1 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/demo03-grade-list.vue @@ -0,0 +1,199 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/erp/modules/form.vue b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/form.vue new file mode 100644 index 000000000..79561fb39 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/erp/modules/form.vue @@ -0,0 +1,87 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/inner/data.ts b/apps/web-naive/src/views/infra/demo/demo03/inner/data.ts new file mode 100644 index 000000000..241afa3b9 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/inner/data.ts @@ -0,0 +1,273 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/inner'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + type: 'datetime', + valueFormat: 'YYYY-MM-dd HH:mm:ss', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { type: 'expand', width: 80, slots: { content: 'expand_content' } }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改列表的字段 */ +export function useDemo03CourseGridEditColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '名字', + minWidth: 120, + slots: { default: 'name' }, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + slots: { default: 'score' }, + }, + { + title: '操作', + width: 280, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03CourseGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} + +/** 列表的字段 */ +export function useDemo03GradeGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'studentId', + title: '学生编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'teacher', + title: '班主任', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/demo/demo03/inner/index.vue b/apps/web-naive/src/views/infra/demo/demo03/inner/index.vue new file mode 100644 index 000000000..976b2da1a --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/inner/index.vue @@ -0,0 +1,203 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue new file mode 100644 index 000000000..2be8932f9 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-course-form.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue new file mode 100644 index 000000000..50297db48 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-course-list.vue @@ -0,0 +1,56 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-grade-form.vue b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-grade-form.vue new file mode 100644 index 000000000..3e2854608 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-grade-form.vue @@ -0,0 +1,51 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue new file mode 100644 index 000000000..1ad2db740 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/demo03-grade-list.vue @@ -0,0 +1,56 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/inner/modules/form.vue b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/form.vue new file mode 100644 index 000000000..fcc36caa3 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/inner/modules/form.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/normal/data.ts b/apps/web-naive/src/views/infra/demo/demo03/normal/data.ts new file mode 100644 index 000000000..5add375ae --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/normal/data.ts @@ -0,0 +1,208 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { Demo03StudentApi } from '#/api/infra/demo/demo03/normal'; + +import { DICT_TYPE } from '@vben/constants'; +import { getDictOptions } from '@vben/hooks'; + +import { getRangePickerDefaultProps } from '#/utils'; + +/** 新增/修改的表单 */ +export function useFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + rules: 'required', + component: 'RadioGroup', + componentProps: { + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + }, + }, + { + fieldName: 'birthday', + label: '出生日期', + rules: 'required', + component: 'DatePicker', + componentProps: { + type: 'datetime', + valueFormat: 'YYYY-MM-dd HH:mm:ss', + }, + }, + { + fieldName: 'description', + label: '简介', + rules: 'required', + component: 'RichTextarea', + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'name', + label: '名字', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入名字', + }, + }, + { + fieldName: 'sex', + label: '性别', + component: 'Select', + componentProps: { + clearable: true, + options: getDictOptions(DICT_TYPE.SYSTEM_USER_SEX, 'number'), + placeholder: '请选择性别', + }, + }, + { + fieldName: 'description', + label: '简介', + component: 'Input', + componentProps: { + clearable: true, + placeholder: '请输入简介', + }, + }, + { + fieldName: 'createTime', + label: '创建时间', + component: 'DatePicker', + componentProps: { + ...getRangePickerDefaultProps(), + clearable: true, + }, + }, + ]; +} + +/** 列表的字段 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { type: 'checkbox', width: 40 }, + { + field: 'id', + title: '编号', + minWidth: 120, + }, + { + field: 'name', + title: '名字', + minWidth: 120, + }, + { + field: 'sex', + title: '性别', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.SYSTEM_USER_SEX }, + }, + }, + { + field: 'birthday', + title: '出生日期', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + field: 'description', + title: '简介', + minWidth: 120, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 120, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生课程) ==================== + +/** 新增/修改列表的字段 */ +export function useDemo03CourseGridEditColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'name', + title: '名字', + minWidth: 120, + slots: { default: 'name' }, + }, + { + field: 'score', + title: '分数', + minWidth: 120, + slots: { default: 'score' }, + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +// ==================== 子表(学生班级) ==================== + +/** 新增/修改的表单 */ +export function useDemo03GradeFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'id', + component: 'InputNumber', + dependencies: { + triggerFields: [''], + show: () => false, + }, + }, + { + fieldName: 'name', + label: '名字', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入名字', + }, + }, + { + fieldName: 'teacher', + label: '班主任', + rules: 'required', + component: 'Input', + componentProps: { + placeholder: '请输入班主任', + }, + }, + ]; +} diff --git a/apps/web-naive/src/views/infra/demo/demo03/normal/index.vue b/apps/web-naive/src/views/infra/demo/demo03/normal/index.vue new file mode 100644 index 000000000..35de85ddf --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/normal/index.vue @@ -0,0 +1,185 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue b/apps/web-naive/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue new file mode 100644 index 000000000..4d674cb99 --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/normal/modules/demo03-course-form.vue @@ -0,0 +1,119 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/normal/modules/demo03-grade-form.vue b/apps/web-naive/src/views/infra/demo/demo03/normal/modules/demo03-grade-form.vue new file mode 100644 index 000000000..5d5f396bf --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/normal/modules/demo03-grade-form.vue @@ -0,0 +1,51 @@ + + + diff --git a/apps/web-naive/src/views/infra/demo/demo03/normal/modules/form.vue b/apps/web-naive/src/views/infra/demo/demo03/normal/modules/form.vue new file mode 100644 index 000000000..624c941fa --- /dev/null +++ b/apps/web-naive/src/views/infra/demo/demo03/normal/modules/form.vue @@ -0,0 +1,120 @@ + + + diff --git a/apps/web-naive/src/views/infra/druid/index.vue b/apps/web-naive/src/views/infra/druid/index.vue new file mode 100644 index 000000000..01ec58d5f --- /dev/null +++ b/apps/web-naive/src/views/infra/druid/index.vue @@ -0,0 +1,36 @@ + + + diff --git a/packages/effects/common-ui/src/components/iframe/index.ts b/packages/effects/common-ui/src/components/iframe/index.ts new file mode 100644 index 000000000..d437bc0d3 --- /dev/null +++ b/packages/effects/common-ui/src/components/iframe/index.ts @@ -0,0 +1 @@ +export { default as IFrame } from './iframe.vue'; diff --git a/packages/effects/common-ui/src/components/index.ts b/packages/effects/common-ui/src/components/index.ts new file mode 100644 index 000000000..3bc11325f --- /dev/null +++ b/packages/effects/common-ui/src/components/index.ts @@ -0,0 +1,40 @@ +export * from './api-component'; +export * from './captcha'; +export * from './card/comparison-card'; +export * from './card/statistic-card'; +export * from './card/summary-card'; +export * from './col-page'; +export * from './content-wrap'; +export * from './count-to'; +export * from './doc-alert'; +export * from './ellipsis-text'; +export * from './icon-picker'; +export * from './iframe'; +export * from './json-viewer'; +export * from './loading'; +export * from './page'; +export * from './resize'; +export * from './tippy'; +export * from './tree'; +export * from '@vben-core/form-ui'; +export * from '@vben-core/popup-ui'; + +// 给文档用 +export { + VbenAvatar, + VbenButton, + VbenButtonGroup, + VbenCheckbox, + VbenCheckButtonGroup, + VbenCountToAnimator, + VbenFullScreen, + VbenInputPassword, + VbenLoading, + VbenLogo, + VbenPinInput, + VbenSelect, + VbenSpinner, +} from '@vben-core/shadcn-ui'; + +export type { FlattenedItem } from '@vben-core/shadcn-ui'; +export { globalShareState } from '@vben-core/shared/global-state'; diff --git a/packages/effects/common-ui/src/components/json-viewer/index.vue b/packages/effects/common-ui/src/components/json-viewer/index.vue new file mode 100644 index 000000000..f66c31828 --- /dev/null +++ b/packages/effects/common-ui/src/components/json-viewer/index.vue @@ -0,0 +1,112 @@ + + + diff --git a/packages/effects/common-ui/src/components/json-viewer/style.scss b/packages/effects/common-ui/src/components/json-viewer/style.scss new file mode 100644 index 000000000..47acb891f --- /dev/null +++ b/packages/effects/common-ui/src/components/json-viewer/style.scss @@ -0,0 +1,98 @@ +.default-json-theme { + font-family: Consolas, Menlo, Courier, monospace; + font-size: 14px; + color: hsl(var(--foreground)); + white-space: nowrap; + background: hsl(var(--background)); + + &.jv-container.boxed { + border: 1px solid hsl(var(--border)); + } + + .jv-ellipsis { + display: inline-block; + padding: 0 4px 2px; + font-size: 0.9em; + line-height: 0.9; + vertical-align: 2px; + color: hsl(var(--secondary-foreground)); + cursor: pointer; + user-select: none; + background-color: hsl(var(--secondary)); + border-radius: 3px; + } + + .jv-button { + color: hsl(var(--primary)); + } + + .jv-key { + color: hsl(var(--heavy-foreground)); + } + + .jv-item { + &.jv-array { + color: hsl(var(--heavy-foreground)); + } + + &.jv-boolean { + color: hsl(var(--red-400)); + } + + &.jv-function { + color: hsl(var(--destructive-foreground)); + } + + &.jv-number { + color: hsl(var(--info-foreground)); + } + + &.jv-number-float { + color: hsl(var(--info-foreground)); + } + + &.jv-number-integer { + color: hsl(var(--info-foreground)); + } + + &.jv-object { + color: hsl(var(--accent-darker)); + } + + &.jv-undefined { + color: hsl(var(--secondary-foreground)); + } + + &.jv-string { + color: hsl(var(--primary)); + overflow-wrap: break-word; + white-space: normal; + } + } + + &.jv-container .jv-code { + padding: 10px; + + &.boxed:not(.open) { + padding-bottom: 20px; + margin-bottom: 10px; + } + + &.open { + padding-bottom: 10px; + } + + .jv-toggle { + &::before { + padding: 0 2px; + border-radius: 2px; + } + + &:hover { + &::before { + background: hsl(var(--accent-foreground)); + } + } + } + } +} diff --git a/packages/effects/common-ui/src/components/page/page.vue b/packages/effects/common-ui/src/components/page/page.vue new file mode 100644 index 000000000..e5522f987 --- /dev/null +++ b/packages/effects/common-ui/src/components/page/page.vue @@ -0,0 +1,121 @@ + + + diff --git a/packages/effects/common-ui/src/components/resize/resize.vue b/packages/effects/common-ui/src/components/resize/resize.vue new file mode 100644 index 000000000..68be4d9cf --- /dev/null +++ b/packages/effects/common-ui/src/components/resize/resize.vue @@ -0,0 +1,1120 @@ + + + + + diff --git a/packages/effects/common-ui/src/components/tree/index.ts b/packages/effects/common-ui/src/components/tree/index.ts new file mode 100644 index 000000000..ce3bc5c68 --- /dev/null +++ b/packages/effects/common-ui/src/components/tree/index.ts @@ -0,0 +1 @@ +export { default as Tree } from './tree.vue'; diff --git a/packages/effects/common-ui/src/components/tree/tree.vue b/packages/effects/common-ui/src/components/tree/tree.vue new file mode 100644 index 000000000..1f2fcc17b --- /dev/null +++ b/packages/effects/common-ui/src/components/tree/tree.vue @@ -0,0 +1,25 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/code-login.vue b/packages/effects/common-ui/src/ui/authentication/code-login.vue new file mode 100644 index 000000000..d7563d8f1 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/code-login.vue @@ -0,0 +1,127 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/dingding-login.vue b/packages/effects/common-ui/src/ui/authentication/dingding-login.vue new file mode 100644 index 000000000..80b123c1b --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/dingding-login.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/packages/effects/common-ui/src/ui/authentication/login.vue b/packages/effects/common-ui/src/ui/authentication/login.vue new file mode 100644 index 000000000..c2a9c4bb6 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/login.vue @@ -0,0 +1,203 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue b/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue new file mode 100644 index 000000000..5d7cfac91 --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue @@ -0,0 +1,106 @@ + + + diff --git a/packages/effects/common-ui/src/ui/authentication/third-party-login.vue b/packages/effects/common-ui/src/ui/authentication/third-party-login.vue new file mode 100644 index 000000000..fc36ce90d --- /dev/null +++ b/packages/effects/common-ui/src/ui/authentication/third-party-login.vue @@ -0,0 +1,55 @@ + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue new file mode 100644 index 000000000..236b4764d --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue @@ -0,0 +1,64 @@ + + + diff --git a/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue new file mode 100644 index 000000000..ef34cd09d --- /dev/null +++ b/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue @@ -0,0 +1,63 @@ + + + diff --git a/packages/effects/common-ui/src/ui/index.ts b/packages/effects/common-ui/src/ui/index.ts new file mode 100644 index 000000000..8b1db5585 --- /dev/null +++ b/packages/effects/common-ui/src/ui/index.ts @@ -0,0 +1,5 @@ +export * from './about'; +export * from './authentication'; +export * from './dashboard'; +export * from './fallback'; +export * from './profile'; diff --git a/packages/effects/common-ui/src/ui/profile/base-setting.vue b/packages/effects/common-ui/src/ui/profile/base-setting.vue new file mode 100644 index 000000000..1ef1c1a04 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/base-setting.vue @@ -0,0 +1,56 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/index.ts b/packages/effects/common-ui/src/ui/profile/index.ts new file mode 100644 index 000000000..209a2e813 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/index.ts @@ -0,0 +1,6 @@ +export { default as ProfileBaseSetting } from './base-setting.vue'; +export { default as ProfileNotificationSetting } from './notification-setting.vue'; +export { default as ProfilePasswordSetting } from './password-setting.vue'; +export { default as Profile } from './profile.vue'; +export { default as ProfileSecuritySetting } from './security-setting.vue'; +export type * from './types'; diff --git a/packages/effects/common-ui/src/ui/profile/notification-setting.vue b/packages/effects/common-ui/src/ui/profile/notification-setting.vue new file mode 100644 index 000000000..9a3e48820 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/notification-setting.vue @@ -0,0 +1,53 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/password-setting.vue b/packages/effects/common-ui/src/ui/profile/password-setting.vue new file mode 100644 index 000000000..1d90e086b --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/password-setting.vue @@ -0,0 +1,56 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/profile.vue b/packages/effects/common-ui/src/ui/profile/profile.vue new file mode 100644 index 000000000..47b83b47a --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/profile.vue @@ -0,0 +1,62 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/security-setting.vue b/packages/effects/common-ui/src/ui/profile/security-setting.vue new file mode 100644 index 000000000..9a3e48820 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/security-setting.vue @@ -0,0 +1,53 @@ + + diff --git a/packages/effects/common-ui/src/ui/profile/types.ts b/packages/effects/common-ui/src/ui/profile/types.ts new file mode 100644 index 000000000..e600bde83 --- /dev/null +++ b/packages/effects/common-ui/src/ui/profile/types.ts @@ -0,0 +1,21 @@ +import type { BasicUserInfo } from '@vben/types'; + +export interface Props { + title?: string; + userInfo: BasicUserInfo | null; + tabs: { + label: string; + value: string; + }[]; +} + +export interface FormSchemaItem { + description: string; + fieldName: string; + label: string; + value: boolean; +} + +export interface SettingProps { + formSchema: FormSchemaItem[]; +} diff --git a/packages/effects/hooks/package.json b/packages/effects/hooks/package.json new file mode 100644 index 000000000..76d83a5ef --- /dev/null +++ b/packages/effects/hooks/package.json @@ -0,0 +1,33 @@ +{ + "name": "@vben/hooks", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/hooks" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:", + "watermark-js-plus": "catalog:" + } +} diff --git a/packages/effects/hooks/src/index.ts b/packages/effects/hooks/src/index.ts new file mode 100644 index 000000000..b795bb3c5 --- /dev/null +++ b/packages/effects/hooks/src/index.ts @@ -0,0 +1,10 @@ +export * from './use-app-config'; +export * from './use-content-maximize'; +export * from './use-design-tokens'; +export * from './use-dict'; +export * from './use-hover-toggle'; +export * from './use-pagination'; +export * from './use-refresh'; +export * from './use-tabs'; +export * from './use-watermark'; +export * from '@vben-core/composables'; diff --git a/packages/effects/hooks/src/use-app-config.ts b/packages/effects/hooks/src/use-app-config.ts new file mode 100644 index 000000000..444896413 --- /dev/null +++ b/packages/effects/hooks/src/use-app-config.ts @@ -0,0 +1,48 @@ +import type { + ApplicationConfig, + VbenAdminProAppConfigRaw, +} from '@vben/types/global'; + +/** + * 由 vite-inject-app-config 注入的全局配置 + */ +export function useAppConfig( + env: Record, + isProduction: boolean, +): ApplicationConfig { + // 生产环境下,直接使用 window._VBEN_ADMIN_PRO_APP_CONF_ 全局变量 + const config = isProduction + ? window._VBEN_ADMIN_PRO_APP_CONF_ + : (env as VbenAdminProAppConfigRaw); + + const { + VITE_GLOB_API_URL, + VITE_GLOB_AUTH_DINGDING_CORP_ID, + VITE_GLOB_AUTH_DINGDING_CLIENT_ID, + } = config; + + const applicationConfig: ApplicationConfig = { + apiURL: VITE_GLOB_API_URL, + auth: {}, + }; + if (VITE_GLOB_AUTH_DINGDING_CORP_ID && VITE_GLOB_AUTH_DINGDING_CLIENT_ID) { + applicationConfig.auth.dingding = { + clientId: VITE_GLOB_AUTH_DINGDING_CLIENT_ID, + corpId: VITE_GLOB_AUTH_DINGDING_CORP_ID, + }; + } + + return applicationConfig; +} + +export function isTenantEnable(): boolean { + return import.meta.env.VITE_APP_TENANT_ENABLE === 'true'; +} + +export function isCaptchaEnable(): boolean { + return import.meta.env.VITE_APP_CAPTCHA_ENABLE === 'true'; +} + +export function isDocAlertEnable(): boolean { + return import.meta.env.VITE_APP_DOCALERT_ENABLE !== 'false'; +} diff --git a/packages/effects/hooks/src/use-dict.ts b/packages/effects/hooks/src/use-dict.ts new file mode 100644 index 000000000..a1e49eae9 --- /dev/null +++ b/packages/effects/hooks/src/use-dict.ts @@ -0,0 +1,87 @@ +import { useDictStore } from '@vben/stores'; +import { isObject } from '@vben/utils'; + +type ColorType = 'error' | 'info' | 'success' | 'warning'; + +export interface DictDataType { + dictType?: string; + label: string; + value: boolean | number | string; + colorType?: ColorType; + cssClass?: string; +} + +export interface NumberDictDataType extends DictDataType { + value: number; +} + +export interface StringDictDataType extends DictDataType { + value: string; +} + +/** + * 获取字典标签 + * + * @param dictType 字典类型 + * @param value 字典值 + * @returns 字典标签 + */ +export function getDictLabel(dictType: string, value: any) { + const dictStore = useDictStore(); + const dictObj = dictStore.getDictData(dictType, value); + return isObject(dictObj) ? dictObj.label : ''; +} + +/** + * 获取字典对象 + * + * @param dictType 字典类型 + * @param value 字典值 + * @returns 字典对象 + */ +export function getDictObj(dictType: string, value: any) { + const dictStore = useDictStore(); + const dictObj = dictStore.getDictData(dictType, value); + return isObject(dictObj) ? dictObj : null; +} + +/** + * 获取字典数组 用于select radio 等 + * + * @param dictType 字典类型 + * @param valueType 字典值类型,默认 string 类型 + * @returns 字典数组 + */ +export function getDictOptions( + dictType: string, + valueType: 'boolean' | 'number' | 'string' = 'string', +): DictDataType[] { + const dictStore = useDictStore(); + const dictOpts = dictStore.getDictOptions(dictType); + const dictOptions: DictDataType[] = []; + if (dictOpts.length > 0) { + let dictValue: boolean | number | string = ''; + dictOpts.forEach((d) => { + switch (valueType) { + case 'boolean': { + dictValue = `${d.value}` === 'true'; + break; + } + case 'number': { + dictValue = Number.parseInt(`${d.value}`); + break; + } + case 'string': { + dictValue = `${d.value}`; + break; + } + // No default + } + dictOptions.push({ + value: dictValue, + label: d.label, + }); + }); + } + return dictOptions.length > 0 ? dictOptions : []; +} diff --git a/packages/effects/hooks/src/use-pagination.ts b/packages/effects/hooks/src/use-pagination.ts new file mode 100644 index 000000000..0115606d3 --- /dev/null +++ b/packages/effects/hooks/src/use-pagination.ts @@ -0,0 +1,72 @@ +import type { Ref } from 'vue'; + +import { computed, ref, unref, watch } from 'vue'; + +/** + * Paginates an array of items + * @param list The array to paginate + * @param pageNo The current page number (1-based) + * @param pageSize Number of items per page + * @returns Paginated array slice + * @throws {Error} If pageNo or pageSize are invalid + */ +function pagination(list: T[], pageNo: number, pageSize: number): T[] { + if (pageNo < 1) throw new Error('Page number must be positive'); + if (pageSize < 1) throw new Error('Page size must be positive'); + + const offset = (pageNo - 1) * Number(pageSize); + const ret = + offset + pageSize >= list.length + ? list.slice(offset) + : list.slice(offset, offset + pageSize); + return ret; +} + +export function usePagination( + list: Ref, + pageSize: number, + totalChangeToFirstPage = true, +) { + const currentPage = ref(1); + const pageSizeRef = ref(pageSize); + + const totalPages = computed(() => + Math.ceil(unref(list).length / unref(pageSizeRef)), + ); + + const paginationList = computed(() => { + return pagination(unref(list), unref(currentPage), unref(pageSizeRef)); + }); + + const total = computed(() => { + return unref(list).length; + }); + + if (totalChangeToFirstPage) { + watch(total, () => { + setCurrentPage(1); + }); + } + + function setCurrentPage(page: number) { + if (page === 1 && unref(totalPages) === 0) { + currentPage.value = 1; + } else { + if (page < 1 || page > unref(totalPages)) { + throw new Error('Invalid page number'); + } + currentPage.value = page; + } + } + + function setPageSize(pageSize: number) { + if (pageSize < 1) { + throw new Error('Page size must be positive'); + } + pageSizeRef.value = pageSize; + // Reset to first page to prevent invalid state + currentPage.value = 1; + } + + return { setCurrentPage, total, setPageSize, paginationList, currentPage }; +} diff --git a/packages/effects/layouts/package.json b/packages/effects/layouts/package.json new file mode 100644 index 000000000..3c195d3ac --- /dev/null +++ b/packages/effects/layouts/package.json @@ -0,0 +1,43 @@ +{ + "name": "@vben/layouts", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/layouts" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/composables": "workspace:*", + "@vben-core/form-ui": "workspace:*", + "@vben-core/layout-ui": "workspace:*", + "@vben-core/menu-ui": "workspace:*", + "@vben-core/popup-ui": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/tabs-ui": "workspace:*", + "@vben/constants": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/stores": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/packages/effects/layouts/src/authentication/authentication.vue b/packages/effects/layouts/src/authentication/authentication.vue new file mode 100644 index 000000000..6d3bf8c33 --- /dev/null +++ b/packages/effects/layouts/src/authentication/authentication.vue @@ -0,0 +1,196 @@ + + + + + diff --git a/packages/effects/layouts/src/authentication/form.vue b/packages/effects/layouts/src/authentication/form.vue new file mode 100644 index 000000000..fa73e128e --- /dev/null +++ b/packages/effects/layouts/src/authentication/form.vue @@ -0,0 +1,38 @@ + + + diff --git a/packages/effects/layouts/src/basic/header/header.vue b/packages/effects/layouts/src/basic/header/header.vue new file mode 100644 index 000000000..44ec67172 --- /dev/null +++ b/packages/effects/layouts/src/basic/header/header.vue @@ -0,0 +1,195 @@ + + + + diff --git a/packages/effects/layouts/src/basic/layout.vue b/packages/effects/layouts/src/basic/layout.vue new file mode 100644 index 000000000..605f3cf52 --- /dev/null +++ b/packages/effects/layouts/src/basic/layout.vue @@ -0,0 +1,412 @@ + + + diff --git a/packages/effects/layouts/src/basic/menu/use-navigation.ts b/packages/effects/layouts/src/basic/menu/use-navigation.ts new file mode 100644 index 000000000..daa6dc71a --- /dev/null +++ b/packages/effects/layouts/src/basic/menu/use-navigation.ts @@ -0,0 +1,74 @@ +import type { RouteRecordNormalized } from 'vue-router'; + +import { useRouter } from 'vue-router'; + +import { isHttpUrl, openRouteInNewWindow, openWindow } from '@vben/utils'; + +function useNavigation() { + const router = useRouter(); + const routeMetaMap = new Map(); + + // 初始化路由映射 + const initRouteMetaMap = () => { + const routes = router.getRoutes(); + routes.forEach((route) => { + routeMetaMap.set(route.path, route); + }); + }; + + initRouteMetaMap(); + + // 监听路由变化 + router.afterEach(() => { + initRouteMetaMap(); + }); + + // 检查是否应该在新窗口打开 + const shouldOpenInNewWindow = (path: string): boolean => { + if (isHttpUrl(path)) { + return true; + } + const route = routeMetaMap.get(path); + // 如果有外链或者设置了在新窗口打开,返回 true + return !!(route?.meta?.link || route?.meta?.openInNewWindow); + }; + + const resolveHref = (path: string): string => { + return router.resolve(path).href; + }; + + const navigation = async (path: string) => { + try { + const route = routeMetaMap.get(path); + const { openInNewWindow = false, query = {}, link } = route?.meta ?? {}; + + // 检查是否有外链 + if (link && typeof link === 'string') { + openWindow(link, { target: '_blank' }); + return; + } + + if (isHttpUrl(path)) { + openWindow(path, { target: '_blank' }); + } else if (openInNewWindow) { + openRouteInNewWindow(resolveHref(path)); + } else { + await router.push({ + path, + query, + }); + } + } catch (error) { + console.error('Navigation failed:', error); + throw error; + } + }; + + const willOpenedByWindow = (path: string) => { + return shouldOpenInNewWindow(path); + }; + + return { navigation, willOpenedByWindow }; +} + +export { useNavigation }; diff --git a/packages/effects/layouts/src/widgets/global-search/search-panel.vue b/packages/effects/layouts/src/widgets/global-search/search-panel.vue new file mode 100644 index 000000000..6e9404945 --- /dev/null +++ b/packages/effects/layouts/src/widgets/global-search/search-panel.vue @@ -0,0 +1,288 @@ + + + diff --git a/packages/effects/layouts/src/widgets/index.ts b/packages/effects/layouts/src/widgets/index.ts new file mode 100644 index 000000000..665cdb9e9 --- /dev/null +++ b/packages/effects/layouts/src/widgets/index.ts @@ -0,0 +1,14 @@ +export { default as Breadcrumb } from './breadcrumb.vue'; +export * from './check-updates'; +export { default as AuthenticationColorToggle } from './color-toggle.vue'; +export * from './global-search'; +export * from './help'; +export { default as LanguageToggle } from './language-toggle.vue'; +export { default as AuthenticationLayoutToggle } from './layout-toggle.vue'; +export * from './lock-screen'; +export * from './notification'; +export * from './preferences'; +export * from './tenant-dropdown'; +export * from './theme-toggle'; +export * from './timezone'; +export * from './user-dropdown'; diff --git a/packages/effects/layouts/src/widgets/language-toggle.vue b/packages/effects/layouts/src/widgets/language-toggle.vue new file mode 100644 index 000000000..f92432f9b --- /dev/null +++ b/packages/effects/layouts/src/widgets/language-toggle.vue @@ -0,0 +1,39 @@ + + + diff --git a/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue b/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue new file mode 100644 index 000000000..908e67060 --- /dev/null +++ b/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue @@ -0,0 +1,110 @@ + + + diff --git a/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue b/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue new file mode 100644 index 000000000..3f700334f --- /dev/null +++ b/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue @@ -0,0 +1,167 @@ + + + diff --git a/packages/effects/layouts/src/widgets/notification/notification.vue b/packages/effects/layouts/src/widgets/notification/notification.vue new file mode 100644 index 000000000..4135ea9f9 --- /dev/null +++ b/packages/effects/layouts/src/widgets/notification/notification.vue @@ -0,0 +1,241 @@ + + + + diff --git a/packages/effects/layouts/src/widgets/notification/types.ts b/packages/effects/layouts/src/widgets/notification/types.ts new file mode 100644 index 000000000..3f015cec5 --- /dev/null +++ b/packages/effects/layouts/src/widgets/notification/types.ts @@ -0,0 +1,17 @@ +interface NotificationItem { + id: any; + avatar: string; + date: string; + isRead?: boolean; + message: string; + title: string; + /** + * 跳转链接,可以是路由路径或完整 URL + * @example '/dashboard' 或 'https://example.com' + */ + link?: string; + query?: Record; + state?: Record; +} + +export type { NotificationItem }; diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue b/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue new file mode 100644 index 000000000..c69cd3aa9 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue @@ -0,0 +1,47 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue b/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue new file mode 100644 index 000000000..8e9f09e7a --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue @@ -0,0 +1,63 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue b/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue new file mode 100644 index 000000000..33d99e99f --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue @@ -0,0 +1,55 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue b/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue new file mode 100644 index 000000000..d0dba8ecc --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue @@ -0,0 +1,163 @@ + + + diff --git a/packages/effects/layouts/src/widgets/preferences/preferences-button.vue b/packages/effects/layouts/src/widgets/preferences/preferences-button.vue new file mode 100644 index 000000000..8598bb270 --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/preferences-button.vue @@ -0,0 +1,20 @@ + + diff --git a/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue new file mode 100644 index 000000000..301004d6e --- /dev/null +++ b/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue @@ -0,0 +1,488 @@ + + + + + diff --git a/packages/effects/layouts/src/widgets/tenant-dropdown/tenant-dropdown.vue b/packages/effects/layouts/src/widgets/tenant-dropdown/tenant-dropdown.vue new file mode 100644 index 000000000..76e2b2f1f --- /dev/null +++ b/packages/effects/layouts/src/widgets/tenant-dropdown/tenant-dropdown.vue @@ -0,0 +1,85 @@ + + diff --git a/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue b/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue new file mode 100644 index 000000000..8d24b53c1 --- /dev/null +++ b/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/packages/effects/layouts/src/widgets/timezone/index.ts b/packages/effects/layouts/src/widgets/timezone/index.ts new file mode 100644 index 000000000..eef486ddb --- /dev/null +++ b/packages/effects/layouts/src/widgets/timezone/index.ts @@ -0,0 +1 @@ +export { default as TimezoneButton } from './timezone-button.vue'; diff --git a/packages/effects/layouts/src/widgets/timezone/timezone-button.vue b/packages/effects/layouts/src/widgets/timezone/timezone-button.vue new file mode 100644 index 000000000..f426b94cd --- /dev/null +++ b/packages/effects/layouts/src/widgets/timezone/timezone-button.vue @@ -0,0 +1,87 @@ + + + + + diff --git a/packages/effects/plugins/package.json b/packages/effects/plugins/package.json new file mode 100644 index 000000000..ef0c8fe4e --- /dev/null +++ b/packages/effects/plugins/package.json @@ -0,0 +1,70 @@ +{ + "name": "@vben/plugins", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/plugins" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + "./code-editor": { + "types": "./src/code-editor/index.ts", + "default": "./src/code-editor/index.ts" + }, + "./echarts": { + "types": "./src/echarts/index.ts", + "default": "./src/echarts/index.ts" + }, + "./vxe-table": { + "types": "./src/vxe-table/index.ts", + "default": "./src/vxe-table/index.ts" + }, + "./motion": { + "types": "./src/motion/index.ts", + "default": "./src/motion/index.ts" + }, + "./markmap": { + "types": "./src/markmap/index.ts", + "default": "./src/markmap/index.ts" + }, + "./tinyflow": { + "types": "./src/tinyflow/index.ts", + "default": "./src/tinyflow/index.ts" + } + }, + "dependencies": { + "@tinyflow-ai/vue": "catalog:", + "@vben-core/form-ui": "workspace:*", + "@vben-core/shadcn-ui": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben/hooks": "workspace:*", + "@vben/icons": "workspace:*", + "@vben/locales": "workspace:*", + "@vben/preferences": "workspace:*", + "@vben/types": "workspace:*", + "@vben/utils": "workspace:*", + "@vueuse/core": "catalog:", + "@vueuse/motion": "catalog:", + "codemirror": "catalog:", + "echarts": "catalog:", + "markdown-it": "catalog:", + "markmap-common": "catalog:", + "markmap-lib": "catalog:", + "markmap-toolbar": "catalog:", + "markmap-view": "catalog:", + "vue": "catalog:", + "vxe-pc-ui": "catalog:", + "vxe-table": "catalog:" + }, + "devDependencies": { + "@types/codemirror": "catalog:", + "@types/markdown-it": "catalog:" + } +} diff --git a/packages/effects/plugins/src/code-editor/code-editor.vue b/packages/effects/plugins/src/code-editor/code-editor.vue new file mode 100644 index 000000000..7062ac02b --- /dev/null +++ b/packages/effects/plugins/src/code-editor/code-editor.vue @@ -0,0 +1,54 @@ + + + diff --git a/packages/effects/plugins/src/code-editor/code-mirror.ts b/packages/effects/plugins/src/code-editor/code-mirror.ts new file mode 100644 index 000000000..a4a9f8ee8 --- /dev/null +++ b/packages/effects/plugins/src/code-editor/code-mirror.ts @@ -0,0 +1,23 @@ +// modes +import 'codemirror/mode/javascript/javascript'; +import 'codemirror/mode/css/css'; +import 'codemirror/mode/htmlmixed/htmlmixed'; +import 'codemirror/mode/vue/vue'; + +// import 'codemirror/addon/lint/lint.css'; +import './codemirror.css'; +import 'codemirror/theme/idea.css'; +import 'codemirror/theme/material-palenight.css'; + +// addons +// import 'codemirror/addon/edit/closebrackets'; +// import 'codemirror/addon/edit/closetag'; +// import 'codemirror/addon/comment/comment'; +// import 'codemirror/addon/fold/foldcode'; +// import 'codemirror/addon/fold/foldgutter'; +// import 'codemirror/addon/fold/brace-fold'; +// import 'codemirror/addon/fold/indent-fold'; +// import 'codemirror/addon/lint/json-lint'; +// import 'codemirror/addon/fold/comment-fold'; + +export { default as CodeMirror } from 'codemirror'; diff --git a/packages/effects/plugins/src/code-editor/code-mirror.vue b/packages/effects/plugins/src/code-editor/code-mirror.vue new file mode 100644 index 000000000..42d58ef7a --- /dev/null +++ b/packages/effects/plugins/src/code-editor/code-mirror.vue @@ -0,0 +1,137 @@ + + + diff --git a/packages/effects/plugins/src/code-editor/codemirror.css b/packages/effects/plugins/src/code-editor/codemirror.css new file mode 100644 index 000000000..55775fac4 --- /dev/null +++ b/packages/effects/plugins/src/code-editor/codemirror.css @@ -0,0 +1,524 @@ +/* BASICS */ + +.CodeMirror { + --base: #545281; + --comment: hsl(210deg 25% 60%); + --keyword: #af4ab1; + --variable: #0055d1; + --function: #c25205; + --string: #2ba46d; + --number: #c25205; + --tags: #d00; + --qualifier: #ff6032; + --important: var(--string); + + position: relative; + height: auto; + height: 100%; + overflow: hidden; + font-family: var(--font-code); + background: white; + direction: ltr; +} + +/* PADDING */ + +.CodeMirror-lines { + min-height: 1px; /* prevents collapsing before first draw */ + padding: 4px 0; /* Vertical padding around content */ + cursor: text; +} + +.CodeMirror-scrollbar-filler, +.CodeMirror-gutter-filler { + background-color: white; /* The little square between H and V scrollbars */ +} + +/* GUTTER */ + +.CodeMirror-gutters { + position: absolute; + top: 0; + left: 0; + z-index: 3; + min-height: 100%; + white-space: nowrap; + background-color: transparent; + border-right: 1px solid #ddd; +} + +.CodeMirror-linenumber { + min-width: 20px; + padding: 0 3px 0 5px; + color: var(--comment); + text-align: right; + white-space: nowrap; + opacity: 0.6; +} + +.CodeMirror-guttermarker { + color: black; +} + +.CodeMirror-guttermarker-subtle { + color: #999; +} + +/* FOLD GUTTER */ + +.CodeMirror-foldmarker { + font-family: arial; + line-height: 0.3; + color: #414141; + text-shadow: + #f96 1px 1px 2px, + #f96 -1px -1px 2px, + #f96 1px -1px 2px, + #f96 -1px 1px 2px; + cursor: pointer; +} + +.CodeMirror-foldgutter { + width: 0.7em; +} + +.CodeMirror-foldgutter-open, +.CodeMirror-foldgutter-folded { + cursor: pointer; +} + +.CodeMirror-foldgutter-open::after, +.CodeMirror-foldgutter-folded::after { + position: relative; + top: -0.1em; + display: inline-block; + font-size: 0.8em; + content: '>'; + opacity: 0.8; + transform: rotate(90deg); + transition: transform 0.2s; +} + +.CodeMirror-foldgutter-folded::after { + transform: none; +} + +/* CURSOR */ + +.CodeMirror-cursor { + position: absolute; + width: 0; + pointer-events: none; + border-right: none; + border-left: 1px solid black; +} + +/* Shown when moving in bi-directional text */ +.CodeMirror div.CodeMirror-secondarycursor { + border-left: 1px solid silver; +} + +.cm-fat-cursor .CodeMirror-cursor { + width: auto; + background: #7e7; + border: 0 !important; +} + +.cm-fat-cursor div.CodeMirror-cursors { + z-index: 1; +} + +.cm-fat-cursor-mark { + background-color: rgb(20 255 20 / 50%); + animation: blink 1.06s steps(1) infinite; +} + +.cm-animate-fat-cursor { + width: auto; + background-color: #7e7; + border: 0; + animation: blink 1.06s steps(1) infinite; +} + +@keyframes blink { + 50% { + background-color: transparent; + } +} + +@keyframes blink { + 50% { + background-color: transparent; + } +} + +@keyframes blink { + 50% { + background-color: transparent; + } +} + +.cm-tab { + display: inline-block; + text-decoration: inherit; +} + +.CodeMirror-rulers { + position: absolute; + inset: -50px 0 -20px; + overflow: hidden; +} + +.CodeMirror-ruler { + position: absolute; + top: 0; + bottom: 0; + border-left: 1px solid #ccc; +} + +/* DEFAULT THEME */ +.cm-s-default.CodeMirror { + background-color: transparent; +} + +.cm-s-default .cm-header { + color: blue; +} + +.cm-s-default .cm-quote { + color: #090; +} + +.cm-negative { + color: #d44; +} + +.cm-positive { + color: #292; +} + +.cm-header, +.cm-strong { + font-weight: bold; +} + +.cm-em { + font-style: italic; +} + +.cm-link { + text-decoration: underline; +} + +.cm-strikethrough { + text-decoration: line-through; +} + +.cm-s-default .cm-atom, +.cm-s-default .cm-def, +.cm-s-default .cm-property, +.cm-s-default .cm-variable-2, +.cm-s-default .cm-variable-3, +.cm-s-default .cm-punctuation { + color: var(--base); +} + +.cm-s-default .cm-hr, +.cm-s-default .cm-comment { + color: var(--comment); +} + +.cm-s-default .cm-attribute, +.cm-s-default .cm-keyword { + color: var(--keyword); +} + +.cm-s-default .cm-variable { + color: var(--variable); +} + +.cm-s-default .cm-bracket, +.cm-s-default .cm-tag { + color: var(--tags); +} + +.cm-s-default .cm-number { + color: var(--number); +} + +.cm-s-default .cm-string, +.cm-s-default .cm-string-2 { + color: var(--string); +} + +.cm-s-default .cm-type { + color: #085; +} + +.cm-s-default .cm-meta { + color: #555; +} + +.cm-s-default .cm-qualifier { + color: var(--qualifier); +} + +.cm-s-default .cm-builtin { + color: #7539ff; +} + +.cm-s-default .cm-link { + color: var(--flash); +} + +.cm-s-default .cm-error { + color: #ff008c; +} + +.cm-invalidchar { + color: #ff008c; +} + +.CodeMirror-composing { + border-bottom: 2px solid; +} + +/* Default styles for common addons */ + +div.CodeMirror span.CodeMirror-matchingbracket { + color: #0b0; +} + +div.CodeMirror span.CodeMirror-nonmatchingbracket { + color: #a22; +} + +.CodeMirror-matchingtag { + background: rgb(255 150 0 / 30%); +} + +.CodeMirror-activeline-background { + background: #e8f2ff; +} + +/* STOP */ + +/* The rest of this file contains styles related to the mechanics of + the editor. You probably shouldn't touch them. */ + +.CodeMirror-scroll { + position: relative; + height: 100%; + padding-bottom: 30px; + margin-right: -30px; + + /* 30px is the magic margin used to hide the element's real scrollbars */ + + /* See overflow: hidden in .CodeMirror */ + margin-bottom: -30px; + overflow: scroll !important; /* Things will break if this is overridden */ + outline: none; /* Prevent dragging from highlighting the element */ +} + +.CodeMirror-sizer { + position: relative; + margin-bottom: 20px !important; + border-right: 30px solid transparent; +} + +/* The fake, visible scrollbars. Used to force redraw during scrolling + before actual scrolling happens, thus preventing shaking and + flickering artifacts. */ +.CodeMirror-vscrollbar, +.CodeMirror-hscrollbar, +.CodeMirror-scrollbar-filler, +.CodeMirror-gutter-filler { + position: absolute; + z-index: 6; + display: none; +} + +.CodeMirror-vscrollbar { + top: 0; + right: 0; + overflow: hidden scroll; +} + +.CodeMirror-hscrollbar { + bottom: 0; + left: 0; + overflow: scroll hidden; +} + +.CodeMirror-scrollbar-filler { + right: 0; + bottom: 0; +} + +.CodeMirror-gutter-filler { + bottom: 0; + left: 0; +} + +.CodeMirror-gutter { + display: inline-block; + height: 100%; + margin-bottom: -30px; + vertical-align: top; + white-space: normal; +} + +.CodeMirror-gutter-wrapper { + position: absolute; + z-index: 4; + background: none !important; + border: none !important; +} + +.CodeMirror-gutter-background { + position: absolute; + top: 0; + bottom: 0; + z-index: 4; +} + +.CodeMirror-gutter-elt { + position: absolute; + z-index: 4; + cursor: default; +} + +.CodeMirror-gutter-wrapper ::selection { + background-color: transparent; +} + +.CodeMirrorwrapper ::selection { + background-color: transparent; +} + +.CodeMirror pre { + position: relative; + z-index: 2; + padding: 0 4px; /* Horizontal padding of content */ + margin: 0; + overflow: visible; + font-family: inherit; + font-size: inherit; + font-variant-ligatures: contextual; + line-height: inherit; + color: inherit; + word-wrap: normal; + white-space: pre; + background: transparent; + border-width: 0; + + /* Reset some styles that the rest of the page might have set */ + border-radius: 0; + -webkit-tap-highlight-color: transparent; +} + +.CodeMirror-wrap pre { + word-break: normal; + word-wrap: break-word; + white-space: pre-wrap; +} + +.CodeMirror-linebackground { + position: absolute; + inset: 0; + z-index: 0; +} + +.CodeMirror-linewidget { + position: relative; + z-index: 2; + padding: 0.1px; /* Force widget margins to stay inside of the container */ +} + +.CodeMirror-rtl pre { + direction: rtl; +} + +.CodeMirror-code { + outline: none; +} + +/* Force content-box sizing for the elements where we expect it */ +.CodeMirror-scroll, +.CodeMirror-sizer, +.CodeMirror-gutter, +.CodeMirror-gutters, +.CodeMirror-linenumber { + box-sizing: content-box; +} + +.CodeMirror-measure { + position: absolute; + visibility: hidden; + width: 100%; + height: 0; + overflow: hidden; +} + +.CodeMirror-measure pre { + position: static; +} + +div.CodeMirror-cursors { + position: relative; + z-index: 3; + visibility: hidden; +} + +div.CodeMirror-dragcursors { + visibility: visible; +} + +.CodeMirror-focused div.CodeMirror-cursors { + visibility: visible; +} + +.CodeMirror-selected { + background: #d9d9d9; +} + +.CodeMirror-focused .CodeMirror-selected { + background: #d7d4f0; +} + +.CodeMirror-crosshair { + cursor: crosshair; +} + +.CodeMirror-line::selection, +.CodeMirror-line > span::selection, +.CodeMirror-line > span > span::selection { + background: #d7d4f0; +} + +.cm-searching { + background-color: #ffa; + background-color: rgb(255 255 0 / 40%); +} + +/* Used to force a border model for a node */ +.cm-force-border { + padding-right: 0.1px; +} + +@media print { + /* Hide the cursor when printing */ + .CodeMirror div.CodeMirror-cursors { + visibility: hidden; + } +} + +/* See issue #2901 */ +.cm-tab-wrap-hack::after { + content: ''; +} + +/* Help users use markselection to safely style text background */ +span.CodeMirror-selectedtext { + background: none; +} diff --git a/packages/effects/plugins/src/code-editor/index.ts b/packages/effects/plugins/src/code-editor/index.ts new file mode 100644 index 000000000..d19a3b42f --- /dev/null +++ b/packages/effects/plugins/src/code-editor/index.ts @@ -0,0 +1,2 @@ +export { default as CodeEditor } from './code-editor.vue'; +export * from './types'; diff --git a/packages/effects/plugins/src/code-editor/types.ts b/packages/effects/plugins/src/code-editor/types.ts new file mode 100644 index 000000000..c1d490d7a --- /dev/null +++ b/packages/effects/plugins/src/code-editor/types.ts @@ -0,0 +1,14 @@ +export enum MODE { + HTML = 'htmlmixed', + JS = 'javascript', + JSON = 'application/json', + VUE = 'vue', +} + +export interface CodeEditorProps { + mode?: string; + value?: string; + readonly?: boolean; + bordered?: boolean; + autoFormat?: boolean; +} diff --git a/packages/effects/plugins/src/echarts/echarts.ts b/packages/effects/plugins/src/echarts/echarts.ts new file mode 100644 index 000000000..216d883d4 --- /dev/null +++ b/packages/effects/plugins/src/echarts/echarts.ts @@ -0,0 +1,90 @@ +import type { + // 系列类型的定义后缀都为 SeriesOption + BarSeriesOption, + GaugeSeriesOption, + LineSeriesOption, + MapSeriesOption, +} from 'echarts/charts'; +import type { + DatasetComponentOption, + DataZoomComponentOption, + GeoComponentOption, + GridComponentOption, + // 组件类型的定义后缀都为 ComponentOption + TitleComponentOption, + TooltipComponentOption, + VisualMapComponentOption, +} from 'echarts/components'; +import type { ComposeOption } from 'echarts/core'; + +import { + BarChart, + FunnelChart, + GaugeChart, + LineChart, + MapChart, + PieChart, + RadarChart, +} from 'echarts/charts'; +import { + // 数据集组件 + DatasetComponent, + DataZoomComponent, + DataZoomInsideComponent, + DataZoomSliderComponent, + GeoComponent, + GridComponent, + LegendComponent, + TitleComponent, + ToolboxComponent, + TooltipComponent, + // 内置数据转换器组件 (filter, sort) + TransformComponent, + VisualMapComponent, +} from 'echarts/components'; +import * as echarts from 'echarts/core'; +import { LabelLayout, UniversalTransition } from 'echarts/features'; +import { CanvasRenderer } from 'echarts/renderers'; + +// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型 +export type ECOption = ComposeOption< + | BarSeriesOption + | DatasetComponentOption + | DataZoomComponentOption + | GaugeSeriesOption + | GeoComponentOption + | GridComponentOption + | LineSeriesOption + | MapSeriesOption + | TitleComponentOption + | TooltipComponentOption + | VisualMapComponentOption +>; + +// 注册必须的组件 +echarts.use([ + TitleComponent, + PieChart, + RadarChart, + TooltipComponent, + GridComponent, + DatasetComponent, + DataZoomComponent, + DataZoomInsideComponent, + DataZoomSliderComponent, + TransformComponent, + BarChart, + LineChart, + FunnelChart, + GaugeChart, + LabelLayout, + UniversalTransition, + CanvasRenderer, + LegendComponent, + ToolboxComponent, + VisualMapComponent, + MapChart, + GeoComponent, +]); + +export default echarts; diff --git a/packages/effects/plugins/src/echarts/use-echarts.ts b/packages/effects/plugins/src/echarts/use-echarts.ts new file mode 100644 index 000000000..652aba4a5 --- /dev/null +++ b/packages/effects/plugins/src/echarts/use-echarts.ts @@ -0,0 +1,162 @@ +import type { EChartsOption } from 'echarts'; + +import type { Ref } from 'vue'; + +import type { Nullable } from '@vben/types'; + +import type EchartsUI from './echarts-ui.vue'; + +import { computed, nextTick, watch } from 'vue'; + +import { usePreferences } from '@vben/preferences'; + +import { + tryOnUnmounted, + useDebounceFn, + useResizeObserver, + useTimeoutFn, + useWindowSize, +} from '@vueuse/core'; + +import echarts from './echarts'; +// TODO @xingyu:有 500kb,china.json 会影响打包么? +import chinaMap from './map/china.json'; + +type EchartsUIType = typeof EchartsUI | undefined; + +type EchartsThemeType = 'dark' | 'light' | null; + +function useEcharts(chartRef: Ref) { + let chartInstance: echarts.ECharts | null = null; + let cacheOptions: EChartsOption = {}; + + const { isDark } = usePreferences(); + const { height, width } = useWindowSize(); + const resizeHandler: () => void = useDebounceFn(resize, 200); + + echarts.registerMap('china', { + geoJSON: chinaMap as any, + specialAreas: { + china: { + left: 500, + top: 500, + width: 1000, + height: 1000, + }, + }, + }); + + const getChartEl = (): HTMLElement | null => { + const refValue = chartRef?.value as unknown; + if (!refValue) return null; + if (refValue instanceof HTMLElement) { + return refValue; + } + const maybeComponent = refValue as { $el?: HTMLElement }; + return maybeComponent.$el ?? null; + }; + + const isElHidden = (el: HTMLElement | null): boolean => { + if (!el) return true; + return el.offsetHeight === 0 || el.offsetWidth === 0; + }; + + const getOptions = computed((): EChartsOption => { + if (!isDark.value) { + return {}; + } + + return { + backgroundColor: 'transparent', + }; + }); + + const initCharts = (t?: EchartsThemeType) => { + const el = chartRef?.value?.$el; + if (!el) { + return; + } + chartInstance = echarts.init(el, t || isDark.value ? 'dark' : null); + + return chartInstance; + }; + + const renderEcharts = ( + options: EChartsOption, + clear = true, + ): Promise> => { + cacheOptions = options; + const currentOptions = { + ...options, + ...getOptions.value, + }; + return new Promise((resolve) => { + if (chartRef.value?.offsetHeight === 0) { + useTimeoutFn(async () => { + resolve(await renderEcharts(currentOptions)); + }, 30); + return; + } + nextTick(() => { + const el = getChartEl(); + if (isElHidden(el)) { + useTimeoutFn(async () => { + resolve(await renderEcharts(currentOptions)); + }, 30); + return; + } + useTimeoutFn(() => { + if (!chartInstance) { + const instance = initCharts(); + if (!instance) return; + } + clear && chartInstance?.clear(); + chartInstance?.setOption(currentOptions); + resolve(chartInstance); + }, 30); + }); + }); + }; + + function resize() { + const el = getChartEl(); + if (isElHidden(el)) { + return; + } + chartInstance?.resize({ + animation: { + duration: 300, + easing: 'quadraticIn', + }, + }); + } + + watch([width, height], () => { + resizeHandler?.(); + }); + + useResizeObserver(chartRef as never, resizeHandler); + + watch(isDark, () => { + if (chartInstance) { + chartInstance.dispose(); + initCharts(); + renderEcharts(cacheOptions); + resize(); + } + }); + + tryOnUnmounted(() => { + // 销毁实例,释放资源 + chartInstance?.dispose(); + }); + return { + renderEcharts, + resize, + getChartInstance: () => chartInstance, + }; +} + +export { useEcharts }; + +export type { EchartsUIType }; diff --git a/packages/effects/plugins/src/tinyflow/index.ts b/packages/effects/plugins/src/tinyflow/index.ts new file mode 100644 index 000000000..f21bd14c1 --- /dev/null +++ b/packages/effects/plugins/src/tinyflow/index.ts @@ -0,0 +1 @@ +export { default as Tinyflow } from './tinyflow.vue'; diff --git a/packages/effects/plugins/src/tinyflow/tinyflow.vue b/packages/effects/plugins/src/tinyflow/tinyflow.vue new file mode 100644 index 000000000..256f8a081 --- /dev/null +++ b/packages/effects/plugins/src/tinyflow/tinyflow.vue @@ -0,0 +1,74 @@ + + + diff --git a/packages/effects/plugins/src/vxe-table/index.ts b/packages/effects/plugins/src/vxe-table/index.ts new file mode 100644 index 000000000..66196d772 --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/index.ts @@ -0,0 +1,27 @@ +import { defineAsyncComponent } from 'vue'; + +export { setupVbenVxeTable } from './init'; +export { default as VbenVxeTableToolbar } from './table-toolbar.vue'; +export type { VxeTableGridOptions } from './types'; +export * from './use-vxe-grid'; +export { default as VbenVxeGrid } from './use-vxe-grid.vue'; +export { useTableToolbar } from './use-vxe-toolbar'; +export * from './validation'; + +export type { + VxeGridListeners, + VxeGridProps, + VxeGridPropTypes, + VxeTableInstance, +} from 'vxe-table'; + +// 异步导出 vxe-table 相关组件提供给需要单独使用 vxe-table 的场景 +export const AsyncVxeTable = defineAsyncComponent(() => + import('vxe-table').then((mod) => mod.VxeTable), +); +export const AsyncVxeColumn = defineAsyncComponent(() => + import('vxe-table').then((mod) => mod.VxeColumn), +); +export const AsyncVxeToolbar = defineAsyncComponent(() => + import('vxe-table').then((mod) => mod.VxeToolbar), +); diff --git a/packages/effects/plugins/src/vxe-table/init.ts b/packages/effects/plugins/src/vxe-table/init.ts new file mode 100644 index 000000000..00e9d8abe --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/init.ts @@ -0,0 +1,131 @@ +import type { SetupVxeTable } from './types'; + +import { defineComponent, watch } from 'vue'; + +import { usePreferences } from '@vben/preferences'; + +import { useVbenForm } from '@vben-core/form-ui'; + +import { + VxeButton, + VxeCheckbox, + + // VxeFormGather, + // VxeForm, + // VxeFormItem, + VxeIcon, + VxeInput, + VxeLoading, + VxeModal, + VxeNumberInput, + VxePager, + // VxeList, + // VxeModal, + // VxeOptgroup, + // VxeOption, + // VxePulldown, + // VxeRadio, + // VxeRadioButton, + VxeRadioGroup, + VxeSelect, + VxeTooltip, + VxeUI, + VxeUpload, + // VxeSwitch, + // VxeTextarea, +} from 'vxe-pc-ui'; +import enUS from 'vxe-pc-ui/lib/language/en-US'; +// 导入默认的语言 +import zhCN from 'vxe-pc-ui/lib/language/zh-CN'; +import { + VxeColgroup, + VxeColumn, + VxeGrid, + VxeTable, + VxeToolbar, +} from 'vxe-table'; + +import { extendsDefaultFormatter } from './extends'; + +// 是否加载过 +let isInit = false; + +// eslint-disable-next-line import/no-mutable-exports +export let useTableForm: typeof useVbenForm; + +// 部分组件,如果没注册,vxe-table 会报错,这里实际没用组件,只是为了不报错,同时可以减少打包体积 +const createVirtualComponent = (name = '') => { + return defineComponent({ + name, + }); +}; + +export function initVxeTable() { + if (isInit) { + return; + } + + VxeUI.component(VxeTable); + VxeUI.component(VxeColumn); + VxeUI.component(VxeColgroup); + VxeUI.component(VxeGrid); + VxeUI.component(VxeToolbar); + + VxeUI.component(VxeButton); + // VxeUI.component(VxeButtonGroup); + VxeUI.component(VxeCheckbox); + // VxeUI.component(VxeCheckboxGroup); + VxeUI.component(createVirtualComponent('VxeForm')); + // VxeUI.component(VxeFormGather); + // VxeUI.component(VxeFormItem); + VxeUI.component(VxeIcon); + VxeUI.component(VxeInput); + // VxeUI.component(VxeList); + VxeUI.component(VxeLoading); + VxeUI.component(VxeModal); + VxeUI.component(VxeNumberInput); + // VxeUI.component(VxeOptgroup); + // VxeUI.component(VxeOption); + VxeUI.component(VxePager); + // VxeUI.component(VxePulldown); + // VxeUI.component(VxeRadio); + // VxeUI.component(VxeRadioButton); + VxeUI.component(VxeRadioGroup); + VxeUI.component(VxeSelect); + // VxeUI.component(VxeSwitch); + // VxeUI.component(VxeTextarea); + VxeUI.component(VxeTooltip); + VxeUI.component(VxeUpload); + + isInit = true; +} + +export function setupVbenVxeTable(setupOptions: SetupVxeTable) { + const { configVxeTable, useVbenForm } = setupOptions; + + initVxeTable(); + useTableForm = useVbenForm; + + const { isDark, locale } = usePreferences(); + + const localMap = { + 'zh-CN': zhCN, + 'en-US': enUS, + }; + + watch( + [() => isDark.value, () => locale.value], + ([isDarkValue, localeValue]) => { + VxeUI.setTheme(isDarkValue ? 'dark' : 'light'); + VxeUI.setI18n(localeValue, localMap[localeValue]); + VxeUI.setLanguage(localeValue); + }, + { + immediate: true, + }, + ); + + extendsDefaultFormatter(VxeUI); + + configVxeTable(VxeUI); +} diff --git a/packages/effects/plugins/src/vxe-table/style.css b/packages/effects/plugins/src/vxe-table/style.css new file mode 100644 index 000000000..152fcf219 --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/style.css @@ -0,0 +1,143 @@ +:root .vxe-grid { + --vxe-ui-font-color: hsl(var(--foreground)); + --vxe-ui-font-primary-color: hsl(var(--primary)); + + /* --vxe-ui-font-lighten-color: #babdc0; + --vxe-ui-font-darken-color: #86898e; */ + --vxe-ui-font-disabled-color: hsl(var(--foreground) / 50%); + + /* base */ + --vxe-ui-base-popup-border-color: hsl(var(--border)); + --vxe-ui-input-disabled-color: hsl(var(--border) / 60%); + + /* --vxe-ui-base-popup-box-shadow: 0px 12px 30px 8px rgb(0 0 0 / 50%); */ + + /* layout */ + --vxe-ui-layout-background-color: hsl(var(--background)); + --vxe-ui-table-resizable-line-color: hsl(var(--heavy)); + + /* --vxe-ui-table-fixed-left-scrolling-box-shadow: 8px 0px 10px -5px hsl(var(--accent)); + --vxe-ui-table-fixed-right-scrolling-box-shadow: -8px 0px 10px -5px hsl(var(--accent)); */ + + /* input */ + --vxe-ui-input-border-color: hsl(var(--border)); + + /* --vxe-ui-input-placeholder-color: #8d9095; */ + + /* --vxe-ui-input-disabled-background-color: #262727; */ + + /* loading */ + --vxe-ui-loading-background-color: hsl(var(--overlay-content)); + + /* table */ + --vxe-ui-table-header-background-color: hsl(var(--accent)); + --vxe-ui-table-border-color: hsl(var(--border)); + --vxe-ui-table-row-hover-background-color: hsl(var(--accent-hover)); + --vxe-ui-table-row-striped-background-color: hsl(var(--accent) / 60%); + --vxe-ui-table-row-hover-striped-background-color: hsl(var(--accent)); + --vxe-ui-table-row-radio-checked-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-radio-checked-background-color: hsl( + var(--accent-hover) + ); + --vxe-ui-table-row-checkbox-checked-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-checkbox-checked-background-color: hsl( + var(--accent-hover) + ); + --vxe-ui-table-row-current-background-color: hsl(var(--accent)); + --vxe-ui-table-row-hover-current-background-color: hsl(var(--accent-hover)); + --vxe-ui-font-primary-tinge-color: hsl(var(--primary)); + --vxe-ui-font-primary-lighten-color: hsl(var(--primary) / 60%); + --vxe-ui-font-primary-darken-color: hsl(var(--primary)); + + height: auto !important; + + /* --vxe-ui-table-fixed-scrolling-box-shadow-color: rgb(0 0 0 / 80%); */ +} + +.vxe-pager { + .vxe-pager--prev-btn:not(.is--disabled):active, + .vxe-pager--next-btn:not(.is--disabled):active, + .vxe-pager--num-btn:not(.is--disabled):active, + .vxe-pager--jump-prev:not(.is--disabled):active, + .vxe-pager--jump-next:not(.is--disabled):active, + .vxe-pager--prev-btn:not(.is--disabled):focus, + .vxe-pager--next-btn:not(.is--disabled):focus, + .vxe-pager--num-btn:not(.is--disabled):focus, + .vxe-pager--jump-prev:not(.is--disabled):focus, + .vxe-pager--jump-next:not(.is--disabled):focus { + color: hsl(var(--accent-foreground)); + background-color: hsl(var(--accent)); + border: 1px solid hsl(var(--border)); + box-shadow: 0 0 0 1px hsl(var(--border)); + } + + .vxe-pager--wrapper { + display: flex; + align-items: center; + } + + .vxe-pager--sizes { + margin-right: auto; + } +} + +.vxe-pager--wrapper { + @apply justify-center md:justify-end; +} + +.vxe-tools--operate { + margin-right: 0.25rem; + margin-left: 0.75rem; +} + +.vxe-table-custom--checkbox-option:hover { + background: none !important; +} + +.vxe-toolbar { + padding: 0; +} + +.vxe-buttons--wrapper:not(:empty), +.vxe-tools--operate:not(:empty), +.vxe-tools--wrapper:not(:empty) { + padding: 0.6em 0; +} + +.vxe-tools--operate:not(:has(button)) { + margin-left: 0; +} + +.vxe-grid--layout-header-wrapper { + overflow: visible; +} + +.vxe-grid--layout-body-content-wrapper { + overflow: hidden; +} + +/* 必填字段错误样式 */ +.vxe-table .required-field-error::after { + position: absolute; + top: -1px; + right: -1px; + z-index: 10; + padding: 1px 4px; + font-size: 10px; + line-height: 1; + color: white; + content: '必填'; + background-color: #ff4d4f; + border-radius: 0 0 0 4px; +} + +/* 必填字段内的输入框样式 */ +.vxe-table .required-field-error .ant-select, +.vxe-table .required-field-error .ant-input-number, +.vxe-table .required-field-error .ant-input { + border-color: #ff4d4f !important; +} + +.vxe-table .required-field-error .ant-select .ant-select-selector { + border-color: #ff4d4f !important; +} diff --git a/packages/effects/plugins/src/vxe-table/table-toolbar.vue b/packages/effects/plugins/src/vxe-table/table-toolbar.vue new file mode 100644 index 000000000..6e9332494 --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/table-toolbar.vue @@ -0,0 +1,74 @@ + + + + diff --git a/packages/effects/plugins/src/vxe-table/use-vxe-toolbar.ts b/packages/effects/plugins/src/vxe-table/use-vxe-toolbar.ts new file mode 100644 index 000000000..773b20f7e --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/use-vxe-toolbar.ts @@ -0,0 +1,48 @@ +import type { VxeTableInstance, VxeToolbarInstance } from 'vxe-table'; + +import { ref, watch } from 'vue'; + +import VbenVxeTableToolbar from './table-toolbar.vue'; + +/** + * vxe 原生工具栏挂载封装 + * 解决每个组件使用 vxe-table 组件时都需要写一遍的问题 + */ +export function useTableToolbar() { + const hiddenSearchBar = ref(false); // 隐藏搜索栏 + const tableToolbarRef = ref>(); + const tableRef = ref(); + const isBound = ref(false); + + /** 挂载 toolbar 工具栏 */ + async function bindTableToolbar() { + const table = tableRef.value; + const tableToolbar = tableToolbarRef.value; + if (table && tableToolbar) { + // 延迟 1 秒,确保 toolbar 组件已经挂载 + setTimeout(async () => { + const toolbar = tableToolbar.getToolbarRef(); + if (!toolbar) { + console.error('[toolbar 挂载失败] Table toolbar not found'); + } + await table.connectToolbar(toolbar as VxeToolbarInstance); + isBound.value = true; + }, 1000); // 延迟挂载确保 toolbar 正确挂载 + } + } + + watch( + () => tableRef.value, + async (val) => { + if (!val || isBound.value) return; + await bindTableToolbar(); + }, + { immediate: true }, + ); + + return { + hiddenSearchBar, + tableToolbarRef, + tableRef, + }; +} diff --git a/packages/effects/plugins/src/vxe-table/validation.ts b/packages/effects/plugins/src/vxe-table/validation.ts new file mode 100644 index 000000000..487f24107 --- /dev/null +++ b/packages/effects/plugins/src/vxe-table/validation.ts @@ -0,0 +1,61 @@ +/** + * 创建验证类名的工具函数 + * @param isValidating 验证状态 + * @param fieldName 字段名 + * @param validationRules 验证规则,可以是字符串或自定义函数 + * @returns 返回 className 函数 + */ +function createValidationClassName( + isValidating: any, + fieldName: string, + validationRules: ((row: any) => boolean) | string, +) { + return ({ row }: { row: any }) => { + if (!isValidating?.value) return ''; + + let isValid = true; + if (typeof validationRules === 'string') { + // 处理简单的验证规则 + if (validationRules === 'required') { + isValid = + fieldName === 'count' + ? row[fieldName] && row[fieldName] > 0 + : !!row[fieldName]; + } + } else if (typeof validationRules === 'function') { + // 处理自定义验证函数 + isValid = validationRules(row); + } + + return isValid ? '' : 'required-field-error'; + }; +} + +/** + * 创建必填字段验证 + * @param isValidating 验证状态 + * @param fieldName 字段名 + * @returns 返回 className 函数 + */ +function createRequiredValidation(isValidating: any, fieldName: string) { + return createValidationClassName(isValidating, fieldName, 'required'); +} + +/** + * 创建自定义验证 + * @param isValidating 验证状态 + * @param validationFn 自定义验证函数 + * @returns 返回 className 函数 + */ +function createCustomValidation( + isValidating: any, + validationFn: (row: any) => boolean, +) { + return createValidationClassName(isValidating, '', validationFn); +} + +export { + createCustomValidation, + createRequiredValidation, + createValidationClassName, +}; diff --git a/packages/effects/request/package.json b/packages/effects/request/package.json new file mode 100644 index 000000000..d7dfd4167 --- /dev/null +++ b/packages/effects/request/package.json @@ -0,0 +1,33 @@ +{ + "name": "@vben/request", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/effects/request" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@microsoft/fetch-event-source": "catalog:", + "@vben/locales": "workspace:*", + "@vben/utils": "workspace:*", + "axios": "catalog:", + "qs": "catalog:" + }, + "devDependencies": { + "@types/qs": "catalog:", + "axios-mock-adapter": "catalog:" + } +} diff --git a/packages/effects/request/src/request-client/modules/downloader.test.ts b/packages/effects/request/src/request-client/modules/downloader.test.ts new file mode 100644 index 000000000..27e8a30cb --- /dev/null +++ b/packages/effects/request/src/request-client/modules/downloader.test.ts @@ -0,0 +1,157 @@ +import type { AxiosRequestConfig } from 'axios'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { FileDownloader } from './downloader'; + +describe('fileDownloader', () => { + let fileDownloader: FileDownloader; + const mockAxiosInstance = { + get: vi.fn(), + } as any; + + beforeEach(() => { + fileDownloader = new FileDownloader(mockAxiosInstance); + }); + + it('should create an instance of FileDownloader', () => { + expect(fileDownloader).toBeInstanceOf(FileDownloader); + }); + + it('should download a file and return a Blob', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + mockAxiosInstance.get.mockResolvedValueOnce(mockResponse); + + const result = await fileDownloader.download(url); + + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { + method: 'GET', + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should merge provided config with default config', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + mockAxiosInstance.get.mockResolvedValueOnce(mockResponse); + + const customConfig: AxiosRequestConfig = { + headers: { 'Custom-Header': 'value' }, + }; + + const result = await fileDownloader.download(url, customConfig); + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.get).toHaveBeenCalledWith(url, { + ...customConfig, + method: 'GET', + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/file'; + mockAxiosInstance.get.mockRejectedValueOnce(new Error('Network Error')); + await expect(fileDownloader.download(url)).rejects.toThrow('Network Error'); + }); + + it('should handle empty URL gracefully', async () => { + const url = ''; + mockAxiosInstance.get.mockRejectedValueOnce( + new Error('Request failed with status code 404'), + ); + + await expect(fileDownloader.download(url)).rejects.toThrow( + 'Request failed with status code 404', + ); + }); + + it('should handle null URL gracefully', async () => { + const url = null as unknown as string; + mockAxiosInstance.get.mockRejectedValueOnce( + new Error('Request failed with status code 404'), + ); + + await expect(fileDownloader.download(url)).rejects.toThrow( + 'Request failed with status code 404', + ); + }); +}); + +describe('fileDownloader use other method', () => { + let fileDownloader: FileDownloader; + + it('should call request using get', async () => { + const url = 'https://example.com/file'; + const mockBlob = new Blob(['file content'], { type: 'text/plain' }); + const mockResponse: Blob = mockBlob; + + const mockAxiosInstance = { + request: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + + mockAxiosInstance.request.mockResolvedValueOnce(mockResponse); + + const result = await fileDownloader.download(url); + + expect(result).toBeInstanceOf(Blob); + expect(result).toEqual(mockBlob); + expect(mockAxiosInstance.request).toHaveBeenCalledWith(url, { + method: 'GET', + responseType: 'blob', + responseReturn: 'body', + }); + }); + + it('should call post', async () => { + const url = 'https://example.com/file'; + + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + + const customConfig: AxiosRequestConfig = { + method: 'POST', + data: { name: 'aa' }, + }; + + await fileDownloader.download(url, customConfig); + + expect(mockAxiosInstance.post).toHaveBeenCalledWith( + url, + { name: 'aa' }, + { + method: 'POST', + responseType: 'blob', + responseReturn: 'body', + }, + ); + }); + + it('should handle errors gracefully', async () => { + const url = 'https://example.com/file'; + const mockAxiosInstance = { + post: vi.fn(), + } as any; + + fileDownloader = new FileDownloader(mockAxiosInstance); + await expect(() => + fileDownloader.download(url, { method: 'postt' }), + ).rejects.toThrow( + 'RequestClient does not support method "POSTT". Please ensure the method is properly implemented in your RequestClient instance.', + ); + }); +}); diff --git a/packages/effects/request/src/request-client/modules/downloader.ts b/packages/effects/request/src/request-client/modules/downloader.ts new file mode 100644 index 000000000..6c3000589 --- /dev/null +++ b/packages/effects/request/src/request-client/modules/downloader.ts @@ -0,0 +1,60 @@ +import type { RequestClient } from '../request-client'; +import type { RequestClientConfig } from '../types'; + +type DownloadRequestConfig = { + /** + * 定义期望获得的数据类型。 + * raw: 原始的AxiosResponse,包括headers、status等。 + * body: 只返回响应数据的BODY部分(Blob) + */ + responseReturn?: 'body' | 'raw'; +} & Omit; + +class FileDownloader { + private client: RequestClient; + + constructor(client: RequestClient) { + this.client = client; + } + /** + * 下载文件 + * @param url 文件的完整链接 + * @param config 配置信息,可选。 + * @returns 如果config.responseReturn为'body',则返回Blob(默认),否则返回RequestResponse + */ + public async download( + url: string, + config?: DownloadRequestConfig, + ): Promise { + const finalConfig: DownloadRequestConfig = { + responseReturn: 'body', + method: 'GET', + ...config, + responseType: 'blob', + }; + + // Prefer a generic request if available; otherwise, dispatch to method-specific calls. + const method = (finalConfig.method || 'GET').toUpperCase(); + const clientAny = this.client as any; + + if (typeof clientAny.request === 'function') { + return await clientAny.request(url, finalConfig); + } + const lower = method.toLowerCase(); + + if (typeof clientAny[lower] === 'function') { + if (['POST', 'PUT'].includes(method)) { + const { data, ...rest } = finalConfig as Record; + return await clientAny[lower](url, data, rest); + } + + return await clientAny[lower](url, finalConfig); + } + + throw new Error( + `RequestClient does not support method "${method}". Please ensure the method is properly implemented in your RequestClient instance.`, + ); + } +} + +export { FileDownloader }; diff --git a/packages/effects/request/src/request-client/modules/sse.test.ts b/packages/effects/request/src/request-client/modules/sse.test.ts new file mode 100644 index 000000000..4e8c6a9d2 --- /dev/null +++ b/packages/effects/request/src/request-client/modules/sse.test.ts @@ -0,0 +1,142 @@ +import type { RequestClient } from '../request-client'; + +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { SSE } from './sse'; + +// 模拟 TextDecoder +const OriginalTextDecoder = globalThis.TextDecoder; + +beforeEach(() => { + vi.stubGlobal( + 'TextDecoder', + class { + private decoder = new OriginalTextDecoder(); + decode(value: Uint8Array, opts?: any) { + return this.decoder.decode(value, opts); + } + }, + ); +}); + +// 创建 fetch mock +const createFetchMock = (chunks: string[], ok = true) => { + const encoder = new TextEncoder(); + let index = 0; + return vi.fn().mockResolvedValue({ + ok, + status: ok ? 200 : 500, + body: { + getReader: () => ({ + read: async () => { + if (index < chunks.length) { + return { done: false, value: encoder.encode(chunks[index++]) }; + } + return { done: true, value: undefined }; + }, + }), + }, + }); +}; + +describe('sSE', () => { + let client: RequestClient; + let sse: SSE; + + beforeEach(() => { + vi.restoreAllMocks(); + client = { + getBaseUrl: () => 'http://localhost', + instance: { + interceptors: { + request: { + handlers: [], + }, + }, + }, + } as unknown as RequestClient; + sse = new SSE(client); + }); + + it('should call requestSSE when postSSE is used', async () => { + const spy = vi.spyOn(sse, 'requestSSE').mockResolvedValue(undefined); + await sse.postSSE('/test', { foo: 'bar' }, { headers: { a: '1' } }); + expect(spy).toHaveBeenCalledWith( + '/test', + { foo: 'bar' }, + { + headers: { a: '1' }, + method: 'POST', + }, + ); + }); + + it('should throw error if fetch response not ok', async () => { + vi.stubGlobal('fetch', createFetchMock([], false)); + await expect(sse.requestSSE('/bad')).rejects.toThrow( + 'HTTP error! status: 500', + ); + }); + + it('should trigger onMessage and onEnd callbacks', async () => { + const messages: string[] = []; + const onMessage = vi.fn((msg: string) => messages.push(msg)); + const onEnd = vi.fn(); + + vi.stubGlobal('fetch', createFetchMock(['hello', ' world'])); + + await sse.requestSSE('/sse', undefined, { onMessage, onEnd }); + + expect(onMessage).toHaveBeenCalledTimes(2); + expect(messages.join('')).toBe('hello world'); + // onEnd 不再带参数 + expect(onEnd).toHaveBeenCalled(); + }); + + it('should apply request interceptors', async () => { + const interceptor = vi.fn(async (config) => { + config.headers['x-test'] = 'intercepted'; + return config; + }); + (client.instance.interceptors.request as any).handlers.push({ + fulfilled: interceptor, + }); + + // 创建 fetch mock,并挂到全局 + const fetchMock = createFetchMock(['data']); + vi.stubGlobal('fetch', fetchMock); + + await sse.requestSSE('/sse', undefined, {}); + + expect(interceptor).toHaveBeenCalled(); + expect(fetchMock).toHaveBeenCalledWith( + 'http://localhost/sse', + expect.objectContaining({ + headers: expect.any(Headers), + }), + ); + + const calls = fetchMock.mock?.calls; + expect(calls).toBeDefined(); + expect(calls?.length).toBeGreaterThan(0); + + const init = calls?.[0]?.[1] as RequestInit; + expect(init).toBeDefined(); + + const headers = init?.headers as Headers; + expect(headers?.get('x-test')).toBe('intercepted'); + expect(headers?.get('accept')).toBe('text/event-stream'); + }); + + it('should throw error when no reader', async () => { + vi.stubGlobal( + 'fetch', + vi.fn().mockResolvedValue({ + ok: true, + status: 200, + body: null, + }), + ); + await expect(sse.requestSSE('/sse')).rejects.toThrow('No reader'); + }); +}); diff --git a/packages/effects/request/src/request-client/modules/sse.ts b/packages/effects/request/src/request-client/modules/sse.ts new file mode 100644 index 000000000..09d13017e --- /dev/null +++ b/packages/effects/request/src/request-client/modules/sse.ts @@ -0,0 +1,136 @@ +import type { AxiosRequestHeaders, InternalAxiosRequestConfig } from 'axios'; + +import type { RequestClient } from '../request-client'; +import type { SseRequestOptions } from '../types'; + +/** + * SSE模块 + */ +class SSE { + private client: RequestClient; + + constructor(client: RequestClient) { + this.client = client; + } + + public async postSSE( + url: string, + data?: any, + requestOptions?: SseRequestOptions, + ) { + return this.requestSSE(url, data, { + ...requestOptions, + method: 'POST', + }); + } + + /** + * SSE请求方法 + * @param url - 请求URL + * @param data - 请求数据 + * @param requestOptions - SSE请求选项 + */ + public async requestSSE( + url: string, + data?: any, + requestOptions?: SseRequestOptions, + ) { + const baseUrl = this.client.getBaseUrl() || ''; + + let axiosConfig: InternalAxiosRequestConfig = { + url, + method: (requestOptions?.method as any) ?? 'GET', + headers: {} as AxiosRequestHeaders, + }; + const requestInterceptors = this.client.instance.interceptors + .request as any; + if ( + requestInterceptors.handlers && + requestInterceptors.handlers.length > 0 + ) { + for (const handler of requestInterceptors.handlers) { + if (typeof handler?.fulfilled === 'function') { + const next = await handler.fulfilled(axiosConfig as any); + if (next) axiosConfig = next as InternalAxiosRequestConfig; + } + } + } + + const merged = new Headers(); + Object.entries( + (axiosConfig.headers ?? {}) as Record, + ).forEach(([k, v]) => merged.set(k, String(v))); + if (requestOptions?.headers) { + new Headers(requestOptions.headers).forEach((v, k) => merged.set(k, v)); + } + if (!merged.has('accept')) { + merged.set('accept', 'text/event-stream'); + } + + let bodyInit = requestOptions?.body ?? data; + const ct = (merged.get('content-type') || '').toLowerCase(); + if ( + bodyInit && + typeof bodyInit === 'object' && + !ArrayBuffer.isView(bodyInit as any) && + !(bodyInit instanceof ArrayBuffer) && + !(bodyInit instanceof Blob) && + !(bodyInit instanceof FormData) && + ct.includes('application/json') + ) { + bodyInit = JSON.stringify(bodyInit); + } + const requestInit: RequestInit = { + ...requestOptions, + method: axiosConfig.method, + headers: merged, + body: bodyInit, + }; + + const response = await fetch(safeJoinUrl(baseUrl, url), requestInit); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const reader = response.body?.getReader(); + const decoder = new TextDecoder(); + + if (!reader) { + throw new Error('No reader'); + } + let isEnd = false; + while (!isEnd) { + const { done, value } = await reader.read(); + if (done) { + isEnd = true; + decoder.decode(new Uint8Array(0), { stream: false }); + requestOptions?.onEnd?.(); + reader.releaseLock?.(); + break; + } + const content = decoder.decode(value, { stream: true }); + requestOptions?.onMessage?.(content); + } + } +} + +function safeJoinUrl(baseUrl: string | undefined, url: string): string { + if (!baseUrl) { + return url; // 没有 baseUrl,直接返回 url + } + + // 如果 url 本身就是绝对地址,直接返回 + if (/^https?:\/\//i.test(url)) { + return url; + } + + // 如果 baseUrl 是完整 URL,就用 new URL + if (/^https?:\/\//i.test(baseUrl)) { + return new URL(url, baseUrl).toString(); + } + + // 否则,当作路径拼接 + return `${baseUrl.replace(/\/+$/, '')}/${url.replace(/^\/+/, '')}`; +} + +export { SSE }; diff --git a/packages/effects/request/src/request-client/request-client.ts b/packages/effects/request/src/request-client/request-client.ts new file mode 100644 index 000000000..c764dda68 --- /dev/null +++ b/packages/effects/request/src/request-client/request-client.ts @@ -0,0 +1,182 @@ +import type { AxiosInstance, AxiosResponse } from 'axios'; + +import type { RequestClientConfig, RequestClientOptions } from './types'; + +import { bindMethods, isString, merge } from '@vben/utils'; + +import axios from 'axios'; +import qs from 'qs'; + +import { FileDownloader } from './modules/downloader'; +import { InterceptorManager } from './modules/interceptor'; +import { SSE } from './modules/sse'; +import { FileUploader } from './modules/uploader'; + +function getParamsSerializer( + paramsSerializer: RequestClientOptions['paramsSerializer'], +) { + if (isString(paramsSerializer)) { + switch (paramsSerializer) { + case 'brackets': { + return (params: any) => + qs.stringify(params, { arrayFormat: 'brackets' }); + } + case 'comma': { + return (params: any) => qs.stringify(params, { arrayFormat: 'comma' }); + } + case 'indices': { + return (params: any) => + qs.stringify(params, { arrayFormat: 'indices' }); + } + case 'repeat': { + return (params: any) => qs.stringify(params, { arrayFormat: 'repeat' }); + } + } + } + return paramsSerializer; +} + +class RequestClient { + public addRequestInterceptor: InterceptorManager['addRequestInterceptor']; + + public addResponseInterceptor: InterceptorManager['addResponseInterceptor']; + public download: FileDownloader['download']; + + public readonly instance: AxiosInstance; + // 是否正在刷新token + public isRefreshing = false; + public postSSE: SSE['postSSE']; + // 刷新token队列 + public refreshTokenQueue: ((token: string) => void)[] = []; + public requestSSE: SSE['requestSSE']; + public upload: FileUploader['upload']; + + /** + * 构造函数,用于创建Axios实例 + * @param options - Axios请求配置,可选 + */ + constructor(options: RequestClientOptions = {}) { + // 合并默认配置和传入的配置 + const defaultConfig: RequestClientOptions = { + headers: { + 'Content-Type': 'application/json;charset=utf-8', + }, + responseReturn: 'raw', + // 默认超时时间 + timeout: 10_000, + paramsSerializer: 'repeat', + }; + const { ...axiosConfig } = options; + const requestConfig = merge(axiosConfig, defaultConfig); + requestConfig.paramsSerializer = getParamsSerializer( + requestConfig.paramsSerializer, + ); + this.instance = axios.create(requestConfig); + + bindMethods(this); + + // 实例化拦截器管理器 + const interceptorManager = new InterceptorManager(this.instance); + this.addRequestInterceptor = + interceptorManager.addRequestInterceptor.bind(interceptorManager); + this.addResponseInterceptor = + interceptorManager.addResponseInterceptor.bind(interceptorManager); + + // 实例化文件上传器 + const fileUploader = new FileUploader(this); + this.upload = fileUploader.upload.bind(fileUploader); + // 实例化文件下载器 + const fileDownloader = new FileDownloader(this); + this.download = fileDownloader.download.bind(fileDownloader); + // 实例化SSE模块 + const sse = new SSE(this); + this.postSSE = sse.postSSE.bind(sse); + this.requestSSE = sse.requestSSE.bind(sse); + } + + /** + * DELETE请求方法 + */ + public delete( + url: string, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, method: 'DELETE' }); + } + + /** + * GET请求方法 + */ + public get(url: string, config?: RequestClientConfig): Promise { + return this.request(url, { ...config, method: 'GET' }); + } + + /** + * 获取基础URL + */ + public getBaseUrl() { + return this.instance.defaults.baseURL; + } + + /** + * POST请求方法 + */ + public post( + url: string, + data?: any, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, data, method: 'POST' }); + } + + /** + * PUT请求方法 + */ + public put( + url: string, + data?: any, + config?: RequestClientConfig, + ): Promise { + return this.request(url, { ...config, data, method: 'PUT' }); + } + + /** + * 通用的请求方法 + */ + public async request( + url: string, + config: RequestClientConfig, + ): Promise { + try { + const response: AxiosResponse = await this.instance({ + url, + ...config, + ...(config.paramsSerializer + ? { paramsSerializer: getParamsSerializer(config.paramsSerializer) } + : {}), + }); + return response as T; + } catch (error: any) { + throw error.response ? error.response.data : error; + } + } +} + +/** + * 构建排序字段,处理 vxe 排序条件 + * + * add by 芋艿 + */ +export const buildSortingField = (sorts: any[]) => { + if (!sorts || sorts.length === 0) { + return {}; + } + const result: Record = {}; + sorts.forEach((sort: any, index: number) => { + result[`sortingFields[${index}].field`] = sort.field; + result[`sortingFields[${index}].order`] = sort.order; + }); + return result; +}; + +export { RequestClient }; diff --git a/packages/effects/request/src/request-client/types.ts b/packages/effects/request/src/request-client/types.ts new file mode 100644 index 000000000..c1722b852 --- /dev/null +++ b/packages/effects/request/src/request-client/types.ts @@ -0,0 +1,103 @@ +import type { + AxiosRequestConfig, + AxiosResponse, + CreateAxiosDefaults, + InternalAxiosRequestConfig, +} from 'axios'; + +type ExtendOptions = { + /** + * 参数序列化方式。预置的有 + * - brackets: ids[]=1&ids[]=2&ids[]=3 + * - comma: ids=1,2,3 + * - indices: ids[0]=1&ids[1]=2&ids[2]=3 + * - repeat: ids=1&ids=2&ids=3 + */ + paramsSerializer?: + | 'brackets' + | 'comma' + | 'indices' + | 'repeat' + | AxiosRequestConfig['paramsSerializer']; + /** + * 响应数据的返回方式。 + * - raw: 原始的AxiosResponse,包括headers、status等,不做是否成功请求的检查。 + * - body: 返回响应数据的BODY部分(只会根据status检查请求是否成功,忽略对code的判断,这种情况下应由调用方检查请求是否成功)。 + * - data: 解构响应的BODY数据,只返回其中的data节点数据(会检查status和code是否为成功状态)。 + */ + responseReturn?: 'body' | 'data' | 'raw'; +}; +type RequestClientConfig = AxiosRequestConfig & ExtendOptions; + +type RequestResponse = AxiosResponse & { + config: RequestClientConfig; +}; + +type RequestContentType = + | 'application/json;charset=utf-8' + | 'application/octet-stream;charset=utf-8' + | 'application/x-www-form-urlencoded;charset=utf-8' + | 'multipart/form-data;charset=utf-8'; + +type RequestClientOptions = CreateAxiosDefaults & ExtendOptions; + +/** + * SSE 请求选项 + */ +interface SseRequestOptions extends RequestInit { + onMessage?: (message: string) => void; + onEnd?: () => void; +} + +interface RequestInterceptorConfig { + fulfilled?: ( + config: ExtendOptions & InternalAxiosRequestConfig, + ) => + | (ExtendOptions & InternalAxiosRequestConfig) + | Promise>; + rejected?: (error: any) => any; +} + +interface ResponseInterceptorConfig { + fulfilled?: ( + response: RequestResponse, + ) => Promise | RequestResponse; + rejected?: (error: any) => any; +} + +type MakeErrorMessageFn = (message: string, error: any) => void; + +interface HttpResponse { + /** + * 0 表示成功 其他表示失败 + * 0 means success, others means fail + */ + code: number; + data: T; + msg: string; +} + +interface PageParam { + [key: string]: any; + pageNo: number; + pageSize: number; +} + +interface PageResult { + list: T[]; + total: number; +} + +export type { + HttpResponse, + MakeErrorMessageFn, + PageParam, + PageResult, + RequestClientConfig, + RequestClientOptions, + RequestContentType, + RequestInterceptorConfig, + RequestResponse, + ResponseInterceptorConfig, + SseRequestOptions, +}; diff --git a/packages/icons/package.json b/packages/icons/package.json new file mode 100644 index 000000000..77fa0c4f1 --- /dev/null +++ b/packages/icons/package.json @@ -0,0 +1,22 @@ +{ + "name": "@vben/icons", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/icons" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/icons": "workspace:*" + } +} diff --git a/packages/icons/src/iconify/index.ts b/packages/icons/src/iconify/index.ts new file mode 100644 index 000000000..f4f2222b9 --- /dev/null +++ b/packages/icons/src/iconify/index.ts @@ -0,0 +1,67 @@ +import { createIconifyIcon } from '@vben-core/icons'; + +export * from '@vben-core/icons'; + +export const MdiKeyboardEsc = createIconifyIcon('mdi:keyboard-esc'); + +export const MdiWechat = createIconifyIcon('mdi:wechat'); + +export const MdiGithub = createIconifyIcon('mdi:github'); + +export const MdiGoogle = createIconifyIcon('mdi:google'); + +export const MdiQqchat = createIconifyIcon('mdi:qqchat'); + +export const MdiCheckboxMarkedCircleOutline = createIconifyIcon( + 'mdi:checkbox-marked-circle-outline', +); + +export const MsRefresh = createIconifyIcon('material-symbols:refresh-rounded'); + +export const TMinimize = createIconifyIcon('tabler:arrows-minimize'); + +export const AntdProfileOutlined = createIconifyIcon( + 'ant-design:profile-outlined', +); + +export const FolderOpenOutlined = createIconifyIcon( + 'ant-design:folder-open-outlined', +); + +export const DownloadOutlined = createIconifyIcon( + 'ant-design:download-outlined', +); + +export const EyeOutlined = createIconifyIcon('ant-design:eye-outlined'); + +export const ApiOutlined = createIconifyIcon('ant-design:api-outlined'); + +export const ZoomOutOutlined = createIconifyIcon( + 'ant-design:zoom-out-outlined', +); + +export const ZoomInOutlined = createIconifyIcon('ant-design:zoom-in-outlined'); + +export const UndoOutlined = createIconifyIcon('ant-design:undo-outlined'); + +export const RedoOutlined = createIconifyIcon('ant-design:redo-outlined'); + +export const ReloadOutlined = createIconifyIcon('ant-design:reload-outlined'); + +export const AlignLeftOutlined = createIconifyIcon( + 'ant-design:align-left-outlined', +); + +export const WarningOutlined = createIconifyIcon('ant-design:warning-outlined'); + +export const RiDingding = createIconifyIcon('ri:dingding-fill'); + +export const MenuOutlined = createIconifyIcon('ant-design:menu-outlined'); + +export const PlusOutlined = createIconifyIcon('ant-design:plus-outlined'); + +export const CloseCircleFilled = createIconifyIcon( + 'ant-design:close-circle-filled', +); + +export const SelectOutlined = createIconifyIcon('ant-design:select-outlined'); diff --git a/packages/icons/src/svg/icons/ai/gpt.svg b/packages/icons/src/svg/icons/ai/gpt.svg new file mode 100644 index 000000000..2625ec012 --- /dev/null +++ b/packages/icons/src/svg/icons/ai/gpt.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/icons/bpm/bpm-approve.svg b/packages/icons/src/svg/icons/bpm/bpm-approve.svg new file mode 100644 index 000000000..06aa09d6d --- /dev/null +++ b/packages/icons/src/svg/icons/bpm/bpm-approve.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/bpm/bpm-cancel.svg b/packages/icons/src/svg/icons/bpm/bpm-cancel.svg new file mode 100644 index 000000000..ab9b1553b --- /dev/null +++ b/packages/icons/src/svg/icons/bpm/bpm-cancel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/bpm/bpm-reject.svg b/packages/icons/src/svg/icons/bpm/bpm-reject.svg new file mode 100644 index 000000000..21fd5f60e --- /dev/null +++ b/packages/icons/src/svg/icons/bpm/bpm-reject.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/bpm/bpm-running.svg b/packages/icons/src/svg/icons/bpm/bpm-running.svg new file mode 100644 index 000000000..5908c13b2 --- /dev/null +++ b/packages/icons/src/svg/icons/bpm/bpm-running.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/dingding.svg b/packages/icons/src/svg/icons/dingding.svg new file mode 100644 index 000000000..c2fc786b7 --- /dev/null +++ b/packages/icons/src/svg/icons/dingding.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/icons/github.svg b/packages/icons/src/svg/icons/github.svg new file mode 100644 index 000000000..39d092d9f --- /dev/null +++ b/packages/icons/src/svg/icons/github.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/icons/google.svg b/packages/icons/src/svg/icons/google.svg new file mode 100644 index 000000000..f2ad0a403 --- /dev/null +++ b/packages/icons/src/svg/icons/google.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/icons/pay/alipay-app.svg b/packages/icons/src/svg/icons/pay/alipay-app.svg new file mode 100644 index 000000000..ebf11883e --- /dev/null +++ b/packages/icons/src/svg/icons/pay/alipay-app.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/alipay-bar.svg b/packages/icons/src/svg/icons/pay/alipay-bar.svg new file mode 100644 index 000000000..eb1e1e848 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/alipay-bar.svg @@ -0,0 +1,2 @@ + diff --git a/packages/icons/src/svg/icons/pay/alipay-pc.svg b/packages/icons/src/svg/icons/pay/alipay-pc.svg new file mode 100644 index 000000000..2a7527703 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/alipay-pc.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/alipay-qr.svg b/packages/icons/src/svg/icons/pay/alipay-qr.svg new file mode 100644 index 000000000..48337508a --- /dev/null +++ b/packages/icons/src/svg/icons/pay/alipay-qr.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/alipay-wap.svg b/packages/icons/src/svg/icons/pay/alipay-wap.svg new file mode 100644 index 000000000..87075dbb7 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/alipay-wap.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/mock.svg b/packages/icons/src/svg/icons/pay/mock.svg new file mode 100644 index 000000000..e0a685719 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/mock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/wallet.svg b/packages/icons/src/svg/icons/pay/wallet.svg new file mode 100644 index 000000000..27b09ead5 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/wallet.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/wx-app.svg b/packages/icons/src/svg/icons/pay/wx-app.svg new file mode 100644 index 000000000..ad40b2a2c --- /dev/null +++ b/packages/icons/src/svg/icons/pay/wx-app.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/wx-bar.svg b/packages/icons/src/svg/icons/pay/wx-bar.svg new file mode 100644 index 000000000..11292e6e7 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/wx-bar.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/wx-lite.svg b/packages/icons/src/svg/icons/pay/wx-lite.svg new file mode 100644 index 000000000..0c925cf39 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/wx-lite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/wx-native.svg b/packages/icons/src/svg/icons/pay/wx-native.svg new file mode 100644 index 000000000..bf3ba2b61 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/wx-native.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/pay/wx-pub.svg b/packages/icons/src/svg/icons/pay/wx-pub.svg new file mode 100644 index 000000000..3a6d15b70 --- /dev/null +++ b/packages/icons/src/svg/icons/pay/wx-pub.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/packages/icons/src/svg/icons/qqchat.svg b/packages/icons/src/svg/icons/qqchat.svg new file mode 100644 index 000000000..ac9a7f030 --- /dev/null +++ b/packages/icons/src/svg/icons/qqchat.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/icons/tdesign-logo.svg b/packages/icons/src/svg/icons/tdesign-logo.svg new file mode 100644 index 000000000..bdbba5cd9 --- /dev/null +++ b/packages/icons/src/svg/icons/tdesign-logo.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/icons/src/svg/icons/wechat.svg b/packages/icons/src/svg/icons/wechat.svg new file mode 100644 index 000000000..346b0379a --- /dev/null +++ b/packages/icons/src/svg/icons/wechat.svg @@ -0,0 +1 @@ + diff --git a/packages/icons/src/svg/index.ts b/packages/icons/src/svg/index.ts new file mode 100644 index 000000000..1e81686c6 --- /dev/null +++ b/packages/icons/src/svg/index.ts @@ -0,0 +1,77 @@ +import { createIconifyIcon } from '@vben-core/icons'; + +import './load.js'; + +const SvgAvatar1Icon = createIconifyIcon('svg:avatar-1'); +const SvgAvatar2Icon = createIconifyIcon('svg:avatar-2'); +const SvgAvatar3Icon = createIconifyIcon('svg:avatar-3'); +const SvgAvatar4Icon = createIconifyIcon('svg:avatar-4'); +const SvgDownloadIcon = createIconifyIcon('svg:download'); +const SvgCardIcon = createIconifyIcon('svg:card'); +const SvgBellIcon = createIconifyIcon('svg:bell'); +const SvgCakeIcon = createIconifyIcon('svg:cake'); +const SvgAntdvLogoIcon = createIconifyIcon('svg:antdv-logo'); +const SvgGithubIcon = createIconifyIcon('svg:github'); +const SvgGoogleIcon = createIconifyIcon('svg:google'); +const SvgQQChatIcon = createIconifyIcon('svg:qqchat'); +const SvgWeChatIcon = createIconifyIcon('svg:wechat'); +const SvgDingDingIcon = createIconifyIcon('svg:dingding'); +const SvgTDesignIcon = createIconifyIcon('svg:tdesign-logo'); + +/** AI */ +const SvgGptIcon = createIconifyIcon('svg:gpt'); + +/** 支付 */ +const SvgAlipayPcIcon = createIconifyIcon('svg:alipay-pc'); +const SvgAlipayWapIcon = createIconifyIcon('svg:alipay-wap'); +const SvgAlipayAppIcon = createIconifyIcon('svg:alipay-app'); +const SvgAlipayQrIcon = createIconifyIcon('svg:alipay-qr'); +const SvgAlipayBarIcon = createIconifyIcon('svg:alipay-bar'); +const SvgWxPubIcon = createIconifyIcon('svg:wx-pub'); +const SvgWxLiteIcon = createIconifyIcon('svg:wx-lite'); +const SvgWxAppIcon = createIconifyIcon('svg:wx-app'); +const SvgWxNativeIcon = createIconifyIcon('svg:wx-native'); +const SvgWxBarIcon = createIconifyIcon('svg:wx-bar'); +const SvgWalletIcon = createIconifyIcon('svg:wallet'); +const SvgMockIcon = createIconifyIcon('svg:mock'); + +/** BPM */ +const SvgBpmRunningIcon = createIconifyIcon('svg:bpm-running'); +const SvgBpmApproveIcon = createIconifyIcon('svg:bpm-approve'); +const SvgBpmRejectIcon = createIconifyIcon('svg:bpm-reject'); +const SvgBpmCancelIcon = createIconifyIcon('svg:bpm-cancel'); + +export { + SvgAlipayAppIcon, + SvgAlipayBarIcon, + SvgAlipayPcIcon, + SvgAlipayQrIcon, + SvgAlipayWapIcon, + SvgAntdvLogoIcon, + SvgAvatar1Icon, + SvgAvatar2Icon, + SvgAvatar3Icon, + SvgAvatar4Icon, + SvgBellIcon, + SvgBpmApproveIcon, + SvgBpmCancelIcon, + SvgBpmRejectIcon, + SvgBpmRunningIcon, + SvgCakeIcon, + SvgCardIcon, + SvgDingDingIcon, + SvgDownloadIcon, + SvgGithubIcon, + SvgGoogleIcon, + SvgGptIcon, + SvgMockIcon, + SvgQQChatIcon, + SvgTDesignIcon, + SvgWalletIcon, + SvgWeChatIcon, + SvgWxAppIcon, + SvgWxBarIcon, + SvgWxLiteIcon, + SvgWxNativeIcon, + SvgWxPubIcon, +}; diff --git a/packages/locales/package.json b/packages/locales/package.json new file mode 100644 index 000000000..7d479159b --- /dev/null +++ b/packages/locales/package.json @@ -0,0 +1,28 @@ +{ + "name": "@vben/locales", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/locales" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@intlify/core-base": "catalog:", + "@vben-core/composables": "workspace:*", + "vue": "catalog:", + "vue-i18n": "catalog:" + } +} diff --git a/packages/locales/src/i18n.ts b/packages/locales/src/i18n.ts new file mode 100644 index 000000000..6378f2c59 --- /dev/null +++ b/packages/locales/src/i18n.ts @@ -0,0 +1,147 @@ +import type { App } from 'vue'; +import type { Locale } from 'vue-i18n'; + +import type { + ImportLocaleFn, + LoadMessageFn, + LocaleSetupOptions, + SupportedLanguagesType, +} from './typing'; + +import { unref } from 'vue'; +import { createI18n } from 'vue-i18n'; + +import { useSimpleLocale } from '@vben-core/composables'; + +const i18n = createI18n({ + globalInjection: true, + legacy: false, + locale: '', + messages: {}, +}); + +const modules = import.meta.glob('./langs/**/*.json'); + +const { setSimpleLocale } = useSimpleLocale(); + +const localesMap = loadLocalesMapFromDir( + /\.\/langs\/([^/]+)\/(.*)\.json$/, + modules, +); +let loadMessages: LoadMessageFn; + +/** + * Load locale modules + * @param modules + */ +function loadLocalesMap(modules: Record Promise>) { + const localesMap: Record = {}; + + for (const [path, loadLocale] of Object.entries(modules)) { + const key = path.match(/([\w-]*)\.(json)/)?.[1]; + if (key) { + localesMap[key] = loadLocale as ImportLocaleFn; + } + } + return localesMap; +} + +/** + * Load locale modules with directory structure + * @param regexp - Regular expression to match language and file names + * @param modules - The modules object containing paths and import functions + * @returns A map of locales to their corresponding import functions + */ +function loadLocalesMapFromDir( + regexp: RegExp, + modules: Record Promise>, +): Record { + const localesRaw: Record Promise>> = {}; + const localesMap: Record = {}; + + // Iterate over the modules to extract language and file names + for (const path in modules) { + const match = path.match(regexp); + if (match) { + const [_, locale, fileName] = match; + if (locale && fileName) { + if (!localesRaw[locale]) { + localesRaw[locale] = {}; + } + if (modules[path]) { + localesRaw[locale][fileName] = modules[path]; + } + } + } + } + + // Convert raw locale data into async import functions + for (const [locale, files] of Object.entries(localesRaw)) { + localesMap[locale] = async () => { + const messages: Record = {}; + for (const [fileName, importFn] of Object.entries(files)) { + messages[fileName] = ((await importFn()) as any)?.default; + } + return { default: messages }; + }; + } + + return localesMap; +} + +/** + * Set i18n language + * @param locale + */ +function setI18nLanguage(locale: Locale) { + i18n.global.locale.value = locale; + + document?.querySelector('html')?.setAttribute('lang', locale); +} + +async function setupI18n(app: App, options: LocaleSetupOptions = {}) { + const { defaultLocale = 'zh-CN' } = options; + // app可以自行扩展一些第三方库和组件库的国际化 + loadMessages = options.loadMessages || (async () => ({})); + app.use(i18n); + await loadLocaleMessages(defaultLocale); + + // 在控制台打印警告 + i18n.global.setMissingHandler((locale, key) => { + if (options.missingWarn && key !== 'OAuth 2.0' && key.includes('.')) { + console.warn( + `[intlify] Not found '${key}' key in '${locale}' locale messages.`, + ); + } + }); +} + +/** + * Load locale messages + * @param lang + */ +async function loadLocaleMessages(lang: SupportedLanguagesType) { + if (unref(i18n.global.locale) === lang) { + return setI18nLanguage(lang); + } + setSimpleLocale(lang); + + const message = await localesMap[lang]?.(); + + if (message?.default) { + i18n.global.setLocaleMessage(lang, message.default); + } + + const mergeMessage = await loadMessages(lang); + i18n.global.mergeLocaleMessage(lang, mergeMessage); + + return setI18nLanguage(lang); +} + +export { + i18n, + loadLocaleMessages, + loadLocalesMap, + loadLocalesMapFromDir, + setupI18n, +}; diff --git a/packages/locales/src/langs/en-US/authentication.json b/packages/locales/src/langs/en-US/authentication.json new file mode 100644 index 000000000..d1fd9b77d --- /dev/null +++ b/packages/locales/src/langs/en-US/authentication.json @@ -0,0 +1,70 @@ +{ + "welcomeBack": "Welcome Back", + "pageTitle": "Plug-and-play Admin system", + "pageDesc": "Efficient, versatile frontend template", + "loginSuccess": "Login Successful", + "loginSuccessDesc": "Welcome Back", + "loginSubtitle": "Enter your account details to manage your projects", + "selectAccount": "Quick Select Account", + "username": "Username", + "password": "Password", + "nickname": "Nickname", + "tenant": "Tenant", + "usernameTip": "Please enter username", + "passwordErrorTip": "Password is incorrect", + "passwordTip": "Please enter password", + "nicknameTip": "Please enter nickname", + "tenantTip": "Please select tenant", + "verifyRequiredTip": "Please complete the verification first", + "rememberMe": "Remember Me", + "createAnAccount": "Create an Account", + "createAccount": "Create Account", + "alreadyHaveAccount": "Already have an account?", + "accountTip": "Don't have an account?", + "signUp": "Sign Up", + "signUpSubtitle": "Make managing your applications simple and fun", + "confirmPassword": "Confirm Password", + "confirmPasswordTip": "The passwords do not match", + "agree": "I agree to", + "privacyPolicy": "Privacy-policy", + "terms": "Terms", + "agreeTip": "Please agree to the Privacy Policy and Terms", + "goToLogin": "Login instead", + "passwordStrength": "Use 8 or more characters with a mix of letters, numbers & symbols", + "forgetPassword": "Forget Password?", + "forgetPasswordSubtitle": "Enter your email and we'll send you instructions to reset your password", + "resetPasswordSuccess": "Reset password success", + "emailTip": "Please enter email", + "emailValidErrorTip": "The email format you entered is incorrect", + "resetPassword": "Reset Password", + "email": "Email", + "qrcodeSubtitle": "Scan the QR code with your phone to login", + "qrcodePrompt": "Click 'Confirm' after scanning to complete login", + "qrcodeLogin": "QR Code Login", + "wechatLogin": "Wechat Login", + "qqLogin": "QQ Login", + "githubLogin": "Github Login", + "googleLogin": "Google Login", + "dingdingLogin": "Dingding Login", + "codeSubtitle": "Enter your phone number to start managing your project", + "code": "Security code", + "codeTip": "Security code required {0} characters", + "mobile": "Mobile", + "mobileLogin": "Mobile Login", + "mobileTip": "Please enter mobile number", + "mobileErrortip": "The phone number format is incorrect", + "sendCode": "Get Security code", + "sendText": "Resend in {0}s", + "thirdPartyLogin": "Or continue with", + "weChat": "WeChat", + "qq": "QQ", + "gitHub": "GitHub", + "google": "Google", + "loginAgainTitle": "Please Log In Again", + "loginAgainSubTitle": "Your login session has expired. Please log in again to continue.", + "layout": { + "center": "Align Center", + "alignLeft": "Align Left", + "alignRight": "Align Right" + } +} diff --git a/packages/locales/src/langs/en-US/preferences.json b/packages/locales/src/langs/en-US/preferences.json new file mode 100644 index 000000000..b0bbe3741 --- /dev/null +++ b/packages/locales/src/langs/en-US/preferences.json @@ -0,0 +1,192 @@ +{ + "title": "Preferences", + "subtitle": "Customize Preferences & Preview in Real Time", + "enableStickyPreferencesNavigationBar": "Enable sticky preferences navigation bar", + "disableStickyPreferencesNavigationBar": "Disable sticky preferences navigation bar", + "resetTip": "Data has changed, click to reset", + "resetTitle": "Reset Preferences", + "resetSuccess": "Preferences reset successfully", + "appearance": "Appearance", + "layout": "Layout", + "content": "Content", + "other": "Other", + "wide": "Wide", + "compact": "Fixed", + "followSystem": "Follow System", + "vertical": "Vertical", + "verticalTip": "Side vertical menu mode", + "horizontal": "Horizontal", + "horizontalTip": "Horizontal menu mode, all menus displayed at the top", + "twoColumn": "Two Column", + "twoColumnTip": "Vertical Two Column Menu Mode", + "headerSidebarNav": "Header Vertical", + "headerSidebarNavTip": "Header Full Width, Sidebar Navigation Mode", + "headerTwoColumn": "Header Two Column", + "headerTwoColumnTip": "Header Navigation & Sidebar Two Column co-exists", + "mixedMenu": "Mixed Menu", + "mixedMenuTip": "Vertical & Horizontal Menu Co-exists", + "fullContent": "Full Content", + "fullContentTip": "Only display content body, hide all menus", + "normal": "Normal", + "plain": "Plain", + "rounded": "Rounded", + "copyPreferences": "Copy Preferences", + "copyPreferencesSuccessTitle": "Copy successful", + "copyPreferencesSuccess": "Copy successful, please override in `src/preferences.ts` under app", + "clearAndLogout": "Clear Cache & Logout", + "mode": "Mode", + "general": "General", + "language": "Language", + "dynamicTitle": "Dynamic Title", + "watermark": "Watermark", + "watermarkContent": "Please input Watermark content", + "checkUpdates": "Periodic update check", + "position": { + "title": "Preferences Postion", + "header": "Header", + "auto": "Auto", + "fixed": "Fixed" + }, + "sidebar": { + "buttons": "Show Buttons", + "buttonFixed": "Fixed", + "buttonCollapsed": "Collapsed", + "title": "Sidebar", + "width": "Width", + "visible": "Show Sidebar", + "collapsed": "Collpase Menu", + "collapsedShowTitle": "Show Menu Title", + "autoActivateChild": "Auto Activate SubMenu", + "autoActivateChildTip": "`Enabled` to automatically activate the submenu while click menu.", + "expandOnHover": "Expand On Hover", + "expandOnHoverTip": "When the mouse hovers over menu, \n `Enabled` to expand children menus \n `Disabled` to expand whole sidebar." + }, + "tabbar": { + "title": "Tabbar", + "enable": "Enable Tab Bar", + "icon": "Show Tabbar Icon", + "showMore": "Show More Button", + "showMaximize": "Show Maximize Button", + "persist": "Persist Tabs", + "maxCount": "Max Count of Tabs", + "maxCountTip": "When the number of tabs exceeds the maximum,\nthe oldest tab will be closed.\n Set to 0 to disable count checking.", + "draggable": "Enable Draggable Sort", + "wheelable": "Support Mouse Wheel", + "middleClickClose": "Close Tab when Mouse Middle Button Click", + "wheelableTip": "When enabled, the Tabbar area responds to vertical scrolling events of the scroll wheel.", + "styleType": { + "title": "Tabs Style", + "chrome": "Chrome", + "card": "Card", + "plain": "Plain", + "brisk": "Brisk" + }, + "contextMenu": { + "reload": "Reload", + "close": "Close", + "pin": "Pin", + "unpin": "Unpin", + "closeLeft": "Close Left Tabs", + "closeRight": "Close Right Tabs", + "closeOther": "Close Other Tabs", + "closeAll": "Close All Tabs", + "openInNewWindow": "Open in New Window", + "maximize": "Maximize", + "restoreMaximize": "Restore" + } + }, + "navigationMenu": { + "title": "Navigation Menu", + "style": "Navigation Menu Style", + "accordion": "Sidebar Accordion Menu", + "split": "Navigation Menu Separation", + "splitTip": "When enabled, the sidebar displays the top bar's submenu" + }, + "breadcrumb": { + "title": "Breadcrumb", + "home": "Show Home Button", + "enable": "Enable Breadcrumb", + "icon": "Show Breadcrumb Icon", + "background": "background", + "style": "Breadcrumb Style", + "hideOnlyOne": "Hidden when only one" + }, + "animation": { + "title": "Animation", + "loading": "Page Loading", + "transition": "Page Transition", + "progress": "Page Progress" + }, + "theme": { + "title": "Theme", + "radius": "Radius", + "light": "Light", + "dark": "Dark", + "darkSidebar": "Semi Dark Sidebar", + "darkHeader": "Semi Dark Header", + "weakMode": "Weak Mode", + "grayMode": "Gray Mode", + "builtin": { + "title": "Built-in", + "default": "Default", + "violet": "Violet", + "pink": "Pink", + "rose": "Rose", + "skyBlue": "Sky Blue", + "deepBlue": "Deep Blue", + "green": "Green", + "deepGreen": "Deep Green", + "orange": "Orange", + "yellow": "Yellow", + "zinc": "Zinc", + "neutral": "Neutral", + "slate": "Slate", + "gray": "Gray", + "custom": "Custom" + } + }, + "header": { + "title": "Header", + "visible": "Show Header", + "modeStatic": "Static", + "modeFixed": "Fixed", + "modeAuto": "Auto hide & Show", + "modeAutoScroll": "Scroll to Hide & Show", + "menuAlign": "Menu Align", + "menuAlignStart": "Start", + "menuAlignEnd": "End", + "menuAlignCenter": "Center" + }, + "footer": { + "title": "Footer", + "visible": "Show Footer", + "fixed": "Fixed at Bottom" + }, + "copyright": { + "title": "Copyright", + "enable": "Enable Copyright", + "companyName": "Company Name", + "companySiteLink": "Company Site Link", + "date": "Date", + "icp": "ICP License Number", + "icpLink": "ICP Site Link" + }, + "shortcutKeys": { + "title": "Shortcut Keys", + "global": "Global", + "search": "Global Search", + "logout": "Logout", + "preferences": "Preferences" + }, + "widget": { + "title": "Widget", + "globalSearch": "Enable Global Search", + "fullscreen": "Enable Fullscreen", + "themeToggle": "Enable Theme Toggle", + "languageToggle": "Enable Language Toggle", + "notification": "Enable Notification", + "sidebarToggle": "Enable Sidebar Toggle", + "lockScreen": "Enable Lock Screen", + "refresh": "Enable Refresh" + } +} diff --git a/packages/locales/src/langs/en-US/ui.json b/packages/locales/src/langs/en-US/ui.json new file mode 100644 index 000000000..08722ab81 --- /dev/null +++ b/packages/locales/src/langs/en-US/ui.json @@ -0,0 +1,155 @@ +{ + "formRules": { + "required": "Please enter {0}", + "selectRequired": "Please select {0}", + "minLength": "{0} must be at least {1} characters", + "maxLength": "{0} can be at most {1} characters", + "length": "{0} must be {1} characters long", + "alreadyExists": "{0} `{1}` already exists", + "startWith": "{0} must start with `{1}`", + "invalidURL": "Please input a valid URL", + "mobile": "Please input a valid {0}" + }, + "actionTitle": { + "copy": "Copy {0}", + "cancel": "Cancel {0}", + "edit": "Modify {0}", + "create": "Create {0}", + "delete": "Delete {0}", + "deleteBatch": "Delete Batch", + "detail": "Detail {0}", + "view": "View {0}", + "import": "Import", + "export": "Export" + }, + "actionMessage": { + "deleteConfirm": "Are you sure to delete {0}?", + "deleteBatchConfirm": "Are you sure to delete selected items?", + "deleting": "Deleting {0} ...", + "deletingBatch": "Deleting selected ...", + "deleteSuccess": "{0} deleted successfully", + "deleteFailed": "{0} deleted failed", + "operationSuccess": "Operation succeeded", + "operationFailed": "Operation failed", + "importSuccess": "Import succeeded", + "importFail": "Import failed", + "downloadTemplateFail": "Download template failed", + "updating": "Updating {0}...", + "updateSuccess": "Update {0} successfully", + "updateFailed": "Update {0} failed" + }, + "placeholder": { + "input": "Please enter", + "select": "Please select" + }, + "captcha": { + "title": "Please complete the security verification", + "sliderSuccessText": "Passed", + "sliderDefaultText": "Slider and drag", + "alt": "Supports img tag src attribute value", + "sliderRotateDefaultTip": "Click picture to refresh", + "sliderTranslateDefaultTip": "Click picture to refresh", + "sliderRotateFailTip": "Validation failed", + "sliderRotateSuccessTip": "Validation successful, time {0} seconds", + "sliderTranslateFailTip": "Validation failed", + "sliderTranslateSuccessTip": "Validation successful, time {0} seconds", + "refreshAriaLabel": "Refresh captcha", + "confirmAriaLabel": "Confirm selection", + "confirm": "Confirm", + "pointAriaLabel": "Click point", + "clickInOrder": "Please click in order" + }, + "iconPicker": { + "placeholder": "Select an icon", + "search": "Search icon..." + }, + "jsonViewer": { + "copy": "Copy", + "copied": "Copied" + }, + "fallback": { + "pageNotFound": "Oops! Page Not Found", + "pageNotFoundDesc": "Sorry, we couldn't find the page you were looking for.", + "forbidden": "Oops! Access Denied", + "forbiddenDesc": "Sorry, but you don't have permission to access this page.", + "internalError": "Oops! Something Went Wrong", + "internalErrorDesc": "Sorry, but the server encountered an error.", + "offline": "Offline Page", + "offlineError": "Oops! Network Error", + "offlineErrorDesc": "Sorry, can't connect to the internet. Check your connection.", + "comingSoon": "Coming Soon", + "http": { + "requestTimeout": "The request timed out. Please try again later.", + "networkError": "A network error occurred. Please check your internet connection and try again.", + "badRequest": "Bad Request. Please check your input and try again.", + "unauthorized": "Unauthorized. Please log in to continue.", + "forbidden": "Forbidden. You do not have permission to access this resource.", + "notFound": "Not Found. The requested resource could not be found.", + "internalServerError": "Internal Server Error. Something went wrong on our end. Please try again later." + } + }, + "widgets": { + "document": "Document", + "profile": "Profile", + "qa": "Q&A", + "setting": "Settings", + "logoutTip": "Do you want to logout?", + "viewAll": "View All Messages", + "notifications": "Notifications", + "markAllAsRead": "Make All as Read", + "clearNotifications": "Clear", + "checkUpdatesTitle": "New Version Available", + "checkUpdatesDescription": "Click to refresh and get the latest version", + "search": { + "title": "Search", + "searchNavigate": "Search Navigation", + "select": "Select", + "navigate": "Navigate", + "close": "Close", + "noResults": "No Search Results Found", + "noRecent": "No Search History", + "recent": "Search History" + }, + "lockScreen": { + "title": "Lock Screen", + "screenButton": "Locking", + "password": "Password", + "placeholder": "Please enter password", + "unlock": "Click to unlock", + "errorPasswordTip": "Password error, please re-enter", + "backToLogin": "Back to login", + "entry": "Enter the system" + }, + "timezone": { + "setTimezone": "Set Timezone", + "setSuccess": "Timezone set successfully" + } + }, + "upload": { + "upload": "Upload", + "imgUpload": "ImageUpload", + "accept": "Support {0} format", + "acceptUpload": "Only upload files in {0} format", + "maxSize": "A single file does not exceed {0}MB", + "maxSizeMultiple": "Only upload files up to {0}MB!", + "maxNumber": "Only upload up to {0} files", + "uploadSuccess": "Upload successfully", + "uploadError": "Upload failed", + "imgUploading": "Image sending, please wait..." + }, + "cropper": { + "selectImage": "Select Image", + "uploadSuccess": "Uploaded success!", + "imageTooBig": "Image too big", + "modalTitle": "Avatar upload", + "okText": "Confirm and upload", + "btn_reset": "Reset", + "btn_rotate_left": "Counterclockwise rotation", + "btn_rotate_right": "Clockwise rotation", + "btn_scale_x": "Flip horizontal", + "btn_scale_y": "Flip vertical", + "btn_zoom_in": "Zoom in", + "btn_zoom_out": "Zoom out", + "preview": "Preview" + } +} diff --git a/packages/locales/src/langs/zh-CN/authentication.json b/packages/locales/src/langs/zh-CN/authentication.json new file mode 100644 index 000000000..1eb61aed6 --- /dev/null +++ b/packages/locales/src/langs/zh-CN/authentication.json @@ -0,0 +1,70 @@ +{ + "welcomeBack": "欢迎回来", + "pageTitle": "开箱即用的大型中后台管理系统", + "pageDesc": "工程化、高性能、跨组件库的前端模版", + "loginSuccess": "登录成功", + "loginSuccessDesc": "欢迎回来", + "loginSubtitle": "请输入您的帐户信息以开始管理您的项目", + "selectAccount": "快速选择账号", + "username": "账号", + "password": "密码", + "nickname": "昵称", + "tenant": "租户", + "usernameTip": "请输入用户名", + "passwordTip": "请输入密码", + "nicknameTip": "请输入昵称", + "tenantTip": "请选择租户", + "verifyRequiredTip": "请先完成验证", + "passwordErrorTip": "密码错误", + "rememberMe": "记住账号", + "createAnAccount": "创建一个账号", + "createAccount": "创建账号", + "alreadyHaveAccount": "已经有账号了?", + "accountTip": "还没有账号?", + "signUp": "注册", + "signUpSubtitle": "让您的应用程序管理变得简单而有趣", + "confirmPassword": "确认密码", + "confirmPasswordTip": "两次输入的密码不一致", + "agree": "我同意", + "privacyPolicy": "隐私政策", + "terms": "条款", + "agreeTip": "请同意隐私政策和条款", + "goToLogin": "去登录", + "passwordStrength": "使用 8 个或更多字符,混合字母、数字和符号", + "forgetPassword": "忘记密码?", + "forgetPasswordSubtitle": "输入您的电子邮件,我们将向您发送重置密码的连接", + "resetPasswordSuccess": "重置密码成功", + "emailTip": "请输入邮箱", + "emailValidErrorTip": "你输入的邮箱格式不正确", + "resetPassword": "重置密码", + "email": "邮箱", + "qrcodeSubtitle": "请用手机扫描二维码登录", + "qrcodePrompt": "扫码后点击 '确认',即可完成登录", + "qrcodeLogin": "扫码登录", + "wechatLogin": "微信登录", + "qqLogin": "QQ登录", + "githubLogin": "Github登录", + "googleLogin": "Google登录", + "dingdingLogin": "钉钉登录", + "codeSubtitle": "请输入您的手机号码以开始管理您的项目", + "code": "验证码", + "codeTip": "请输入{0}位验证码", + "mobile": "手机号码", + "mobileTip": "请输入手机号", + "mobileErrortip": "手机号码格式错误", + "mobileLogin": "手机号登录", + "sendCode": "获取验证码", + "sendText": "{0}秒后重新获取", + "thirdPartyLogin": "其他登录方式", + "weChat": "微信", + "qq": "QQ", + "gitHub": "GitHub", + "google": "Google", + "loginAgainTitle": "重新登录", + "loginAgainSubTitle": "您的登录状态已过期,请重新登录以继续。", + "layout": { + "center": "居中", + "alignLeft": "居左", + "alignRight": "居右" + } +} diff --git a/packages/locales/src/langs/zh-CN/preferences.json b/packages/locales/src/langs/zh-CN/preferences.json new file mode 100644 index 000000000..de8413df9 --- /dev/null +++ b/packages/locales/src/langs/zh-CN/preferences.json @@ -0,0 +1,192 @@ +{ + "title": "偏好设置", + "subtitle": "自定义偏好设置 & 实时预览", + "enableStickyPreferencesNavigationBar": "开启首选项导航栏吸顶效果", + "disableStickyPreferencesNavigationBar": "关闭首选项导航栏吸顶效果", + "resetTitle": "重置偏好设置", + "resetTip": "数据有变化,点击可进行重置", + "resetSuccess": "重置偏好设置成功", + "appearance": "外观", + "layout": "布局", + "content": "内容", + "other": "其它", + "wide": "流式", + "compact": "定宽", + "followSystem": "跟随系统", + "vertical": "垂直", + "verticalTip": "侧边垂直菜单模式", + "horizontal": "水平", + "horizontalTip": "水平菜单模式,菜单全部显示在顶部", + "twoColumn": "双列菜单", + "twoColumnTip": "垂直双列菜单模式", + "headerSidebarNav": "侧边导航", + "headerSidebarNavTip": "顶部通栏,侧边导航模式", + "headerTwoColumn": "混合双列", + "headerTwoColumnTip": "双列、水平菜单共存模式", + "mixedMenu": "混合垂直", + "mixedMenuTip": "垂直水平菜单共存", + "fullContent": "内容全屏", + "fullContentTip": "不显示任何菜单,只显示内容主体", + "normal": "常规", + "plain": "朴素", + "rounded": "圆润", + "copyPreferences": "复制偏好设置", + "copyPreferencesSuccessTitle": "复制成功", + "copyPreferencesSuccess": "复制成功,请在 app 下的 `src/preferences.ts`内进行覆盖", + "clearAndLogout": "清空缓存 & 退出登录", + "mode": "模式", + "general": "通用", + "language": "语言", + "dynamicTitle": "动态标题", + "watermark": "水印", + "watermarkContent": "请输入水印文案", + "checkUpdates": "定时检查更新", + "position": { + "title": "偏好设置位置", + "header": "顶栏", + "auto": "自动", + "fixed": "固定" + }, + "sidebar": { + "buttons": "显示按钮", + "buttonFixed": "固定按钮", + "buttonCollapsed": "折叠按钮", + "title": "侧边栏", + "width": "宽度", + "visible": "显示侧边栏", + "collapsed": "折叠菜单", + "collapsedShowTitle": "折叠显示菜单名", + "autoActivateChild": "自动激活子菜单", + "autoActivateChildTip": "点击顶层菜单时,自动激活第一个子菜单或者上一次激活的子菜单", + "expandOnHover": "鼠标悬停展开", + "expandOnHoverTip": "鼠标在折叠区域悬浮时,`启用`则展开当前子菜单,`禁用`则展开整个侧边栏" + }, + "tabbar": { + "title": "标签栏", + "enable": "启用标签栏", + "icon": "显示标签栏图标", + "showMore": "显示更多按钮", + "showMaximize": "显示最大化按钮", + "persist": "持久化标签页", + "maxCount": "最大标签数", + "maxCountTip": "每次打开新的标签时如果超过最大标签数,\n会自动关闭一个最先打开的标签\n设置为 0 则不限制", + "draggable": "启动拖拽排序", + "wheelable": "启用纵向滚轮响应", + "middleClickClose": "点击鼠标中键关闭标签页", + "wheelableTip": "开启后,标签栏区域可以响应滚轮的纵向滚动事件。\n关闭时,只能响应系统的横向滚动事件(需要按下Shift再滚动滚轮)", + "styleType": { + "title": "标签页风格", + "chrome": "谷歌", + "card": "卡片", + "plain": "朴素", + "brisk": "轻快" + }, + "contextMenu": { + "reload": "重新加载", + "close": "关闭", + "pin": "固定", + "unpin": "取消固定", + "closeLeft": "关闭左侧标签页", + "closeRight": "关闭右侧标签页", + "closeOther": "关闭其它标签页", + "closeAll": "关闭全部标签页", + "openInNewWindow": "在新窗口打开", + "maximize": "最大化", + "restoreMaximize": "还原" + } + }, + "navigationMenu": { + "title": "导航菜单", + "style": "导航菜单风格", + "accordion": "侧边导航菜单手风琴模式", + "split": "导航菜单分离", + "splitTip": "开启时,侧边栏显示顶栏对应菜单的子菜单" + }, + "breadcrumb": { + "title": "面包屑导航", + "enable": "开启面包屑导航", + "icon": "显示面包屑图标", + "home": "显示首页按钮", + "style": "面包屑风格", + "hideOnlyOne": "仅有一个时隐藏", + "background": "背景" + }, + "animation": { + "title": "动画", + "loading": "页面切换 Loading", + "transition": "页面切换动画", + "progress": "页面切换进度条" + }, + "theme": { + "title": "主题", + "radius": "圆角", + "light": "浅色", + "dark": "深色", + "darkSidebar": "深色侧边栏", + "darkHeader": "深色顶栏", + "weakMode": "色弱模式", + "grayMode": "灰色模式", + "builtin": { + "title": "内置主题", + "default": "默认", + "violet": "紫罗兰", + "pink": "樱花粉", + "rose": "玫瑰红", + "skyBlue": "天蓝色", + "deepBlue": "深蓝色", + "green": "浅绿色", + "deepGreen": "深绿色", + "orange": "橙黄色", + "yellow": "柠檬黄", + "zinc": "锌色灰", + "neutral": "中性色", + "slate": "石板灰", + "gray": "中灰色", + "custom": "自定义" + } + }, + "header": { + "title": "顶栏", + "modeStatic": "静止", + "modeFixed": "固定", + "modeAuto": "自动隐藏和显示", + "modeAutoScroll": "滚动隐藏和显示", + "visible": "显示顶栏", + "menuAlign": "菜单位置", + "menuAlignStart": "左侧", + "menuAlignEnd": "右侧", + "menuAlignCenter": "居中" + }, + "footer": { + "title": "底栏", + "visible": "显示底栏", + "fixed": "固定在底部" + }, + "copyright": { + "title": "版权", + "enable": "启用版权", + "companyName": "公司名", + "companySiteLink": "公司主页", + "date": "日期", + "icp": "ICP 备案号", + "icpLink": "ICP 网站链接" + }, + "shortcutKeys": { + "title": "快捷键", + "global": "全局", + "search": "全局搜索", + "logout": "退出登录", + "preferences": "偏好设置" + }, + "widget": { + "title": "小部件", + "globalSearch": "启用全局搜索", + "fullscreen": "启用全屏", + "themeToggle": "启用主题切换", + "languageToggle": "启用语言切换", + "notification": "启用通知", + "sidebarToggle": "启用侧边栏切换", + "lockScreen": "启用锁屏", + "refresh": "启用刷新" + } +} diff --git a/packages/locales/src/langs/zh-CN/ui.json b/packages/locales/src/langs/zh-CN/ui.json new file mode 100644 index 000000000..3b1bc453a --- /dev/null +++ b/packages/locales/src/langs/zh-CN/ui.json @@ -0,0 +1,155 @@ +{ + "formRules": { + "required": "请输入{0}", + "selectRequired": "请选择{0}", + "minLength": "{0}至少{1}个字符", + "maxLength": "{0}最多{1}个字符", + "length": "{0}长度必须为{1}个字符", + "alreadyExists": "{0} `{1}` 已存在", + "startWith": "{0}必须以 {1} 开头", + "invalidURL": "请输入有效的链接", + "mobile": "请输入正确的{0}" + }, + "actionTitle": { + "copy": "复制{0}", + "cancel": "取消{0}", + "edit": "修改{0}", + "create": "新增{0}", + "delete": "删除{0}", + "deleteBatch": "批量删除", + "detail": "详情{0}", + "view": "查看{0}", + "import": "导入", + "export": "导出" + }, + "actionMessage": { + "deleteConfirm": "确定删除 {0} 吗?", + "deleteBatchConfirm": "确定删除所选项吗?", + "deleting": "正在删除 {0} ...", + "deletingBatch": "正在删除所选项 ...", + "deleteSuccess": "{0} 删除成功", + "deleteFailed": "{0} 删除失败", + "operationSuccess": "操作成功", + "operationFailed": "操作失败", + "importSuccess": "导入成功", + "importFail": "导入失败", + "downloadTemplateFail": "下载模板失败", + "updating": "正在更新 {0}...", + "updateSuccess": "更新 {0} 成功", + "updateFailed": "更新 {0} 失败" + }, + "placeholder": { + "input": "请输入", + "select": "请选择" + }, + "captcha": { + "title": "请完成安全验证", + "sliderSuccessText": "验证通过", + "sliderDefaultText": "请按住滑块拖动", + "sliderRotateDefaultTip": "点击图片可刷新", + "sliderTranslateDefaultTip": "点击图片可刷新", + "sliderRotateFailTip": "验证失败", + "sliderRotateSuccessTip": "验证成功,耗时{0}秒", + "sliderTranslateFailTip": "验证失败", + "sliderTranslateSuccessTip": "验证成功,耗时{0}秒", + "alt": "支持img标签src属性值", + "refreshAriaLabel": "刷新验证码", + "confirmAriaLabel": "确认选择", + "confirm": "确认", + "pointAriaLabel": "点击点", + "clickInOrder": "请依次点击" + }, + "iconPicker": { + "placeholder": "选择一个图标", + "search": "搜索图标..." + }, + "jsonViewer": { + "copy": "复制", + "copied": "已复制" + }, + "fallback": { + "pageNotFound": "哎呀!未找到页面", + "pageNotFoundDesc": "抱歉,我们无法找到您要找的页面。", + "forbidden": "哎呀!访问被拒绝", + "forbiddenDesc": "抱歉,您没有权限访问此页面。", + "internalError": "哎呀!出错了", + "internalErrorDesc": "抱歉,服务器遇到错误。", + "offline": "离线页面", + "offlineError": "哎呀!网络错误", + "offlineErrorDesc": "抱歉,无法连接到互联网,请检查您的网络连接并重试。", + "comingSoon": "即将推出", + "http": { + "requestTimeout": "请求超时,请稍后再试。", + "networkError": "网络异常,请检查您的网络连接后重试。", + "badRequest": "请求错误。请检查您的输入并重试。", + "unauthorized": "登录认证过期,请重新登录后继续。", + "forbidden": "禁止访问, 您没有权限访问此资源。", + "notFound": "未找到, 请求的资源不存在。", + "internalServerError": "内部服务器错误,请稍后再试。" + } + }, + "widgets": { + "document": "文档", + "profile": "个人中心", + "qa": "问题 & 帮助", + "setting": "设置", + "logoutTip": "是否退出登录?", + "viewAll": "查看所有消息", + "notifications": "通知", + "markAllAsRead": "全部标记为已读", + "clearNotifications": "清空", + "checkUpdatesTitle": "新版本可用", + "checkUpdatesDescription": "点击刷新以获取最新版本", + "search": { + "title": "搜索", + "searchNavigate": "搜索导航菜单", + "select": "选择", + "navigate": "导航", + "close": "关闭", + "noResults": "未找到搜索结果", + "noRecent": "没有搜索历史", + "recent": "搜索历史" + }, + "lockScreen": { + "title": "锁定屏幕", + "screenButton": "锁定", + "password": "密码", + "placeholder": "请输入锁屏密码", + "unlock": "点击解锁", + "errorPasswordTip": "密码错误,请重新输入", + "backToLogin": "返回登录", + "entry": "进入系统" + }, + "timezone": { + "setTimezone": "设置时区", + "setSuccess": "时区设置成功" + } + }, + "upload": { + "upload": "上传", + "imgUpload": "图片上传", + "accept": "支持{0}格式", + "acceptUpload": "只能上传{0}格式文件", + "maxSize": "单个文件不超过{0}MB", + "maxSizeMultiple": "只能上传不超过{0}MB的文件!", + "maxNumber": "最多只能上传{0}个文件", + "uploadSuccess": "上传成功", + "uploadError": "上传失败", + "imgUploading": "图片发送中,请稍等。。。" + }, + "cropper": { + "selectImage": "选择图片", + "uploadSuccess": "上传成功", + "imageTooBig": "图片超限", + "modalTitle": "头像上传", + "okText": "确认并上传", + "btn_reset": "重置", + "btn_rotate_left": "逆时针旋转", + "btn_rotate_right": "顺时针旋转", + "btn_scale_x": "水平翻转", + "btn_scale_y": "垂直翻转", + "btn_zoom_in": "放大", + "btn_zoom_out": "缩小", + "preview": "预览" + } +} diff --git a/packages/preferences/package.json b/packages/preferences/package.json new file mode 100644 index 000000000..e6e52e234 --- /dev/null +++ b/packages/preferences/package.json @@ -0,0 +1,26 @@ +{ + "name": "@vben/preferences", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/preferences" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/preferences": "workspace:*", + "@vben-core/typings": "workspace:*" + } +} diff --git a/packages/stores/package.json b/packages/stores/package.json new file mode 100644 index 000000000..72bcf4974 --- /dev/null +++ b/packages/stores/package.json @@ -0,0 +1,32 @@ +{ + "name": "@vben/stores", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/stores" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/preferences": "workspace:*", + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "pinia": "catalog:", + "pinia-plugin-persistedstate": "catalog:", + "secure-ls": "catalog:", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/packages/stores/shim-pinia.d.ts b/packages/stores/shim-pinia.d.ts new file mode 100644 index 000000000..558d0ba53 --- /dev/null +++ b/packages/stores/shim-pinia.d.ts @@ -0,0 +1,9 @@ +// https://github.com/vuejs/pinia/issues/2098 +declare module 'pinia' { + export function acceptHMRUpdate( + initialUseStore: any | StoreDefinition, + hot: any, + ): (newModule: any) => any; +} + +export { acceptHMRUpdate }; diff --git a/packages/stores/src/modules/dict.ts b/packages/stores/src/modules/dict.ts new file mode 100644 index 000000000..6d80c4b4e --- /dev/null +++ b/packages/stores/src/modules/dict.ts @@ -0,0 +1,73 @@ +import { acceptHMRUpdate, defineStore } from 'pinia'; + +export interface DictItem { + colorType?: string; + cssClass?: string; + label: string; + value: string; +} + +export type Dict = Record; + +interface DictState { + dictCache: Dict; +} + +export const useDictStore = defineStore('core-dict', { + actions: { + getDictData(dictType: string, value: any) { + const dict = this.dictCache[dictType]; + if (!dict) { + return undefined; + } + return ( + dict.find((d) => d.value === value || d.value === value.toString()) ?? + undefined + ); + }, + getDictOptions(dictType: string) { + const dictOptions = this.dictCache[dictType]; + if (!dictOptions) { + return []; + } + return dictOptions; + }, + setDictCache(dicts: Dict) { + this.dictCache = dicts; + }, + setDictCacheByApi( + api: (params: Record) => Promise[]>, + params: Record = {}, + labelField: string = 'label', + valueField: string = 'value', + ) { + api(params).then((dicts) => { + const dictCacheData: Dict = {}; + dicts.forEach((dict) => { + dictCacheData[dict.dictType] = dicts + .filter((d) => d.dictType === dict.dictType) + .map((d) => ({ + colorType: d.colorType, + cssClass: d.cssClass, + label: d[labelField], + value: d[valueField], + })); + }); + this.setDictCache(dictCacheData); + }); + }, + }, + persist: { + // 持久化 + pick: ['dictCache'], + }, + state: (): DictState => ({ + dictCache: {}, + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useDictStore, hot)); +} diff --git a/packages/stores/src/modules/index.ts b/packages/stores/src/modules/index.ts new file mode 100644 index 000000000..6048fecc0 --- /dev/null +++ b/packages/stores/src/modules/index.ts @@ -0,0 +1,5 @@ +export * from './access'; +export * from './dict'; +export * from './tabbar'; +export * from './timezone'; +export * from './user'; diff --git a/packages/stores/src/modules/tabbar.ts b/packages/stores/src/modules/tabbar.ts new file mode 100644 index 000000000..b1b4060f2 --- /dev/null +++ b/packages/stores/src/modules/tabbar.ts @@ -0,0 +1,658 @@ +import type { ComputedRef } from 'vue'; +import type { + RouteLocationNormalized, + Router, + RouteRecordNormalized, +} from 'vue-router'; + +import type { TabDefinition } from '@vben-core/typings'; + +import { toRaw } from 'vue'; + +import { preferences } from '@vben-core/preferences'; +import { + openRouteInNewWindow, + startProgress, + stopProgress, +} from '@vben-core/shared/utils'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + +interface TabbarState { + /** + * @zh_CN 当前打开的标签页列表缓存 + */ + cachedTabs: Set; + /** + * @zh_CN 拖拽结束的索引 + */ + dragEndIndex: number; + /** + * @zh_CN 需要排除缓存的标签页 + */ + excludeCachedTabs: Set; + /** + * @zh_CN 标签右键菜单列表 + */ + menuList: string[]; + /** + * @zh_CN 是否刷新 + */ + renderRouteView?: boolean; + /** + * @zh_CN 当前打开的标签页列表 + */ + tabs: TabDefinition[]; + /** + * @zh_CN 更新时间,用于一些更新场景,使用watch深度监听的话,会损耗性能 + */ + updateTime?: number; +} + +/** + * @zh_CN 访问权限相关 + */ +export const useTabbarStore = defineStore('core-tabbar', { + actions: { + /** + * Close tabs in bulk + */ + async _bulkCloseByKeys(keys: string[]) { + const keySet = new Set(keys); + this.tabs = this.tabs.filter( + (item) => !keySet.has(getTabKeyFromTab(item)), + ); + + await this.updateCacheTabs(); + }, + /** + * @zh_CN 关闭标签页 + * @param tab + */ + _close(tab: TabDefinition) { + if (isAffixTab(tab)) { + return; + } + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + index !== -1 && this.tabs.splice(index, 1); + }, + /** + * @zh_CN 跳转到默认标签页 + */ + async _goToDefaultTab(router: Router) { + if (this.getTabs.length <= 0) { + return; + } + const firstTab = this.getTabs[0]; + if (firstTab) { + await this._goToTab(firstTab, router); + } + }, + /** + * @zh_CN 跳转到标签页 + * @param tab + * @param router + */ + async _goToTab(tab: TabDefinition, router: Router) { + const { params, path, query } = tab; + const toParams = { + params: params || {}, + path, + query: query || {}, + }; + await router.replace(toParams); + }, + /** + * @zh_CN 添加标签页 + * @param routeTab + */ + addTab(routeTab: TabDefinition): TabDefinition { + let tab = cloneTab(routeTab); + if (!tab.key) { + tab.key = getTabKey(routeTab); + } + if (!isTabShown(tab)) { + return tab; + } + + const tabIndex = this.tabs.findIndex((item) => { + return equalTab(item, tab); + }); + + if (tabIndex === -1) { + const maxCount = preferences.tabbar.maxCount; + // 获取动态路由打开数,超过 0 即代表需要控制打开数 + const maxNumOfOpenTab = (routeTab?.meta?.maxNumOfOpenTab ?? + -1) as number; + // 如果动态路由层级大于 0 了,那么就要限制该路由的打开数限制了 + // 获取到已经打开的动态路由数, 判断是否大于某一个值 + if ( + maxNumOfOpenTab > 0 && + this.tabs.filter((tab) => tab.name === routeTab.name).length >= + maxNumOfOpenTab + ) { + // 关闭第一个 + const index = this.tabs.findIndex( + (item) => item.name === routeTab.name, + ); + index !== -1 && this.tabs.splice(index, 1); + } else if (maxCount > 0 && this.tabs.length >= maxCount) { + // 关闭第一个 + const index = this.tabs.findIndex( + (item) => + !Reflect.has(item.meta, 'affixTab') || !item.meta.affixTab, + ); + index !== -1 && this.tabs.splice(index, 1); + } + this.tabs.push(tab); + } else { + // 页面已经存在,不重复添加选项卡,只更新选项卡参数 + const currentTab = toRaw(this.tabs)[tabIndex]; + const mergedTab = { + ...currentTab, + ...tab, + meta: { ...currentTab?.meta, ...tab.meta }, + }; + if (currentTab) { + const curMeta = currentTab.meta; + if (Reflect.has(curMeta, 'affixTab')) { + mergedTab.meta.affixTab = curMeta.affixTab; + } + if (Reflect.has(curMeta, 'newTabTitle')) { + mergedTab.meta.newTabTitle = curMeta.newTabTitle; + } + } + tab = mergedTab; + this.tabs.splice(tabIndex, 1, mergedTab); + } + this.updateCacheTabs(); + return tab; + }, + /** + * @zh_CN 关闭所有标签页 + */ + async closeAllTabs(router: Router) { + const newTabs = this.tabs.filter((tab) => isAffixTab(tab)); + this.tabs = newTabs.length > 0 ? newTabs : [...this.tabs].splice(0, 1); + await this._goToDefaultTab(router); + this.updateCacheTabs(); + }, + /** + * @zh_CN 关闭左侧标签页 + * @param tab + */ + async closeLeftTabs(tab: TabDefinition) { + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + + if (index < 1) { + return; + } + + const leftTabs = this.tabs.slice(0, index); + const keys: string[] = []; + + for (const item of leftTabs) { + if (!isAffixTab(item)) { + keys.push(item.key as string); + } + } + await this._bulkCloseByKeys(keys); + }, + /** + * @zh_CN 关闭其他标签页 + * @param tab + */ + async closeOtherTabs(tab: TabDefinition) { + const closeKeys = this.tabs.map((item) => getTabKeyFromTab(item)); + + const keys: string[] = []; + + for (const key of closeKeys) { + if (key !== getTabKeyFromTab(tab)) { + const closeTab = this.tabs.find( + (item) => getTabKeyFromTab(item) === key, + ); + if (!closeTab) { + continue; + } + if (!isAffixTab(closeTab)) { + keys.push(closeTab.key as string); + } + } + } + await this._bulkCloseByKeys(keys); + }, + /** + * @zh_CN 关闭右侧标签页 + * @param tab + */ + async closeRightTabs(tab: TabDefinition) { + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + + if (index !== -1 && index < this.tabs.length - 1) { + const rightTabs = this.tabs.slice(index + 1); + + const keys: string[] = []; + for (const item of rightTabs) { + if (!isAffixTab(item)) { + keys.push(item.key as string); + } + } + await this._bulkCloseByKeys(keys); + } + }, + + /** + * @zh_CN 关闭标签页 + * @param tab + * @param router + */ + async closeTab(tab: TabDefinition, router: Router) { + const { currentRoute } = router; + // 关闭不是激活选项卡 + if (getTabKey(currentRoute.value) !== getTabKeyFromTab(tab)) { + this._close(tab); + this.updateCacheTabs(); + return; + } + const index = this.getTabs.findIndex( + (item) => getTabKeyFromTab(item) === getTabKey(currentRoute.value), + ); + + const before = this.getTabs[index - 1]; + const after = this.getTabs[index + 1]; + + // 下一个tab存在,跳转到下一个 + if (after) { + this._close(tab); + await this._goToTab(after, router); + // 上一个tab存在,跳转到上一个 + } else if (before) { + this._close(tab); + await this._goToTab(before, router); + } else { + console.error('Failed to close the tab; only one tab remains open.'); + } + }, + + /** + * @zh_CN 通过key关闭标签页 + * @param key + * @param router + */ + async closeTabByKey(key: string, router: Router) { + const originKey = decodeURIComponent(key); + const index = this.tabs.findIndex( + (item) => getTabKeyFromTab(item) === originKey, + ); + if (index === -1) { + return; + } + + const tab = this.tabs[index]; + if (tab) { + await this.closeTab(tab, router); + } + }, + + /** + * 根据tab的key获取tab + * @param key + */ + getTabByKey(key: string) { + return this.getTabs.find( + (item) => getTabKeyFromTab(item) === key, + ) as TabDefinition; + }, + /** + * @zh_CN 新窗口打开标签页 + * @param tab + */ + async openTabInNewWindow(tab: TabDefinition) { + openRouteInNewWindow(tab.fullPath || tab.path); + }, + + /** + * @zh_CN 固定标签页 + * @param tab + */ + async pinTab(tab: TabDefinition) { + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + if (index === -1) { + return; + } + const oldTab = this.tabs[index]; + tab.meta.affixTab = true; + tab.meta.title = oldTab?.meta?.title as string; + // this.addTab(tab); + this.tabs.splice(index, 1, tab); + // 过滤固定tabs,后面更改affixTabOrder的值的话可能会有问题,目前行464排序affixTabs没有设置值 + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + // 获得固定tabs的index + const newIndex = affixTabs.findIndex((item) => equalTab(item, tab)); + // 交换位置重新排序 + await this.sortTabs(index, newIndex); + }, + + /** + * 刷新标签页 + */ + async refresh(router: Router | string) { + // 如果是Router路由,那么就根据当前路由刷新 + // 如果是string字符串,为路由名称,则定向刷新指定标签页,不能是当前路由名称,否则不会刷新 + if (typeof router === 'string') { + return await this.refreshByName(router); + } + + const { currentRoute } = router; + const { name } = currentRoute.value; + + this.excludeCachedTabs.add(name as string); + this.renderRouteView = false; + startProgress(); + + await new Promise((resolve) => setTimeout(resolve, 200)); + + this.excludeCachedTabs.delete(name as string); + this.renderRouteView = true; + stopProgress(); + }, + + /** + * 根据路由名称刷新指定标签页 + */ + async refreshByName(name: string) { + this.excludeCachedTabs.add(name); + await new Promise((resolve) => setTimeout(resolve, 200)); + this.excludeCachedTabs.delete(name); + }, + + /** + * @zh_CN 重置标签页标题 + */ + async resetTabTitle(tab: TabDefinition) { + if (tab?.meta?.newTabTitle) { + return; + } + const findTab = this.tabs.find((item) => equalTab(item, tab)); + if (findTab) { + findTab.meta.newTabTitle = undefined; + await this.updateCacheTabs(); + } + }, + + /** + * 设置固定标签页 + * @param tabs + */ + setAffixTabs(tabs: RouteRecordNormalized[]) { + for (const tab of tabs) { + tab.meta.affixTab = true; + this.addTab(routeToTab(tab)); + } + }, + + /** + * @zh_CN 更新菜单列表 + * @param list + */ + setMenuList(list: string[]) { + this.menuList = list; + }, + + /** + * @zh_CN 设置标签页标题 + * + * @zh_CN 支持设置静态标题字符串或计算属性作为动态标题 + * @zh_CN 当标题为计算属性时,标题会随计算属性值变化而自动更新 + * @zh_CN 适用于需要根据状态或多语言动态更新标题的场景 + * + * @param {TabDefinition} tab - 标签页对象 + * @param {ComputedRef | string} title - 标题内容,支持静态字符串或计算属性 + * + * @example + * // 设置静态标题 + * setTabTitle(tab, '新标签页'); + * + * @example + * // 设置动态标题 + * setTabTitle(tab, computed(() => t('common.dashboard'))); + */ + async setTabTitle(tab: TabDefinition, title: ComputedRef | string) { + const findTab = this.tabs.find((item) => equalTab(item, tab)); + + if (findTab) { + findTab.meta.newTabTitle = title; + + await this.updateCacheTabs(); + } + }, + setUpdateTime() { + this.updateTime = Date.now(); + }, + /** + * @zh_CN 设置标签页顺序 + * @param oldIndex + * @param newIndex + */ + async sortTabs(oldIndex: number, newIndex: number) { + const currentTab = this.tabs[oldIndex]; + if (!currentTab) { + return; + } + this.tabs.splice(oldIndex, 1); + this.tabs.splice(newIndex, 0, currentTab); + this.dragEndIndex = this.dragEndIndex + 1; + }, + + /** + * @zh_CN 切换固定标签页 + * @param tab + */ + async toggleTabPin(tab: TabDefinition) { + const affixTab = tab?.meta?.affixTab ?? false; + + await (affixTab ? this.unpinTab(tab) : this.pinTab(tab)); + }, + + /** + * @zh_CN 取消固定标签页 + * @param tab + */ + async unpinTab(tab: TabDefinition) { + const index = this.tabs.findIndex((item) => equalTab(item, tab)); + if (index === -1) { + return; + } + const oldTab = this.tabs[index]; + tab.meta.affixTab = false; + tab.meta.title = oldTab?.meta?.title as string; + // this.addTab(tab); + this.tabs.splice(index, 1, tab); + // 过滤固定tabs,后面更改affixTabOrder的值的话可能会有问题,目前行464排序affixTabs没有设置值 + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + // 获得固定tabs的index,使用固定tabs的下一个位置也就是活动tabs的第一个位置 + const newIndex = affixTabs.length; + // 交换位置重新排序 + await this.sortTabs(index, newIndex); + }, + /** + * 根据当前打开的选项卡更新缓存 + */ + async updateCacheTabs() { + const cacheMap = new Set(); + + for (const tab of this.tabs) { + // 跳过不需要持久化的标签页 + const keepAlive = tab.meta?.keepAlive; + if (!keepAlive) { + continue; + } + (tab.matched || []).forEach((t, i) => { + if (i > 0) { + cacheMap.add(t.name as string); + } + }); + + const name = tab.name as string; + cacheMap.add(name); + } + this.cachedTabs = cacheMap; + }, + }, + getters: { + affixTabs(): TabDefinition[] { + const affixTabs = this.tabs.filter((tab) => isAffixTab(tab)); + + return affixTabs.toSorted((a, b) => { + const orderA = (a.meta?.affixTabOrder ?? 0) as number; + const orderB = (b.meta?.affixTabOrder ?? 0) as number; + return orderA - orderB; + }); + }, + getCachedTabs(): string[] { + return [...this.cachedTabs]; + }, + getExcludeCachedTabs(): string[] { + return [...this.excludeCachedTabs]; + }, + getMenuList(): string[] { + return this.menuList; + }, + getTabs(): TabDefinition[] { + const normalTabs = this.tabs.filter((tab) => !isAffixTab(tab)); + return [...this.affixTabs, ...normalTabs].filter(Boolean); + }, + }, + persist: [ + // tabs不需要保存在localStorage + { + pick: ['tabs'], + storage: sessionStorage, + }, + ], + state: (): TabbarState => ({ + cachedTabs: new Set(), + dragEndIndex: 0, + excludeCachedTabs: new Set(), + menuList: [ + 'close', + 'affix', + 'maximize', + 'reload', + 'open-in-new-window', + 'close-left', + 'close-right', + 'close-other', + 'close-all', + ], + renderRouteView: true, + tabs: [], + updateTime: Date.now(), + }), +}); + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useTabbarStore, hot)); +} + +/** + * @zh_CN 克隆路由,防止路由被修改 + * @param route + */ +function cloneTab(route: TabDefinition): TabDefinition { + if (!route) { + return route; + } + const { matched, meta, ...opt } = route; + return { + ...opt, + matched: (matched + ? matched.map((item) => ({ + meta: item.meta, + name: item.name, + path: item.path, + })) + : undefined) as RouteRecordNormalized[], + meta: { + ...meta, + newTabTitle: meta.newTabTitle, + }, + }; +} + +/** + * @zh_CN 是否是固定标签页 + * @param tab + */ +function isAffixTab(tab: TabDefinition) { + return tab?.meta?.affixTab ?? false; +} + +/** + * @zh_CN 是否显示标签 + * @param tab + */ +function isTabShown(tab: TabDefinition) { + const matched = tab?.matched ?? []; + return !tab.meta.hideInTab && matched.every((item) => !item.meta.hideInTab); +} + +/** + * 从route获取tab页的key + * @param tab + */ +function getTabKey(tab: RouteLocationNormalized | RouteRecordNormalized) { + const { + fullPath, + path, + meta: { fullPathKey } = {}, + query = {}, + } = tab as RouteLocationNormalized; + // pageKey可能是数组(查询参数重复时可能出现) + const pageKey = Array.isArray(query.pageKey) + ? query.pageKey[0] + : query.pageKey; + let rawKey; + if (pageKey) { + rawKey = pageKey; + } else { + rawKey = fullPathKey === false ? path : (fullPath ?? path); + } + try { + return decodeURIComponent(rawKey); + } catch { + return rawKey; + } +} + +/** + * 从tab获取tab页的key + * 如果tab没有key,那么就从route获取key + * @param tab + */ +function getTabKeyFromTab(tab: TabDefinition): string { + return tab.key ?? getTabKey(tab); +} + +/** + * 比较两个tab是否相等 + * @param a + * @param b + */ +function equalTab(a: TabDefinition, b: TabDefinition) { + return getTabKeyFromTab(a) === getTabKeyFromTab(b); +} + +function routeToTab(route: RouteRecordNormalized) { + return { + meta: route.meta, + name: route.name, + path: route.path, + key: getTabKey(route), + } as TabDefinition; +} + +export { getTabKey }; diff --git a/packages/stores/src/modules/timezone.ts b/packages/stores/src/modules/timezone.ts new file mode 100644 index 000000000..24180c60f --- /dev/null +++ b/packages/stores/src/modules/timezone.ts @@ -0,0 +1,132 @@ +import { ref, unref } from 'vue'; + +import { DEFAULT_TIME_ZONE_OPTIONS } from '@vben-core/preferences'; +import { + getCurrentTimezone, + setCurrentTimezone, +} from '@vben-core/shared/utils'; + +import { acceptHMRUpdate, defineStore } from 'pinia'; + +interface TimezoneHandler { + getTimezone?: () => Promise; + getTimezoneOptions?: () => Promise< + { + label: string; + value: string; + }[] + >; + setTimezone?: (timezone: string) => Promise; +} + +/** + * 默认时区处理模块 + * 时区存储基于pinia存储插件 + */ +const getDefaultTimezoneHandler = (): TimezoneHandler => { + return { + getTimezoneOptions: () => { + return Promise.resolve( + DEFAULT_TIME_ZONE_OPTIONS.map((item) => { + return { + label: item.label, + value: item.timezone, + }; + }), + ); + }, + }; +}; + +/** + * 自定义时区处理模块 + */ +let customTimezoneHandler: null | Partial = null; +const setTimezoneHandler = (handler: Partial) => { + customTimezoneHandler = handler; +}; + +/** + * 获取时区处理模块 + */ +const getTimezoneHandler = () => { + return { + ...getDefaultTimezoneHandler(), + ...customTimezoneHandler, + }; +}; + +/** + * timezone支持模块 + */ +const useTimezoneStore = defineStore( + 'core-timezone', + () => { + const timezoneRef = ref(getCurrentTimezone()); + + /** + * 初始化时区 + * Initialize the timezone + */ + async function initTimezone() { + const timezoneHandler = getTimezoneHandler(); + const timezone = await timezoneHandler.getTimezone?.(); + if (timezone) { + timezoneRef.value = timezone; + } + // 设置dayjs默认时区 + setCurrentTimezone(unref(timezoneRef)); + } + + /** + * 设置时区 + * Set the timezone + * @param timezone 时区字符串 + */ + async function setTimezone(timezone: string) { + const timezoneHandler = getTimezoneHandler(); + await timezoneHandler.setTimezone?.(timezone); + timezoneRef.value = timezone; + // 设置dayjs默认时区 + setCurrentTimezone(timezone); + } + + /** + * 获取时区选项 + * Get the timezone options + */ + async function getTimezoneOptions() { + const timezoneHandler = getTimezoneHandler(); + return (await timezoneHandler.getTimezoneOptions?.()) || []; + } + + initTimezone().catch((error) => { + console.error('Failed to initialize timezone during store setup:', error); + }); + + function $reset() { + timezoneRef.value = getCurrentTimezone(); + } + + return { + timezone: timezoneRef, + setTimezone, + getTimezoneOptions, + $reset, + }; + }, + { + persist: { + // 持久化 + pick: ['timezone'], + }, + }, +); + +export { setTimezoneHandler, useTimezoneStore }; + +// 解决热更新问题 +const hot = import.meta.hot; +if (hot) { + hot.accept(acceptHMRUpdate(useTimezoneStore, hot)); +} diff --git a/packages/styles/package.json b/packages/styles/package.json new file mode 100644 index 000000000..0716a5e5e --- /dev/null +++ b/packages/styles/package.json @@ -0,0 +1,34 @@ +{ + "name": "@vben/styles", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/styles" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./antd": { + "default": "./src/antd/index.css" + }, + "./ele": { + "default": "./src/ele/index.css" + }, + "./naive": { + "default": "./src/naive/index.css" + }, + "./global": { + "default": "./src/global/index.scss" + } + }, + "dependencies": { + "@vben-core/design": "workspace:*" + } +} diff --git a/packages/types/global.d.ts b/packages/types/global.d.ts new file mode 100644 index 000000000..36e546ac2 --- /dev/null +++ b/packages/types/global.d.ts @@ -0,0 +1,38 @@ +import type { RouteMeta as IRouteMeta } from '@vben-core/typings'; + +import 'vue-router'; + +declare module 'vue-router' { + // eslint-disable-next-line @typescript-eslint/no-empty-object-type + interface RouteMeta extends IRouteMeta {} +} + +export interface VbenAdminProAppConfigRaw { + VITE_GLOB_API_URL: string; + VITE_GLOB_AUTH_DINGDING_CLIENT_ID: string; + VITE_GLOB_AUTH_DINGDING_CORP_ID: string; + // API 加解密相关配置 + VITE_APP_API_ENCRYPT_ENABLE: string; + VITE_APP_API_ENCRYPT_HEADER: string; + VITE_APP_API_ENCRYPT_ALGORITHM: string; + VITE_APP_API_ENCRYPT_REQUEST_KEY: string; + VITE_APP_API_ENCRYPT_RESPONSE_KEY: string; +} + +interface AuthConfig { + dingding?: { + clientId: string; + corpId: string; + }; +} + +export interface ApplicationConfig { + apiURL: string; + auth: AuthConfig; +} + +declare global { + interface Window { + _VBEN_ADMIN_PRO_APP_CONF_: VbenAdminProAppConfigRaw; + } +} diff --git a/packages/types/package.json b/packages/types/package.json new file mode 100644 index 000000000..07dbca0e7 --- /dev/null +++ b/packages/types/package.json @@ -0,0 +1,27 @@ +{ + "name": "@vben/types", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/types" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./global": { + "types": "./global.d.ts" + } + }, + "dependencies": { + "@vben-core/typings": "workspace:*", + "vue": "catalog:", + "vue-router": "catalog:" + } +} diff --git a/packages/utils/package.json b/packages/utils/package.json new file mode 100644 index 000000000..faa7b5d5d --- /dev/null +++ b/packages/utils/package.json @@ -0,0 +1,27 @@ +{ + "name": "@vben/utils", + "version": "5.5.9", + "homepage": "https://github.com/vbenjs/vue-vben-admin", + "bugs": "https://github.com/vbenjs/vue-vben-admin/issues", + "repository": { + "type": "git", + "url": "git+https://github.com/vbenjs/vue-vben-admin.git", + "directory": "packages/utils" + }, + "license": "MIT", + "type": "module", + "sideEffects": [ + "**/*.css" + ], + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + } + }, + "dependencies": { + "@vben-core/shared": "workspace:*", + "@vben-core/typings": "workspace:*", + "vue-router": "catalog:" + } +} diff --git a/packages/utils/src/helpers/generate-menus.ts b/packages/utils/src/helpers/generate-menus.ts new file mode 100644 index 000000000..6a1308ebb --- /dev/null +++ b/packages/utils/src/helpers/generate-menus.ts @@ -0,0 +1,178 @@ +import type { Router, RouteRecordRaw } from 'vue-router'; + +import type { + AppRouteRecordRaw, + ExRouteRecordRaw, + MenuRecordRaw, + RouteMeta, + RouteRecordStringComponent, +} from '@vben-core/typings'; + +import { filterTree, isHttpUrl, mapTree } from '@vben-core/shared/utils'; + +/** + * 根据 routes 生成菜单列表 + * @param routes - 路由配置列表 + * @param router - Vue Router 实例 + * @returns 生成的菜单列表 + */ +function generateMenus( + routes: RouteRecordRaw[], + router: Router, +): MenuRecordRaw[] { + // 将路由列表转换为一个以 name 为键的对象映射 + const finalRoutesMap: { [key: string]: string } = Object.fromEntries( + router.getRoutes().map(({ name, path }) => [name, path]), + ); + + let menus = mapTree(routes, (route) => { + // 获取最终的路由路径 + const path = finalRoutesMap[route.name as string] ?? route.path ?? ''; + + const { + meta = {} as RouteMeta, + name: routeName, + redirect, + children = [], + } = route; + const { + activeIcon, + badge, + badgeType, + badgeVariants, + hideChildrenInMenu = false, + icon, + link, + order, + title = '', + } = meta; + + // 确保菜单名称不为空 + const name = (title || routeName || '') as string; + + // 处理子菜单 + const resultChildren = hideChildrenInMenu + ? [] + : ((children as MenuRecordRaw[]) ?? []); + + // 设置子菜单的父子关系 + if (resultChildren.length > 0) { + resultChildren.forEach((child) => { + child.parents = [...(route.parents ?? []), path]; + child.parent = path; + }); + } + + // 确定最终路径 + const resultPath = hideChildrenInMenu ? redirect || path : link || path; + + return { + activeIcon, + badge, + badgeType, + badgeVariants, + icon, + name, + order, + parent: route.parent, + parents: route.parents, + path: resultPath, + show: !meta.hideInMenu, + children: resultChildren, + }; + }); + + // 对菜单进行排序,避免order=0时被替换成999的问题 + menus = menus.toSorted((a, b) => (a?.order ?? 999) - (b?.order ?? 999)); + + // 过滤掉隐藏的菜单项 + return filterTree(menus, (menu) => !!menu.show); +} + +/** + * 转换后端菜单数据为路由数据 + * @param menuList 后端菜单数据 + * @param parent 父级菜单 + * @param nameSet 用于跟踪已使用的 name,防止重复 + * @returns 路由数据 + */ +function convertServerMenuToRouteRecordStringComponent( + menuList: AppRouteRecordRaw[], + parent = '', + nameSet: Set = new Set(), +): RouteRecordStringComponent[] { + const menus: RouteRecordStringComponent[] = []; + menuList.forEach((menu) => { + // 处理顶级链接菜单 + if (isHttpUrl(menu.path) && menu.parentId === 0) { + const urlMenu: RouteRecordStringComponent = { + component: 'IFrameView', + meta: { + hideInMenu: !menu.visible, + icon: menu.icon, + link: menu.path, + orderNo: menu.sort, + title: menu.name, + }, + name: menu.name, + path: `/${menu.path}/index`, + }; + menus.push(urlMenu); + return; + } else if (menu.children && menu.parentId === 0) { + menu.component = 'BasicLayout'; + } else if (!menu.children) { + menu.component = menu.component as string; + } + if (menu.component === 'Layout') { + menu.component = 'BasicLayout'; + } + + if (menu.children && menu.parentId !== 0) { + menu.component = ''; + } + + // path + if (parent) { + menu.path = `${parent}/${menu.path}`; + } + + if (!menu.path.startsWith('/')) { + menu.path = `/${menu.path}`; + } + + // add by 芋艿:防止 name 重复,只有在 name 重复时,才自动添加 id + let finalName = menu.componentName || menu.name; + if (nameSet.has(finalName)) { + finalName = menu.name + menu.id; + console.error(`menu name duplicate: ${menu.name}, id: ${menu.id}`, menu); + } + nameSet.add(finalName); + + const buildMenu: RouteRecordStringComponent = { + component: menu.component, + meta: { + hideInMenu: !menu.visible, + icon: menu.icon, + keepAlive: menu.keepAlive, + orderNo: menu.sort, + title: menu.name, + }, + name: finalName, + path: menu.path, + }; + + if (menu.children && menu.children.length > 0) { + buildMenu.children = convertServerMenuToRouteRecordStringComponent( + menu.children, + menu.path, + nameSet, + ); + } + + menus.push(buildMenu); + }); + return menus; +} + +export { convertServerMenuToRouteRecordStringComponent, generateMenus }; diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts new file mode 100644 index 000000000..eb9bef1ab --- /dev/null +++ b/packages/utils/src/index.ts @@ -0,0 +1,5 @@ +export * from './helpers'; +export * from './validator'; +export * from '@vben-core/shared/cache'; +export * from '@vben-core/shared/color'; +export * from '@vben-core/shared/utils'; diff --git a/packages/utils/src/validator/index.ts b/packages/utils/src/validator/index.ts new file mode 100644 index 000000000..0d7fc2954 --- /dev/null +++ b/packages/utils/src/validator/index.ts @@ -0,0 +1,2 @@ +export * from './regex'; +export * from './validator'; diff --git a/packages/utils/src/validator/regex.ts b/packages/utils/src/validator/regex.ts new file mode 100644 index 000000000..9d1e7c47a --- /dev/null +++ b/packages/utils/src/validator/regex.ts @@ -0,0 +1,18 @@ +/** 手机号正则表达式(中国) */ +const MOBILE_REGEX = /(?:0|86|\+86)?1[3-9]\d{9}/; +/** 身份证号正则表达式 */ +const ID_CARD_REGEX = /^\d{15}|\d{18}$/; +/** 邮箱正则表达式 */ +const EMAIL_REGEX = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; +/** 密码正则表达式 以字母开头,长度在6~18之间,只能包含字母、数字和下划线 */ +const PASSWORD_REGEX = /^[a-z]\w{5,17}$/i; +/** 强密码 必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间 */ +const STRONG_PASSWORD_REGEX = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$/; + +export { + EMAIL_REGEX, + ID_CARD_REGEX, + MOBILE_REGEX, + PASSWORD_REGEX, + STRONG_PASSWORD_REGEX, +}; diff --git a/packages/utils/src/validator/validator.ts b/packages/utils/src/validator/validator.ts new file mode 100644 index 000000000..862f33017 --- /dev/null +++ b/packages/utils/src/validator/validator.ts @@ -0,0 +1,16 @@ +import { MOBILE_REGEX } from './regex'; + +/** + * 验证是否为手机号码(中国) + * + * @param value 值 + * @returns 是否为手机号码(中国) + */ +function isMobile(value?: null | string): boolean { + if (!value) { + return false; + } + return MOBILE_REGEX.test(value); +} + +export { isMobile }; diff --git a/playground/src/views/_core/profile/base-setting.vue b/playground/src/views/_core/profile/base-setting.vue new file mode 100644 index 000000000..aa8a4c260 --- /dev/null +++ b/playground/src/views/_core/profile/base-setting.vue @@ -0,0 +1,65 @@ + + diff --git a/playground/src/views/_core/profile/index.vue b/playground/src/views/_core/profile/index.vue new file mode 100644 index 000000000..8740894e3 --- /dev/null +++ b/playground/src/views/_core/profile/index.vue @@ -0,0 +1,49 @@ + + diff --git a/playground/src/views/_core/profile/notification-setting.vue b/playground/src/views/_core/profile/notification-setting.vue new file mode 100644 index 000000000..324a4b393 --- /dev/null +++ b/playground/src/views/_core/profile/notification-setting.vue @@ -0,0 +1,31 @@ + + diff --git a/playground/src/views/_core/profile/password-setting.vue b/playground/src/views/_core/profile/password-setting.vue new file mode 100644 index 000000000..b246bc37e --- /dev/null +++ b/playground/src/views/_core/profile/password-setting.vue @@ -0,0 +1,66 @@ + + diff --git a/playground/src/views/_core/profile/security-setting.vue b/playground/src/views/_core/profile/security-setting.vue new file mode 100644 index 000000000..be30db58e --- /dev/null +++ b/playground/src/views/_core/profile/security-setting.vue @@ -0,0 +1,43 @@ + + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 000000000..9a2bdc5f8 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,22542 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +catalogs: + default: + '@changesets/changelog-github': + specifier: ^0.5.1 + version: 0.5.1 + '@changesets/cli': + specifier: ^2.29.7 + version: 2.29.7 + '@changesets/git': + specifier: ^3.0.4 + version: 3.0.4 + '@clack/prompts': + specifier: ^0.11.0 + version: 0.11.0 + '@commitlint/cli': + specifier: ^19.8.1 + version: 19.8.1 + '@commitlint/config-conventional': + specifier: ^19.8.1 + version: 19.8.1 + '@eslint/js': + specifier: ^9.39.1 + version: 9.39.1 + '@form-create/ant-design-vue': + specifier: ^3.2.27 + version: 3.2.33 + '@form-create/antd-designer': + specifier: ^3.3.0 + version: 3.4.0 + '@form-create/designer': + specifier: ^3.3.0 + version: 3.4.0 + '@form-create/element-ui': + specifier: ^3.2.27 + version: 3.2.33 + '@iconify/json': + specifier: ^2.2.406 + version: 2.2.410 + '@iconify/tailwind': + specifier: ^1.2.0 + version: 1.2.0 + '@iconify/vue': + specifier: ^5.0.0 + version: 5.0.0 + '@intlify/core-base': + specifier: ^11.1.7 + version: 11.2.2 + '@intlify/unplugin-vue-i18n': + specifier: ^6.0.8 + version: 6.0.8 + '@jspm/generator': + specifier: ^2.6.2 + version: 2.7.6 + '@manypkg/get-packages': + specifier: ^3.0.0 + version: 3.1.0 + '@microsoft/fetch-event-source': + specifier: ^2.0.1 + version: 2.0.1 + '@nolebase/vitepress-plugin-git-changelog': + specifier: ^2.18.0 + version: 2.18.2 + '@playwright/test': + specifier: ^1.56.1 + version: 1.57.0 + '@pnpm/workspace.read-manifest': + specifier: ^1000.2.6 + version: 1000.2.7 + '@stylistic/stylelint-plugin': + specifier: ^3.1.3 + version: 3.1.3 + '@tailwindcss/nesting': + specifier: 0.0.0-insiders.565cd3e + version: 0.0.0-insiders.565cd3e + '@tailwindcss/typography': + specifier: ^0.5.16 + version: 0.5.19 + '@tanstack/vue-store': + specifier: ^0.8.0 + version: 0.8.0 + '@tinyflow-ai/vue': + specifier: ^1.1.1 + version: 1.1.9 + '@tinymce/tinymce-vue': + specifier: ^6.1.0 + version: 6.3.0 + '@types/archiver': + specifier: ^6.0.3 + version: 6.0.4 + '@types/codemirror': + specifier: ^5.60.13 + version: 5.60.17 + '@types/crypto-js': + specifier: ^4.2.2 + version: 4.2.2 + '@types/eslint': + specifier: ^9.6.1 + version: 9.6.1 + '@types/html-minifier-terser': + specifier: ^7.0.2 + version: 7.0.2 + '@types/lodash.clonedeep': + specifier: ^4.5.9 + version: 4.5.9 + '@types/lodash.get': + specifier: ^4.4.9 + version: 4.4.9 + '@types/lodash.isequal': + specifier: ^4.5.8 + version: 4.5.8 + '@types/lodash.set': + specifier: ^4.3.9 + version: 4.3.9 + '@types/markdown-it': + specifier: ^14.1.2 + version: 14.1.2 + '@types/node': + specifier: ^24.10.1 + version: 24.10.1 + '@types/nprogress': + specifier: ^0.2.3 + version: 0.2.3 + '@types/postcss-import': + specifier: ^14.0.3 + version: 14.0.3 + '@types/qrcode': + specifier: ^1.5.5 + version: 1.5.6 + '@types/qs': + specifier: ^6.14.0 + version: 6.14.0 + '@types/sortablejs': + specifier: ^1.15.8 + version: 1.15.9 + '@typescript-eslint/eslint-plugin': + specifier: ^8.46.4 + version: 8.48.0 + '@typescript-eslint/parser': + specifier: ^8.46.4 + version: 8.48.0 + '@vee-validate/zod': + specifier: ^4.15.1 + version: 4.15.1 + '@videojs-player/vue': + specifier: ^1.0.0 + version: 1.0.0 + '@vite-pwa/vitepress': + specifier: ^1.0.0 + version: 1.0.1 + '@vitejs/plugin-vue': + specifier: ^6.0.1 + version: 6.0.2 + '@vitejs/plugin-vue-jsx': + specifier: ^5.1.1 + version: 5.1.2 + '@vue/shared': + specifier: ^3.5.24 + version: 3.5.25 + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.6 + '@vueuse/core': + specifier: ^13.4.0 + version: 13.9.0 + '@vueuse/integrations': + specifier: ^14.0.0 + version: 14.1.0 + '@vueuse/motion': + specifier: ^3.0.3 + version: 3.0.3 + ant-design-vue: + specifier: ^4.2.6 + version: 4.2.6 + archiver: + specifier: ^7.0.1 + version: 7.0.1 + autoprefixer: + specifier: ^10.4.22 + version: 10.4.22 + axios: + specifier: ^1.10.0 + version: 1.13.2 + axios-mock-adapter: + specifier: ^2.1.0 + version: 2.1.0 + benz-amr-recorder: + specifier: ^1.1.5 + version: 1.1.5 + bpmn-js: + specifier: ^17.11.1 + version: 17.11.1 + bpmn-js-properties-panel: + specifier: 5.23.0 + version: 5.23.0 + bpmn-js-token-simulation: + specifier: ^0.36.3 + version: 0.36.3 + cac: + specifier: ^6.7.14 + version: 6.7.14 + camunda-bpmn-moddle: + specifier: ^7.0.1 + version: 7.0.1 + chalk: + specifier: ^5.4.1 + version: 5.6.2 + cheerio: + specifier: ^1.1.0 + version: 1.1.2 + circular-dependency-scanner: + specifier: ^2.3.0 + version: 2.3.0 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + codemirror: + specifier: ^5.65.15 + version: 5.65.20 + commitlint-plugin-function-rules: + specifier: ^4.1.1 + version: 4.1.1 + consola: + specifier: ^3.4.2 + version: 3.4.2 + cropperjs: + specifier: ^1.6.2 + version: 1.6.2 + cross-env: + specifier: ^7.0.3 + version: 7.0.3 + crypto-js: + specifier: ^4.2.0 + version: 4.2.0 + cspell: + specifier: ^8.19.4 + version: 8.19.4 + cssnano: + specifier: ^7.0.7 + version: 7.1.2 + cz-git: + specifier: ^1.11.2 + version: 1.12.0 + czg: + specifier: ^1.11.1 + version: 1.12.0 + dayjs: + specifier: ^1.11.13 + version: 1.11.19 + defu: + specifier: ^6.1.4 + version: 6.1.4 + depcheck: + specifier: ^1.4.7 + version: 1.4.7 + diagram-js: + specifier: ^12.8.1 + version: 12.8.1 + dotenv: + specifier: ^16.6.1 + version: 16.6.1 + echarts: + specifier: ^6.0.0 + version: 6.0.0 + element-plus: + specifier: ^2.10.2 + version: 2.11.8 + es-toolkit: + specifier: ^1.41.0 + version: 1.42.0 + eslint: + specifier: ^9.39.1 + version: 9.39.1 + eslint-config-turbo: + specifier: ^2.6.1 + version: 2.6.1 + eslint-plugin-command: + specifier: ^3.3.1 + version: 3.3.1 + eslint-plugin-eslint-comments: + specifier: ^3.2.0 + version: 3.2.0 + eslint-plugin-import-x: + specifier: ^4.16.1 + version: 4.16.1 + eslint-plugin-jsdoc: + specifier: ^61.2.1 + version: 61.4.1 + eslint-plugin-jsonc: + specifier: ^2.21.0 + version: 2.21.0 + eslint-plugin-n: + specifier: ^17.23.1 + version: 17.23.1 + eslint-plugin-no-only-tests: + specifier: ^3.3.0 + version: 3.3.0 + eslint-plugin-perfectionist: + specifier: ^4.15.1 + version: 4.15.1 + eslint-plugin-prettier: + specifier: ^5.5.4 + version: 5.5.4 + eslint-plugin-regexp: + specifier: ^2.10.0 + version: 2.10.0 + eslint-plugin-unicorn: + specifier: ^62.0.0 + version: 62.0.0 + eslint-plugin-unused-imports: + specifier: ^4.3.0 + version: 4.3.0 + eslint-plugin-vitest: + specifier: ^0.5.4 + version: 0.5.4 + eslint-plugin-vue: + specifier: ^10.5.1 + version: 10.6.1 + execa: + specifier: ^9.6.0 + version: 9.6.0 + fast-xml-parser: + specifier: ^4.5.3 + version: 4.5.3 + find-up: + specifier: ^7.0.0 + version: 7.0.0 + get-port: + specifier: ^7.1.0 + version: 7.1.0 + globals: + specifier: ^16.3.0 + version: 16.5.0 + happy-dom: + specifier: ^17.6.3 + version: 17.6.3 + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 + html-minifier-terser: + specifier: ^7.2.0 + version: 7.2.0 + is-ci: + specifier: ^4.1.0 + version: 4.1.0 + jsencrypt: + specifier: ^3.3.2 + version: 3.5.4 + json-bigint: + specifier: ^1.0.0 + version: 1.0.0 + jsonc-eslint-parser: + specifier: ^2.4.1 + version: 2.4.1 + lefthook: + specifier: ^1.13.6 + version: 1.13.6 + lodash.clonedeep: + specifier: ^4.5.0 + version: 4.5.0 + lucide-vue-next: + specifier: ^0.553.0 + version: 0.553.0 + markdown-it: + specifier: ^14.1.0 + version: 14.1.0 + markmap-common: + specifier: ^0.16.0 + version: 0.16.0 + markmap-lib: + specifier: ^0.16.1 + version: 0.16.1 + markmap-toolbar: + specifier: ^0.17.0 + version: 0.17.2 + markmap-view: + specifier: ^0.16.0 + version: 0.16.0 + medium-zoom: + specifier: ^1.1.0 + version: 1.1.0 + naive-ui: + specifier: ^2.42.0 + version: 2.43.2 + nitropack: + specifier: ^2.11.13 + version: 2.12.9 + nprogress: + specifier: ^0.2.0 + version: 0.2.0 + ora: + specifier: ^8.2.0 + version: 8.2.0 + pinia-plugin-persistedstate: + specifier: ^4.4.1 + version: 4.7.1 + pkg-types: + specifier: ^2.2.0 + version: 2.3.0 + playwright: + specifier: ^1.56.1 + version: 1.57.0 + postcss: + specifier: ^8.5.6 + version: 8.5.6 + postcss-antd-fixes: + specifier: ^0.2.0 + version: 0.2.0 + postcss-html: + specifier: ^1.8.0 + version: 1.8.0 + postcss-import: + specifier: ^16.1.1 + version: 16.1.1 + postcss-preset-env: + specifier: ^10.2.4 + version: 10.4.0 + postcss-scss: + specifier: ^4.0.9 + version: 4.0.9 + prettier: + specifier: ^3.6.2 + version: 3.6.2 + prettier-plugin-tailwindcss: + specifier: ^0.7.1 + version: 0.7.1 + publint: + specifier: ^0.3.12 + version: 0.3.15 + qrcode: + specifier: ^1.5.4 + version: 1.5.4 + qs: + specifier: ^6.14.0 + version: 6.14.0 + reka-ui: + specifier: ^2.6.0 + version: 2.6.0 + resolve.exports: + specifier: ^2.0.3 + version: 2.0.3 + rimraf: + specifier: ^6.1.0 + version: 6.1.2 + rollup: + specifier: ^4.44.1 + version: 4.53.3 + rollup-plugin-visualizer: + specifier: ^5.14.0 + version: 5.14.0 + sass: + specifier: ^1.94.0 + version: 1.94.2 + secure-ls: + specifier: ^2.0.0 + version: 2.0.0 + sortablejs: + specifier: ^1.15.6 + version: 1.15.6 + steady-xml: + specifier: ^0.1.0 + version: 0.1.0 + stylelint: + specifier: ^16.21.0 + version: 16.26.0 + stylelint-config-recess-order: + specifier: ^6.1.0 + version: 6.1.0 + stylelint-config-recommended: + specifier: ^17.0.0 + version: 17.0.0 + stylelint-config-recommended-scss: + specifier: ^14.1.0 + version: 14.1.0 + stylelint-config-recommended-vue: + specifier: ^1.6.1 + version: 1.6.1 + stylelint-config-standard: + specifier: ^38.0.0 + version: 38.0.0 + stylelint-order: + specifier: ^7.0.0 + version: 7.0.0 + stylelint-prettier: + specifier: ^5.0.3 + version: 5.0.3 + stylelint-scss: + specifier: ^6.12.1 + version: 6.12.1 + tailwind-merge: + specifier: ^2.6.0 + version: 2.6.0 + tailwindcss: + specifier: ^3.4.18 + version: 3.4.18 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7 + tdesign-vue-next: + specifier: ^1.17.1 + version: 1.17.3 + theme-colors: + specifier: ^0.1.0 + version: 0.1.0 + tinymce: + specifier: ^7.3.0 + version: 7.9.1 + tippy.js: + specifier: ^6.3.7 + version: 6.3.7 + turbo: + specifier: ^2.6.1 + version: 2.6.1 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + unbuild: + specifier: ^3.6.1 + version: 3.6.1 + unplugin-element-plus: + specifier: ^0.11.1 + version: 0.11.1 + vee-validate: + specifier: ^4.15.1 + version: 4.15.1 + video.js: + specifier: ^7.21.6 + version: 7.21.7 + vite: + specifier: ^7.2.2 + version: 7.2.4 + vite-plugin-compression: + specifier: ^0.5.1 + version: 0.5.1 + vite-plugin-dts: + specifier: ^4.5.4 + version: 4.5.4 + vite-plugin-html: + specifier: ^3.2.2 + version: 3.2.2 + vite-plugin-lazy-import: + specifier: ^1.0.7 + version: 1.0.7 + vite-plugin-pwa: + specifier: ^1.0.1 + version: 1.1.0 + vite-plugin-vue-devtools: + specifier: ^8.0.3 + version: 8.0.5 + vitepress: + specifier: ^1.6.3 + version: 1.6.4 + vitepress-plugin-group-icons: + specifier: ^1.6.1 + version: 1.6.5 + vitest: + specifier: ^3.2.4 + version: 3.2.4 + vue-dompurify-html: + specifier: ^5.3.0 + version: 5.3.0 + vue-eslint-parser: + specifier: ^10.2.0 + version: 10.2.0 + vue-i18n: + specifier: ^11.1.7 + version: 11.2.2 + vue-json-viewer: + specifier: ^3.0.4 + version: 3.0.4 + vue-router: + specifier: ^4.5.1 + version: 4.6.3 + vue-tippy: + specifier: ^6.7.1 + version: 6.7.1 + vue-tsc: + specifier: ^3.1.4 + version: 3.1.5 + vue3-print-nb: + specifier: ^0.1.4 + version: 0.1.4 + vue3-signature: + specifier: ^0.2.4 + version: 0.2.4 + vuedraggable: + specifier: ^4.1.0 + version: 4.1.0 + vxe-pc-ui: + specifier: ^4.10.22 + version: 4.10.31 + vxe-table: + specifier: ^4.17.14 + version: 4.17.20 + watermark-js-plus: + specifier: ^1.6.2 + version: 1.6.3 + zod: + specifier: ^3.25.67 + version: 3.25.76 + zod-defaults: + specifier: 0.1.3 + version: 0.1.3 + +overrides: + '@ast-grep/napi': ^0.39.9 + '@ctrl/tinycolor': ^4.1.0 + clsx: ^2.1.1 + esbuild: 0.25.3 + jiti: ^2.6.1 + pinia: ^3.0.3 + vue: ^3.5.24 + +importers: + + .: + devDependencies: + '@changesets/changelog-github': + specifier: 'catalog:' + version: 0.5.1 + '@changesets/cli': + specifier: 'catalog:' + version: 2.29.7(@types/node@24.10.1) + '@playwright/test': + specifier: 'catalog:' + version: 1.57.0 + '@types/node': + specifier: 'catalog:' + version: 24.10.1 + '@vben/commitlint-config': + specifier: workspace:* + version: link:internal/lint-configs/commitlint-config + '@vben/eslint-config': + specifier: workspace:* + version: link:internal/lint-configs/eslint-config + '@vben/prettier-config': + specifier: workspace:* + version: link:internal/lint-configs/prettier-config + '@vben/stylelint-config': + specifier: workspace:* + version: link:internal/lint-configs/stylelint-config + '@vben/tailwind-config': + specifier: workspace:* + version: link:internal/tailwind-config + '@vben/tsconfig': + specifier: workspace:* + version: link:internal/tsconfig + '@vben/turbo-run': + specifier: workspace:* + version: link:scripts/turbo-run + '@vben/vite-config': + specifier: workspace:* + version: link:internal/vite-config + '@vben/vsh': + specifier: workspace:* + version: link:scripts/vsh + '@vitejs/plugin-vue': + specifier: 'catalog:' + version: 6.0.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': + specifier: 'catalog:' + version: 5.1.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3)) + '@vue/test-utils': + specifier: 'catalog:' + version: 2.4.6 + autoprefixer: + specifier: 'catalog:' + version: 10.4.22(postcss@8.5.6) + cross-env: + specifier: 'catalog:' + version: 7.0.3 + cspell: + specifier: 'catalog:' + version: 8.19.4 + happy-dom: + specifier: 'catalog:' + version: 17.6.3 + is-ci: + specifier: 'catalog:' + version: 4.1.0 + lefthook: + specifier: 'catalog:' + version: 1.13.6 + playwright: + specifier: 'catalog:' + version: 1.57.0 + rimraf: + specifier: 'catalog:' + version: 6.1.2 + tailwindcss: + specifier: 'catalog:' + version: 3.4.18(yaml@2.8.1) + turbo: + specifier: 'catalog:' + version: 2.6.1 + typescript: + specifier: 'catalog:' + version: 5.9.3 + unbuild: + specifier: 'catalog:' + version: 3.6.1(sass@1.94.2)(typescript@5.9.3)(vue-tsc@3.1.5(typescript@5.9.3))(vue@3.5.25(typescript@5.9.3)) + vite: + specifier: 'catalog:' + version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vitest: + specifier: 'catalog:' + version: 3.2.4(@types/node@24.10.1)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-tsc: + specifier: 'catalog:' + version: 3.1.5(typescript@5.9.3) + + apps/web-antd: + dependencies: + '@form-create/ant-design-vue': + specifier: 'catalog:' + version: 3.2.33(vue@3.5.25(typescript@5.9.3)) + '@form-create/antd-designer': + specifier: 'catalog:' + version: 3.4.0(vue@3.5.25(typescript@5.9.3)) + '@tinymce/tinymce-vue': + specifier: 'catalog:' + version: 6.3.0(tinymce@7.9.1)(vue@3.5.25(typescript@5.9.3)) + '@vben/access': + specifier: workspace:* + version: link:../../packages/effects/access + '@vben/common-ui': + specifier: workspace:* + version: link:../../packages/effects/common-ui + '@vben/constants': + specifier: workspace:* + version: link:../../packages/constants + '@vben/hooks': + specifier: workspace:* + version: link:../../packages/effects/hooks + '@vben/icons': + specifier: workspace:* + version: link:../../packages/icons + '@vben/layouts': + specifier: workspace:* + version: link:../../packages/effects/layouts + '@vben/locales': + specifier: workspace:* + version: link:../../packages/locales + '@vben/plugins': + specifier: workspace:* + version: link:../../packages/effects/plugins + '@vben/preferences': + specifier: workspace:* + version: link:../../packages/preferences + '@vben/request': + specifier: workspace:* + version: link:../../packages/effects/request + '@vben/stores': + specifier: workspace:* + version: link:../../packages/stores + '@vben/styles': + specifier: workspace:* + version: link:../../packages/styles + '@vben/types': + specifier: workspace:* + version: link:../../packages/types + '@vben/utils': + specifier: workspace:* + version: link:../../packages/utils + '@videojs-player/vue': + specifier: 'catalog:' + version: 1.0.0(@types/video.js@7.3.58)(video.js@7.21.7)(vue@3.5.25(typescript@5.9.3)) + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + '@vueuse/integrations': + specifier: 'catalog:' + version: 14.1.0(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(focus-trap@7.6.6)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.6)(vue@3.5.25(typescript@5.9.3)) + ant-design-vue: + specifier: 'catalog:' + version: 4.2.6(vue@3.5.25(typescript@5.9.3)) + benz-amr-recorder: + specifier: 'catalog:' + version: 1.1.5 + bpmn-js: + specifier: 'catalog:' + version: 17.11.1 + bpmn-js-properties-panel: + specifier: 'catalog:' + version: 5.23.0(@bpmn-io/properties-panel@3.34.0)(bpmn-js@17.11.1)(camunda-bpmn-js-behaviors@1.11.3(bpmn-js@17.11.1)(camunda-bpmn-moddle@7.0.1)(zeebe-bpmn-moddle@1.11.0))(diagram-js@12.8.1) + bpmn-js-token-simulation: + specifier: 'catalog:' + version: 0.36.3 + camunda-bpmn-moddle: + specifier: 'catalog:' + version: 7.0.1 + cropperjs: + specifier: 'catalog:' + version: 1.6.2 + dayjs: + specifier: 'catalog:' + version: 1.11.19 + diagram-js: + specifier: 'catalog:' + version: 12.8.1 + fast-xml-parser: + specifier: 'catalog:' + version: 4.5.3 + highlight.js: + specifier: 'catalog:' + version: 11.11.1 + pinia: + specifier: ^3.0.3 + version: 3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + steady-xml: + specifier: 'catalog:' + version: 0.1.0 + tinymce: + specifier: 'catalog:' + version: 7.9.1 + video.js: + specifier: 'catalog:' + version: 7.21.7 + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-dompurify-html: + specifier: 'catalog:' + version: 5.3.0(vue@3.5.25(typescript@5.9.3)) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + vue3-print-nb: + specifier: 'catalog:' + version: 0.1.4(typescript@5.9.3) + vue3-signature: + specifier: 'catalog:' + version: 0.2.4(vue@3.5.25(typescript@5.9.3)) + vuedraggable: + specifier: 'catalog:' + version: 4.1.0(vue@3.5.25(typescript@5.9.3)) + + apps/web-ele: + dependencies: + '@form-create/designer': + specifier: 'catalog:' + version: 3.4.0(vue@3.5.25(typescript@5.9.3)) + '@form-create/element-ui': + specifier: 'catalog:' + version: 3.2.33(vue@3.5.25(typescript@5.9.3)) + '@tinymce/tinymce-vue': + specifier: 'catalog:' + version: 6.3.0(tinymce@7.9.1)(vue@3.5.25(typescript@5.9.3)) + '@vben/access': + specifier: workspace:* + version: link:../../packages/effects/access + '@vben/common-ui': + specifier: workspace:* + version: link:../../packages/effects/common-ui + '@vben/constants': + specifier: workspace:* + version: link:../../packages/constants + '@vben/hooks': + specifier: workspace:* + version: link:../../packages/effects/hooks + '@vben/icons': + specifier: workspace:* + version: link:../../packages/icons + '@vben/layouts': + specifier: workspace:* + version: link:../../packages/effects/layouts + '@vben/locales': + specifier: workspace:* + version: link:../../packages/locales + '@vben/plugins': + specifier: workspace:* + version: link:../../packages/effects/plugins + '@vben/preferences': + specifier: workspace:* + version: link:../../packages/preferences + '@vben/request': + specifier: workspace:* + version: link:../../packages/effects/request + '@vben/stores': + specifier: workspace:* + version: link:../../packages/stores + '@vben/styles': + specifier: workspace:* + version: link:../../packages/styles + '@vben/types': + specifier: workspace:* + version: link:../../packages/types + '@vben/utils': + specifier: workspace:* + version: link:../../packages/utils + '@videojs-player/vue': + specifier: 'catalog:' + version: 1.0.0(@types/video.js@7.3.58)(video.js@7.21.7)(vue@3.5.25(typescript@5.9.3)) + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + '@vueuse/integrations': + specifier: 'catalog:' + version: 14.1.0(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(focus-trap@7.6.6)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.6)(vue@3.5.25(typescript@5.9.3)) + benz-amr-recorder: + specifier: 'catalog:' + version: 1.1.5 + cropperjs: + specifier: 'catalog:' + version: 1.6.2 + dayjs: + specifier: 'catalog:' + version: 1.11.19 + element-plus: + specifier: 'catalog:' + version: 2.11.8(vue@3.5.25(typescript@5.9.3)) + highlight.js: + specifier: 'catalog:' + version: 11.11.1 + pinia: + specifier: ^3.0.3 + version: 3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + tinymce: + specifier: 'catalog:' + version: 7.9.1 + video.js: + specifier: 'catalog:' + version: 7.21.7 + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-dompurify-html: + specifier: 'catalog:' + version: 5.3.0(vue@3.5.25(typescript@5.9.3)) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + vue3-print-nb: + specifier: 'catalog:' + version: 0.1.4(typescript@5.9.3) + vue3-signature: + specifier: 'catalog:' + version: 0.2.4(vue@3.5.25(typescript@5.9.3)) + vuedraggable: + specifier: 'catalog:' + version: 4.1.0(vue@3.5.25(typescript@5.9.3)) + devDependencies: + unplugin-element-plus: + specifier: 'catalog:' + version: 0.11.1(magicast@0.5.1) + + apps/web-naive: + dependencies: + '@vben/access': + specifier: workspace:* + version: link:../../packages/effects/access + '@vben/common-ui': + specifier: workspace:* + version: link:../../packages/effects/common-ui + '@vben/constants': + specifier: workspace:* + version: link:../../packages/constants + '@vben/hooks': + specifier: workspace:* + version: link:../../packages/effects/hooks + '@vben/icons': + specifier: workspace:* + version: link:../../packages/icons + '@vben/layouts': + specifier: workspace:* + version: link:../../packages/effects/layouts + '@vben/locales': + specifier: workspace:* + version: link:../../packages/locales + '@vben/plugins': + specifier: workspace:* + version: link:../../packages/effects/plugins + '@vben/preferences': + specifier: workspace:* + version: link:../../packages/preferences + '@vben/request': + specifier: workspace:* + version: link:../../packages/effects/request + '@vben/stores': + specifier: workspace:* + version: link:../../packages/stores + '@vben/styles': + specifier: workspace:* + version: link:../../packages/styles + '@vben/types': + specifier: workspace:* + version: link:../../packages/types + '@vben/utils': + specifier: workspace:* + version: link:../../packages/utils + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + dayjs: + specifier: 'catalog:' + version: 1.11.19 + naive-ui: + specifier: 'catalog:' + version: 2.43.2(vue@3.5.25(typescript@5.9.3)) + pinia: + specifier: ^3.0.3 + version: 3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + + apps/web-tdesign: + dependencies: + '@tinymce/tinymce-vue': + specifier: 'catalog:' + version: 6.3.0(tinymce@7.9.1)(vue@3.5.25(typescript@5.9.3)) + '@vben/access': + specifier: workspace:* + version: link:../../packages/effects/access + '@vben/common-ui': + specifier: workspace:* + version: link:../../packages/effects/common-ui + '@vben/constants': + specifier: workspace:* + version: link:../../packages/constants + '@vben/hooks': + specifier: workspace:* + version: link:../../packages/effects/hooks + '@vben/icons': + specifier: workspace:* + version: link:../../packages/icons + '@vben/layouts': + specifier: workspace:* + version: link:../../packages/effects/layouts + '@vben/locales': + specifier: workspace:* + version: link:../../packages/locales + '@vben/plugins': + specifier: workspace:* + version: link:../../packages/effects/plugins + '@vben/preferences': + specifier: workspace:* + version: link:../../packages/preferences + '@vben/request': + specifier: workspace:* + version: link:../../packages/effects/request + '@vben/stores': + specifier: workspace:* + version: link:../../packages/stores + '@vben/styles': + specifier: workspace:* + version: link:../../packages/styles + '@vben/types': + specifier: workspace:* + version: link:../../packages/types + '@vben/utils': + specifier: workspace:* + version: link:../../packages/utils + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + cropperjs: + specifier: 'catalog:' + version: 1.6.2 + dayjs: + specifier: 'catalog:' + version: 1.11.19 + es-toolkit: + specifier: 'catalog:' + version: 1.42.0 + pinia: + specifier: ^3.0.3 + version: 3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + tdesign-vue-next: + specifier: 'catalog:' + version: 1.17.3(vue@3.5.25(typescript@5.9.3)) + tinymce: + specifier: 'catalog:' + version: 7.9.1 + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + + docs: + dependencies: + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../packages/@core/ui-kit/shadcn-ui + '@vben/common-ui': + specifier: workspace:* + version: link:../packages/effects/common-ui + '@vben/locales': + specifier: workspace:* + version: link:../packages/locales + '@vben/plugins': + specifier: workspace:* + version: link:../packages/effects/plugins + '@vben/styles': + specifier: workspace:* + version: link:../packages/styles + ant-design-vue: + specifier: 'catalog:' + version: 4.2.6(vue@3.5.25(typescript@5.9.3)) + lucide-vue-next: + specifier: 'catalog:' + version: 0.553.0(vue@3.5.25(typescript@5.9.3)) + medium-zoom: + specifier: 'catalog:' + version: 1.1.0 + reka-ui: + specifier: 'catalog:' + version: 2.6.0(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + vitepress-plugin-group-icons: + specifier: 'catalog:' + version: 1.6.5(vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)) + devDependencies: + '@nolebase/vitepress-plugin-git-changelog': + specifier: 'catalog:' + version: 2.18.2(vitepress@1.6.4(@algolia/client-search@5.45.0)(@types/node@24.10.1)(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(less@4.4.2)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.94.2)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.44.1)(typescript@5.9.3))(vue@3.5.25(typescript@5.9.3)) + '@vben/vite-config': + specifier: workspace:* + version: link:../internal/vite-config + '@vite-pwa/vitepress': + specifier: 'catalog:' + version: 1.0.1(vite-plugin-pwa@1.1.0(vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1))(workbox-build@7.4.0)(workbox-window@7.4.0)) + vitepress: + specifier: 'catalog:' + version: 1.6.4(@algolia/client-search@5.45.0)(@types/node@24.10.1)(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(less@4.4.2)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.94.2)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.44.1)(typescript@5.9.3) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + internal/lint-configs/commitlint-config: + dependencies: + '@commitlint/cli': + specifier: 'catalog:' + version: 19.8.1(@types/node@24.10.1)(typescript@5.9.3) + '@commitlint/config-conventional': + specifier: 'catalog:' + version: 19.8.1 + '@vben/node-utils': + specifier: workspace:* + version: link:../../node-utils + commitlint-plugin-function-rules: + specifier: 'catalog:' + version: 4.1.1(@commitlint/lint@19.8.1) + cz-git: + specifier: 'catalog:' + version: 1.12.0 + czg: + specifier: 'catalog:' + version: 1.12.0 + + internal/lint-configs/eslint-config: + dependencies: + eslint-config-turbo: + specifier: 'catalog:' + version: 2.6.1(eslint@9.39.1(jiti@2.6.1))(turbo@2.6.1) + eslint-plugin-command: + specifier: 'catalog:' + version: 3.3.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-import-x: + specifier: 'catalog:' + version: 4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + devDependencies: + '@eslint/js': + specifier: 'catalog:' + version: 9.39.1 + '@types/eslint': + specifier: 'catalog:' + version: 9.6.1 + '@typescript-eslint/eslint-plugin': + specifier: 'catalog:' + version: 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': + specifier: 'catalog:' + version: 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + eslint: + specifier: 'catalog:' + version: 9.39.1(jiti@2.6.1) + eslint-plugin-eslint-comments: + specifier: 'catalog:' + version: 3.2.0(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-jsdoc: + specifier: 'catalog:' + version: 61.4.1(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-jsonc: + specifier: 'catalog:' + version: 2.21.0(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-n: + specifier: 'catalog:' + version: 17.23.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + eslint-plugin-no-only-tests: + specifier: 'catalog:' + version: 3.3.0 + eslint-plugin-perfectionist: + specifier: 'catalog:' + version: 4.15.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + eslint-plugin-prettier: + specifier: 'catalog:' + version: 5.5.4(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2) + eslint-plugin-regexp: + specifier: 'catalog:' + version: 2.10.0(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-unicorn: + specifier: 'catalog:' + version: 62.0.0(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-unused-imports: + specifier: 'catalog:' + version: 4.3.0(@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)) + eslint-plugin-vitest: + specifier: 'catalog:' + version: 0.5.4(@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4(@types/node@24.10.1)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + eslint-plugin-vue: + specifier: 'catalog:' + version: 10.6.1(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1))) + globals: + specifier: 'catalog:' + version: 16.5.0 + jsonc-eslint-parser: + specifier: 'catalog:' + version: 2.4.1 + vue-eslint-parser: + specifier: 'catalog:' + version: 10.2.0(eslint@9.39.1(jiti@2.6.1)) + + internal/lint-configs/prettier-config: + dependencies: + prettier: + specifier: 'catalog:' + version: 3.6.2 + prettier-plugin-tailwindcss: + specifier: 'catalog:' + version: 0.7.1(prettier@3.6.2) + + internal/lint-configs/stylelint-config: + dependencies: + '@stylistic/stylelint-plugin': + specifier: 'catalog:' + version: 3.1.3(stylelint@16.26.0(typescript@5.9.3)) + stylelint-config-recess-order: + specifier: 'catalog:' + version: 6.1.0(stylelint@16.26.0(typescript@5.9.3)) + stylelint-scss: + specifier: 'catalog:' + version: 6.12.1(stylelint@16.26.0(typescript@5.9.3)) + devDependencies: + postcss: + specifier: 'catalog:' + version: 8.5.6 + postcss-html: + specifier: 'catalog:' + version: 1.8.0 + postcss-scss: + specifier: 'catalog:' + version: 4.0.9(postcss@8.5.6) + prettier: + specifier: 'catalog:' + version: 3.6.2 + stylelint: + specifier: 'catalog:' + version: 16.26.0(typescript@5.9.3) + stylelint-config-recommended: + specifier: 'catalog:' + version: 17.0.0(stylelint@16.26.0(typescript@5.9.3)) + stylelint-config-recommended-scss: + specifier: 'catalog:' + version: 14.1.0(postcss@8.5.6)(stylelint@16.26.0(typescript@5.9.3)) + stylelint-config-recommended-vue: + specifier: 'catalog:' + version: 1.6.1(postcss-html@1.8.0)(stylelint@16.26.0(typescript@5.9.3)) + stylelint-config-standard: + specifier: 'catalog:' + version: 38.0.0(stylelint@16.26.0(typescript@5.9.3)) + stylelint-order: + specifier: 'catalog:' + version: 7.0.0(stylelint@16.26.0(typescript@5.9.3)) + stylelint-prettier: + specifier: 'catalog:' + version: 5.0.3(prettier@3.6.2)(stylelint@16.26.0(typescript@5.9.3)) + + internal/node-utils: + dependencies: + '@changesets/git': + specifier: 'catalog:' + version: 3.0.4 + '@manypkg/get-packages': + specifier: 'catalog:' + version: 3.1.0 + chalk: + specifier: 'catalog:' + version: 5.6.2 + consola: + specifier: 'catalog:' + version: 3.4.2 + dayjs: + specifier: 'catalog:' + version: 1.11.19 + execa: + specifier: 'catalog:' + version: 9.6.0 + find-up: + specifier: 'catalog:' + version: 7.0.0 + ora: + specifier: 'catalog:' + version: 8.2.0 + pkg-types: + specifier: 'catalog:' + version: 2.3.0 + prettier: + specifier: 'catalog:' + version: 3.6.2 + rimraf: + specifier: 'catalog:' + version: 6.1.2 + + internal/tailwind-config: + dependencies: + '@iconify/json': + specifier: 'catalog:' + version: 2.2.410 + '@iconify/tailwind': + specifier: 'catalog:' + version: 1.2.0 + '@manypkg/get-packages': + specifier: 'catalog:' + version: 3.1.0 + '@tailwindcss/nesting': + specifier: 'catalog:' + version: 0.0.0-insiders.565cd3e(postcss@8.5.6) + '@tailwindcss/typography': + specifier: 'catalog:' + version: 0.5.19(tailwindcss@3.4.18(yaml@2.8.1)) + autoprefixer: + specifier: 'catalog:' + version: 10.4.22(postcss@8.5.6) + cssnano: + specifier: 'catalog:' + version: 7.1.2(postcss@8.5.6) + jiti: + specifier: ^2.6.1 + version: 2.6.1 + postcss: + specifier: 'catalog:' + version: 8.5.6 + postcss-antd-fixes: + specifier: 'catalog:' + version: 0.2.0(postcss@8.5.6) + postcss-import: + specifier: 'catalog:' + version: 16.1.1(postcss@8.5.6) + postcss-preset-env: + specifier: 'catalog:' + version: 10.4.0(postcss@8.5.6) + tailwindcss: + specifier: 'catalog:' + version: 3.4.18(yaml@2.8.1) + tailwindcss-animate: + specifier: 'catalog:' + version: 1.0.7(tailwindcss@3.4.18(yaml@2.8.1)) + devDependencies: + '@types/postcss-import': + specifier: 'catalog:' + version: 14.0.3 + + internal/tsconfig: + dependencies: + '@vben/types': + specifier: workspace:* + version: link:../../packages/types + vite: + specifier: 'catalog:' + version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + + internal/vite-config: + dependencies: + '@intlify/unplugin-vue-i18n': + specifier: 'catalog:' + version: 6.0.8(@vue/compiler-dom@3.5.25)(eslint@9.39.1(jiti@2.6.1))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.2.2(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)) + '@jspm/generator': + specifier: 'catalog:' + version: 2.7.6 + archiver: + specifier: 'catalog:' + version: 7.0.1 + cheerio: + specifier: 'catalog:' + version: 1.1.2 + get-port: + specifier: 'catalog:' + version: 7.1.0 + html-minifier-terser: + specifier: 'catalog:' + version: 7.2.0 + nitropack: + specifier: 'catalog:' + version: 2.12.9 + resolve.exports: + specifier: 'catalog:' + version: 2.0.3 + vite-plugin-pwa: + specifier: 'catalog:' + version: 1.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(workbox-build@7.4.0)(workbox-window@7.4.0) + vite-plugin-vue-devtools: + specifier: 'catalog:' + version: 8.0.5(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3)) + devDependencies: + '@pnpm/workspace.read-manifest': + specifier: 'catalog:' + version: 1000.2.7 + '@types/archiver': + specifier: 'catalog:' + version: 6.0.4 + '@types/html-minifier-terser': + specifier: 'catalog:' + version: 7.0.2 + '@vben/node-utils': + specifier: workspace:* + version: link:../node-utils + '@vitejs/plugin-vue': + specifier: 'catalog:' + version: 6.0.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': + specifier: 'catalog:' + version: 5.1.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3)) + dayjs: + specifier: 'catalog:' + version: 1.11.19 + dotenv: + specifier: 'catalog:' + version: 16.6.1 + rollup: + specifier: 'catalog:' + version: 4.53.3 + rollup-plugin-visualizer: + specifier: 'catalog:' + version: 5.14.0(rollup@4.53.3) + sass: + specifier: 'catalog:' + version: 1.94.2 + vite: + specifier: 'catalog:' + version: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vite-plugin-compression: + specifier: 'catalog:' + version: 0.5.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + vite-plugin-dts: + specifier: 'catalog:' + version: 4.5.4(@types/node@24.10.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + vite-plugin-html: + specifier: 'catalog:' + version: 3.2.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + vite-plugin-lazy-import: + specifier: 'catalog:' + version: 1.0.7 + + packages/@core/base/design: {} + + packages/@core/base/icons: + dependencies: + '@iconify/vue': + specifier: 'catalog:' + version: 5.0.0(vue@3.5.25(typescript@5.9.3)) + lucide-vue-next: + specifier: 'catalog:' + version: 0.553.0(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + packages/@core/base/shared: + dependencies: + '@ctrl/tinycolor': + specifier: ^4.1.0 + version: 4.2.0 + '@tanstack/vue-store': + specifier: 'catalog:' + version: 0.8.0(vue@3.5.25(typescript@5.9.3)) + '@vue/shared': + specifier: 'catalog:' + version: 3.5.25 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + crypto-js: + specifier: 'catalog:' + version: 4.2.0 + dayjs: + specifier: 'catalog:' + version: 1.11.19 + defu: + specifier: 'catalog:' + version: 6.1.4 + es-toolkit: + specifier: 'catalog:' + version: 1.42.0 + jsencrypt: + specifier: 'catalog:' + version: 3.5.4 + lodash.clonedeep: + specifier: 'catalog:' + version: 4.5.0 + nprogress: + specifier: 'catalog:' + version: 0.2.0 + tailwind-merge: + specifier: 'catalog:' + version: 2.6.0 + theme-colors: + specifier: 'catalog:' + version: 0.1.0 + devDependencies: + '@types/crypto-js': + specifier: 'catalog:' + version: 4.2.2 + '@types/lodash.clonedeep': + specifier: 'catalog:' + version: 4.5.9 + '@types/lodash.get': + specifier: 'catalog:' + version: 4.4.9 + '@types/lodash.isequal': + specifier: 'catalog:' + version: 4.5.8 + '@types/lodash.set': + specifier: 'catalog:' + version: 4.3.9 + '@types/nprogress': + specifier: 'catalog:' + version: 0.2.3 + + packages/@core/base/typings: + dependencies: + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + + packages/@core/composables: + dependencies: + '@vben-core/shared': + specifier: workspace:* + version: link:../base/shared + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + reka-ui: + specifier: 'catalog:' + version: 2.6.0(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + sortablejs: + specifier: 'catalog:' + version: 1.15.6 + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + devDependencies: + '@types/sortablejs': + specifier: 'catalog:' + version: 1.15.9 + + packages/@core/preferences: + dependencies: + '@vben-core/shared': + specifier: workspace:* + version: link:../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + packages/@core/ui-kit/form-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vee-validate/zod': + specifier: 'catalog:' + version: 4.15.1(vue@3.5.25(typescript@5.9.3))(zod@3.25.76) + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + vee-validate: + specifier: 'catalog:' + version: 4.15.1(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + zod: + specifier: 'catalog:' + version: 3.25.76 + zod-defaults: + specifier: 'catalog:' + version: 0.1.3(zod@3.25.76) + + packages/@core/ui-kit/layout-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + packages/@core/ui-kit/menu-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + packages/@core/ui-kit/popup-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + packages/@core/ui-kit/shadcn-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shared': + specifier: workspace:* + version: link:../../base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + class-variance-authority: + specifier: 'catalog:' + version: 0.7.1 + lucide-vue-next: + specifier: 'catalog:' + version: 0.553.0(vue@3.5.25(typescript@5.9.3)) + reka-ui: + specifier: 'catalog:' + version: 2.6.0(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + vee-validate: + specifier: 'catalog:' + version: 4.15.1(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + packages/@core/ui-kit/tabs-ui: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../composables + '@vben-core/icons': + specifier: workspace:* + version: link:../../base/icons + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../shadcn-ui + '@vben-core/typings': + specifier: workspace:* + version: link:../../base/typings + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + packages/constants: + dependencies: + '@vben-core/shared': + specifier: workspace:* + version: link:../@core/base/shared + + packages/effects/access: + dependencies: + '@vben/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben/stores': + specifier: workspace:* + version: link:../../stores + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vben/utils': + specifier: workspace:* + version: link:../../utils + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + + packages/effects/common-ui: + dependencies: + '@vben-core/form-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/form-ui + '@vben-core/popup-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/popup-ui + '@vben-core/preferences': + specifier: workspace:* + version: link:../../@core/preferences + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../@core/base/shared + '@vben/constants': + specifier: workspace:* + version: link:../../constants + '@vben/hooks': + specifier: workspace:* + version: link:../hooks + '@vben/icons': + specifier: workspace:* + version: link:../../icons + '@vben/locales': + specifier: workspace:* + version: link:../../locales + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + '@vueuse/integrations': + specifier: 'catalog:' + version: 14.1.0(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(focus-trap@7.6.6)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.6)(vue@3.5.25(typescript@5.9.3)) + json-bigint: + specifier: 'catalog:' + version: 1.0.0 + qrcode: + specifier: 'catalog:' + version: 1.5.4 + tippy.js: + specifier: 'catalog:' + version: 6.3.7 + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-json-viewer: + specifier: 'catalog:' + version: 3.0.4(vue@3.5.25(typescript@5.9.3)) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + vue-tippy: + specifier: 'catalog:' + version: 6.7.1(vue@3.5.25(typescript@5.9.3)) + devDependencies: + '@types/qrcode': + specifier: 'catalog:' + version: 1.5.6 + + packages/effects/hooks: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../@core/composables + '@vben/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben/stores': + specifier: workspace:* + version: link:../../stores + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vben/utils': + specifier: workspace:* + version: link:../../utils + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + watermark-js-plus: + specifier: 'catalog:' + version: 1.6.3 + + packages/effects/layouts: + dependencies: + '@vben-core/composables': + specifier: workspace:* + version: link:../../@core/composables + '@vben-core/form-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/form-ui + '@vben-core/layout-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/layout-ui + '@vben-core/menu-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/menu-ui + '@vben-core/popup-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/popup-ui + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../@core/base/shared + '@vben-core/tabs-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/tabs-ui + '@vben/constants': + specifier: workspace:* + version: link:../../constants + '@vben/hooks': + specifier: workspace:* + version: link:../hooks + '@vben/icons': + specifier: workspace:* + version: link:../../icons + '@vben/locales': + specifier: workspace:* + version: link:../../locales + '@vben/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben/stores': + specifier: workspace:* + version: link:../../stores + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vben/utils': + specifier: workspace:* + version: link:../../utils + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + + packages/effects/plugins: + dependencies: + '@tinyflow-ai/vue': + specifier: 'catalog:' + version: 1.1.9(svelte@5.45.2)(vue@3.5.25(typescript@5.9.3)) + '@vben-core/form-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/form-ui + '@vben-core/shadcn-ui': + specifier: workspace:* + version: link:../../@core/ui-kit/shadcn-ui + '@vben-core/shared': + specifier: workspace:* + version: link:../../@core/base/shared + '@vben/hooks': + specifier: workspace:* + version: link:../hooks + '@vben/icons': + specifier: workspace:* + version: link:../../icons + '@vben/locales': + specifier: workspace:* + version: link:../../locales + '@vben/preferences': + specifier: workspace:* + version: link:../../preferences + '@vben/types': + specifier: workspace:* + version: link:../../types + '@vben/utils': + specifier: workspace:* + version: link:../../utils + '@vueuse/core': + specifier: 'catalog:' + version: 13.9.0(vue@3.5.25(typescript@5.9.3)) + '@vueuse/motion': + specifier: 'catalog:' + version: 3.0.3(magicast@0.5.1)(vue@3.5.25(typescript@5.9.3)) + codemirror: + specifier: 'catalog:' + version: 5.65.20 + echarts: + specifier: 'catalog:' + version: 6.0.0 + markdown-it: + specifier: 'catalog:' + version: 14.1.0 + markmap-common: + specifier: 'catalog:' + version: 0.16.0 + markmap-lib: + specifier: 'catalog:' + version: 0.16.1(markmap-common@0.16.0) + markmap-toolbar: + specifier: 'catalog:' + version: 0.17.2(markmap-common@0.16.0) + markmap-view: + specifier: 'catalog:' + version: 0.16.0(markmap-common@0.16.0) + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vxe-pc-ui: + specifier: 'catalog:' + version: 4.10.31(vue@3.5.25(typescript@5.9.3)) + vxe-table: + specifier: 'catalog:' + version: 4.17.20(vue@3.5.25(typescript@5.9.3)) + devDependencies: + '@types/codemirror': + specifier: 'catalog:' + version: 5.60.17 + '@types/markdown-it': + specifier: 'catalog:' + version: 14.1.2 + + packages/effects/request: + dependencies: + '@microsoft/fetch-event-source': + specifier: 'catalog:' + version: 2.0.1 + '@vben/locales': + specifier: workspace:* + version: link:../../locales + '@vben/utils': + specifier: workspace:* + version: link:../../utils + axios: + specifier: 'catalog:' + version: 1.13.2 + qs: + specifier: 'catalog:' + version: 6.14.0 + devDependencies: + '@types/qs': + specifier: 'catalog:' + version: 6.14.0 + axios-mock-adapter: + specifier: 'catalog:' + version: 2.1.0(axios@1.13.2) + + packages/icons: + dependencies: + '@vben-core/icons': + specifier: workspace:* + version: link:../@core/base/icons + + packages/locales: + dependencies: + '@intlify/core-base': + specifier: 'catalog:' + version: 11.2.2 + '@vben-core/composables': + specifier: workspace:* + version: link:../@core/composables + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-i18n: + specifier: 'catalog:' + version: 11.2.2(vue@3.5.25(typescript@5.9.3)) + + packages/preferences: + dependencies: + '@vben-core/preferences': + specifier: workspace:* + version: link:../@core/preferences + '@vben-core/typings': + specifier: workspace:* + version: link:../@core/base/typings + + packages/stores: + dependencies: + '@vben-core/preferences': + specifier: workspace:* + version: link:../@core/preferences + '@vben-core/shared': + specifier: workspace:* + version: link:../@core/base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../@core/base/typings + pinia: + specifier: ^3.0.3 + version: 3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + pinia-plugin-persistedstate: + specifier: 'catalog:' + version: 4.7.1(@nuxt/kit@4.2.1(magicast@0.5.1))(pinia@3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))) + secure-ls: + specifier: 'catalog:' + version: 2.0.0 + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + + packages/styles: + dependencies: + '@vben-core/design': + specifier: workspace:* + version: link:../@core/base/design + + packages/types: + dependencies: + '@vben-core/typings': + specifier: workspace:* + version: link:../@core/base/typings + vue: + specifier: ^3.5.24 + version: 3.5.25(typescript@5.9.3) + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + + packages/utils: + dependencies: + '@vben-core/shared': + specifier: workspace:* + version: link:../@core/base/shared + '@vben-core/typings': + specifier: workspace:* + version: link:../@core/base/typings + vue-router: + specifier: 'catalog:' + version: 4.6.3(vue@3.5.25(typescript@5.9.3)) + + scripts/turbo-run: + dependencies: + '@clack/prompts': + specifier: 'catalog:' + version: 0.11.0 + '@vben/node-utils': + specifier: workspace:* + version: link:../../internal/node-utils + cac: + specifier: 'catalog:' + version: 6.7.14 + + scripts/vsh: + dependencies: + '@vben/node-utils': + specifier: workspace:* + version: link:../../internal/node-utils + cac: + specifier: 'catalog:' + version: 6.7.14 + circular-dependency-scanner: + specifier: 'catalog:' + version: 2.3.0 + depcheck: + specifier: 'catalog:' + version: 1.4.7 + publint: + specifier: 'catalog:' + version: 0.3.15 + +packages: + + '@algolia/abtesting@1.11.0': + resolution: {integrity: sha512-a7oQ8dwiyoyVmzLY0FcuBqyqcNSq78qlcOtHmNBumRlHCSnXDcuoYGBGPN1F6n8JoGhviDDsIaF/oQrzTzs6Lg==} + engines: {node: '>= 14.0.0'} + + '@algolia/autocomplete-core@1.17.7': + resolution: {integrity: sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q==} + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7': + resolution: {integrity: sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A==} + peerDependencies: + search-insights: '>= 1 < 3' + + '@algolia/autocomplete-preset-algolia@1.17.7': + resolution: {integrity: sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/autocomplete-shared@1.17.7': + resolution: {integrity: sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg==} + peerDependencies: + '@algolia/client-search': '>= 4.9.1 < 6' + algoliasearch: '>= 4.9.1 < 6' + + '@algolia/client-abtesting@5.45.0': + resolution: {integrity: sha512-WTW0VZA8xHMbzuQD5b3f41ovKZ0MNTIXkWfm0F2PU+XGcLxmxX15UqODzF2sWab0vSbi3URM1xLhJx+bXbd1eQ==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-analytics@5.45.0': + resolution: {integrity: sha512-I3g7VtvG/QJOH3tQO7E7zWTwBfK/nIQXShFLR8RvPgWburZ626JNj332M3wHCYcaAMivN9WJG66S2JNXhm6+Xg==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-common@5.45.0': + resolution: {integrity: sha512-/nTqm1tLiPtbUr+8kHKyFiCOfhRfgC+JxLvOCq471gFZZOlsh6VtFRiKI60/zGmHTojFC6B0mD80PB7KeK94og==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-insights@5.45.0': + resolution: {integrity: sha512-suQTx/1bRL1g/K2hRtbK3ANmbzaZCi13487sxxmqok+alBDKKw0/TI73ZiHjjFXM2NV52inwwcmW4fUR45206Q==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-personalization@5.45.0': + resolution: {integrity: sha512-CId/dbjpzI3eoUhPU6rt/z4GrRsDesqFISEMOwrqWNSrf4FJhiUIzN42Ac+Gzg69uC0RnzRYy60K1y4Na5VSMw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-query-suggestions@5.45.0': + resolution: {integrity: sha512-tjbBKfA8fjAiFtvl9g/MpIPiD6pf3fj7rirVfh1eMIUi8ybHP4ovDzIaE216vHuRXoePQVCkMd2CokKvYq1CLw==} + engines: {node: '>= 14.0.0'} + + '@algolia/client-search@5.45.0': + resolution: {integrity: sha512-nxuCid+Nszs4xqwIMDw11pRJPes2c+Th1yup/+LtpjFH8QWXkr3SirNYSD3OXAeM060HgWWPLA8/Fxk+vwxQOA==} + engines: {node: '>= 14.0.0'} + + '@algolia/ingestion@1.45.0': + resolution: {integrity: sha512-t+1doBzhkQTeOOjLHMlm4slmXBhvgtEGQhOmNpMPTnIgWOyZyESWdm+XD984qM4Ej1i9FRh8VttOGrdGnAjAng==} + engines: {node: '>= 14.0.0'} + + '@algolia/monitoring@1.45.0': + resolution: {integrity: sha512-IaX3ZX1A/0wlgWZue+1BNWlq5xtJgsRo7uUk/aSiYD7lPbJ7dFuZ+yTLFLKgbl4O0QcyHTj1/mSBj9ryF1Lizg==} + engines: {node: '>= 14.0.0'} + + '@algolia/recommend@5.45.0': + resolution: {integrity: sha512-1jeMLoOhkgezCCPsOqkScwYzAAc1Jr5T2hisZl0s32D94ZV7d1OHozBukgOjf8Dw+6Hgi6j52jlAdUWTtkX9Mg==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-browser-xhr@5.45.0': + resolution: {integrity: sha512-46FIoUkQ9N7wq4/YkHS5/W9Yjm4Ab+q5kfbahdyMpkBPJ7IBlwuNEGnWUZIQ6JfUZuJVojRujPRHMihX4awUMg==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-fetch@5.45.0': + resolution: {integrity: sha512-XFTSAtCwy4HdBhSReN2rhSyH/nZOM3q3qe5ERG2FLbYId62heIlJBGVyAPRbltRwNlotlydbvSJ+SQ0ruWC2cw==} + engines: {node: '>= 14.0.0'} + + '@algolia/requester-node-http@5.45.0': + resolution: {integrity: sha512-8mTg6lHx5i44raCU52APsu0EqMsdm4+7Hch/e4ZsYZw0hzwkuaMFh826ngnkYf9XOl58nHoou63aZ874m8AbpQ==} + engines: {node: '>= 14.0.0'} + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ant-design/colors@6.0.0': + resolution: {integrity: sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==} + + '@ant-design/icons-svg@4.4.2': + resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} + + '@ant-design/icons-vue@7.0.1': + resolution: {integrity: sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==} + peerDependencies: + vue: ^3.5.24 + + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + + '@antfu/utils@9.3.0': + resolution: {integrity: sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==} + + '@apideck/better-ajv-errors@0.3.6': + resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==} + engines: {node: '>=10'} + peerDependencies: + ajv: '>=8' + + '@ast-grep/napi-darwin-arm64@0.39.9': + resolution: {integrity: sha512-0gdBC2oPBkIBsh89yUXxJeK37QYRbp1qNZVuRGizT666ljgCw+2NIxHeQGNjwWuI0+g3exrd8FyqD3gMixRx3w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@ast-grep/napi-darwin-x64@0.39.9': + resolution: {integrity: sha512-7QuQNFwcVj71hDAMBErF+mVq2h92vUdHLKa/vq58HdRpix5G3DZqcTKoFjwhCNzc2wsNRAjV+BiIR01znQSxLA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@ast-grep/napi-linux-arm64-gnu@0.39.9': + resolution: {integrity: sha512-qXTjhagRYkPyHNACEzGi5q26OMRdSvLRRqi799oZaSDf96Xj+8tcB5+tFlH0NpqaP84GsZoQ15SfDXeoP4qdmQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@ast-grep/napi-linux-arm64-musl@0.39.9': + resolution: {integrity: sha512-U9KVohyburVGEWiiREFq+iTxdMdSbRyXL+yzCVvGPmLPW4Ca4zpX9Cret4jCJBp8dljSIK0C7txXAc7fM+6rag==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@ast-grep/napi-linux-x64-gnu@0.39.9': + resolution: {integrity: sha512-C5WwiNr/eE7lS0vl9bNdSXrsGIUAga2SaqV3G+ogVl9HeRU2jtGhv+3AgzGvYWj+PtnbtuZ2Nxkm10gEVPhv1A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@ast-grep/napi-linux-x64-musl@0.39.9': + resolution: {integrity: sha512-KutzZSMhHP3P43nQ+kNSbuiDaqkCcdqOcXLnq28MRvS+qwvW+01dJ8eWRlpMJC9vCJmaJAZfI7xkNSp74WkdxA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@ast-grep/napi-win32-arm64-msvc@0.39.9': + resolution: {integrity: sha512-S2kC6K9I7e70oMVn2FDICzVlz7SPZVWNCRKCBD+Dgy1OTn0byzmMLaGpjth3pqN93UCssEC03ooP3tuclS0JSA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@ast-grep/napi-win32-ia32-msvc@0.39.9': + resolution: {integrity: sha512-n18Dqtc7O5q8ZM9gBNkvLd+ajdjLNraw4ZoIcHelI7TnTvh1m6zR3tUuHNxPhL+IJfdKx3ne7ATKlEeiSlMn4g==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@ast-grep/napi-win32-x64-msvc@0.39.9': + resolution: {integrity: sha512-YOF7mO+6nvyRtMC179u82Vr1qIGOmoE1yjwr4cmAf7DVNd+vs42y9tbQGYwLmVY8M3Hg1PviB8XX7FQKewuOGQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@ast-grep/napi@0.39.9': + resolution: {integrity: sha512-qtLLQq1a3isK0iaq0Drl7Qt4PqeyjTrpFxNdA/20O/jYkGiA/oJA8DLMn1bzczsfjlUohe4dg39bpeAqG02uvA==} + engines: {node: '>= 10'} + + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.27.3': + resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.28.5': + resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-optimise-call-expression@7.27.1': + resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.27.1': + resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-replace-supers@7.27.1': + resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.28.3': + resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1': + resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1': + resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': + resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.13.0 + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3': + resolution: {integrity: sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-decorators@7.28.0': + resolution: {integrity: sha512-zOiZqvANjWDUaUS9xMxbMcK/Zccztbe/6ikvUXaG9nsPH3w6qh5UaPGAnirI/WhIbZ8m3OHU0ReyPrknG+ZKeg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-decorators@7.27.1': + resolution: {integrity: sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-assertions@7.27.1': + resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-arrow-functions@7.27.1': + resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-generator-functions@7.28.0': + resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-async-to-generator@7.27.1': + resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoped-functions@7.27.1': + resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-block-scoping@7.28.5': + resolution: {integrity: sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-properties@7.27.1': + resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-class-static-block@7.28.3': + resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.12.0 + + '@babel/plugin-transform-classes@7.28.4': + resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-computed-properties@7.27.1': + resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-dotall-regex@7.27.1': + resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-keys@7.27.1': + resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-dynamic-import@7.27.1': + resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-explicit-resource-management@7.28.0': + resolution: {integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-exponentiation-operator@7.28.5': + resolution: {integrity: sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-export-namespace-from@7.27.1': + resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-for-of@7.27.1': + resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-function-name@7.27.1': + resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-json-strings@7.27.1': + resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-literals@7.27.1': + resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-logical-assignment-operators@7.28.5': + resolution: {integrity: sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-member-expression-literals@7.27.1': + resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-amd@7.27.1': + resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-commonjs@7.27.1': + resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-systemjs@7.28.5': + resolution: {integrity: sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-modules-umd@7.27.1': + resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1': + resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-new-target@7.27.1': + resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1': + resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-numeric-separator@7.27.1': + resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-rest-spread@7.28.4': + resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-object-super@7.27.1': + resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-catch-binding@7.27.1': + resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-optional-chaining@7.28.5': + resolution: {integrity: sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-methods@7.27.1': + resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-private-property-in-object@7.27.1': + resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-property-literals@7.27.1': + resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regenerator@7.28.4': + resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-regexp-modifiers@7.27.1': + resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-transform-reserved-words@7.27.1': + resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-shorthand-properties@7.27.1': + resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-spread@7.27.1': + resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-sticky-regex@7.27.1': + resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-template-literals@7.27.1': + resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typeof-symbol@7.27.1': + resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-typescript@7.28.5': + resolution: {integrity: sha512-x2Qa+v/CuEoX7Dr31iAfr0IhInrVOWZU/2vJMJ00FOR/2nM0BcBEclpaf9sWCDc+v5e9dMrhSH8/atq/kX7+bA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-escapes@7.27.1': + resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-property-regex@7.27.1': + resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-regex@7.27.1': + resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1': + resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/preset-env@7.28.5': + resolution: {integrity: sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} + peerDependencies: + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + + '@babel/preset-typescript@7.28.5': + resolution: {integrity: sha512-+bQy5WOI2V6LJZpPVxY+yp66XdZ2yifu0Mc1aP5CQKgjn4QM5IN2i5fAZ4xKop47pr8rpVhiAeu+nDQa12C8+g==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime-corejs3@7.28.4': + resolution: {integrity: sha512-h7iEYiW4HebClDEhtvFObtPmIvrd1SSfpI9EhOeKk4CtIK/ngBWFpuhCzhdmRKtg71ylcue+9I6dv54XYO1epQ==} + engines: {node: '>=6.9.0'} + + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@bpmn-io/cm-theme@0.1.0-alpha.2': + resolution: {integrity: sha512-ZILgiYzxk3KMvxplUXmdRFQo45/JehDPg5k9tWfehmzUOSE13ssyLPil8uCloMQnb3yyzyOWTjb/wzKXTHlFQw==} + + '@bpmn-io/diagram-js-ui@0.2.3': + resolution: {integrity: sha512-OGyjZKvGK8tHSZ0l7RfeKhilGoOGtFDcoqSGYkX0uhFlo99OVZ9Jn1K7TJGzcE9BdKwvA5Y5kGqHEhdTxHvFfw==} + + '@bpmn-io/extract-process-variables@0.8.0': + resolution: {integrity: sha512-yAS7ZYX+D56K+luC36u96eRMLb4VHcPUwTUqMZ/Z/Je2gou2DJLRbuBTHAB4jjKt4wFCHSG4B8Y+TrBciEYf4w==} + + '@bpmn-io/feel-editor@1.12.1': + resolution: {integrity: sha512-XEsbglURtQsphlvGQll3aiGYsM8Qc22y7fDGmxUqSYv3AVzORBzYdFhFFwfYMfKfzln/qwa3Wlrl0amvELRJ2A==} + engines: {node: '>= 16'} + + '@bpmn-io/feel-lint@1.4.0': + resolution: {integrity: sha512-1bsdR/9vPD7RQVqWWPk0X0tpjLsYTDrCxIzOVtN/h32o4nPGl0dZBU5m07qaFUGD4wG3eOH4Qim1wexHG8YkBw==} + + '@bpmn-io/feel-lint@2.1.0': + resolution: {integrity: sha512-fUGye6KppyowiwUqlfRHpMtXQ8so8jv1rh2LEQp+fhKf+WeLkbokmQwXZbCQe/4Kam/OLQRlx+p3Qnu0IrZQ6A==} + + '@bpmn-io/lang-feel@2.4.0': + resolution: {integrity: sha512-0c1pratAD/YTOaivwLd7MljT0/MoDUMpfm87JbtAOBvivRfEr8mel5l8Ig3kgpYJ+xkznLaA9s79ZDKLh/O8ag==} + + '@bpmn-io/lezer-feel@1.9.0': + resolution: {integrity: sha512-mV+pj+x0++9zT5/RkOOUNtkT2hpKpGWbXuFR8trJlvJeRe1dL/5yPal/RBcnk3z73tILK4kP6LzXelcsshQCEw==} + + '@bpmn-io/properties-panel@3.34.0': + resolution: {integrity: sha512-BkY3JYVDtmuUox4U/B5Mxis0UzW+uY9cZvKEUMKQeW+4IyOZ6yF07/fsabS1koaQV19YxF8im3PX5fibSNxphg==} + + '@cacheable/memory@2.0.6': + resolution: {integrity: sha512-7e8SScMocHxcAb8YhtkbMhGG+EKLRIficb1F5sjvhSYsWTZGxvg4KIDp8kgxnV2PUJ3ddPe6J9QESjKvBWRDkg==} + + '@cacheable/utils@2.3.2': + resolution: {integrity: sha512-8kGE2P+HjfY8FglaOiW+y8qxcaQAfAhVML+i66XJR3YX5FtyDqn6Txctr3K2FrbxLKixRRYYBWMbuGciOhYNDg==} + + '@camunda/feel-builtins@0.2.0': + resolution: {integrity: sha512-Jusm8x3Onqze9E5Y0lGGdPj66bnFKLYNwDz+uG4otsEXgSL0FpF+koGHK48LkF9Jqo67KaP1y3zr2y/HIWRePw==} + + '@carbon/icons@11.70.0': + resolution: {integrity: sha512-pw/u032VfP3UDcAr0SZzYJlfSmtiDkOHEgQSchmU9Z1g7CmpfQ7EWSW8ExcHm4DFdun8M3Z1QCkCyu0Hq+s1gg==} + + '@changesets/apply-release-plan@7.0.13': + resolution: {integrity: sha512-BIW7bofD2yAWoE8H4V40FikC+1nNFEKBisMECccS16W1rt6qqhNTBDmIw5HaqmMgtLNz9e7oiALiEUuKrQ4oHg==} + + '@changesets/assemble-release-plan@6.0.9': + resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} + + '@changesets/changelog-git@0.2.1': + resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} + + '@changesets/changelog-github@0.5.1': + resolution: {integrity: sha512-BVuHtF+hrhUScSoHnJwTELB4/INQxVFc+P/Qdt20BLiBFIHFJDDUaGsZw+8fQeJTRP5hJZrzpt3oZWh0G19rAQ==} + + '@changesets/cli@2.29.7': + resolution: {integrity: sha512-R7RqWoaksyyKXbKXBTbT4REdy22yH81mcFK6sWtqSanxUCbUi9Uf+6aqxZtDQouIqPdem2W56CdxXgsxdq7FLQ==} + hasBin: true + + '@changesets/config@3.1.1': + resolution: {integrity: sha512-bd+3Ap2TKXxljCggI0mKPfzCQKeV/TU4yO2h2C6vAihIo8tzseAn2e7klSuiyYYXvgu53zMN1OeYMIQkaQoWnA==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.3': + resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} + + '@changesets/get-github-info@0.6.0': + resolution: {integrity: sha512-v/TSnFVXI8vzX9/w3DU2Ol+UlTZcu3m0kXTjTT4KlAdwSvwutcByYwyYn9hwerPWfPkT2JfpoX0KgvCEi8Q/SA==} + + '@changesets/get-release-plan@4.0.13': + resolution: {integrity: sha512-DWG1pus72FcNeXkM12tx+xtExyH/c9I1z+2aXlObH3i9YA7+WZEVaiHzHl03thpvAgWTRaH64MpfHxozfF7Dvg==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.4': + resolution: {integrity: sha512-BXANzRFkX+XcC1q/d27NKvlJ1yf7PSAgi8JG6dt8EfbHFHi4neau7mufcSca5zRhwOL8j9s6EqsxmT+s+/E6Sw==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.1': + resolution: {integrity: sha512-iwksMs5Bf/wUItfcg+OXrEpravm5rEd9Bf4oyIPL4kVTmJQ7PNDSd6MDYkpSJR1pn7tz/k8Zf2DhTCqX08Ou+Q==} + + '@changesets/pre@2.0.2': + resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} + + '@changesets/read@0.6.5': + resolution: {integrity: sha512-UPzNGhsSjHD3Veb0xO/MwvasGe8eMyNrR/sT9gR8Q3DhOQZirgKhhXv/8hVsI0QpPjR004Z9iFxoJU6in3uGMg==} + + '@changesets/should-skip-package@0.1.2': + resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.1.0': + resolution: {integrity: sha512-rKQcJ+o1nKNgeoYRHKOS07tAMNd3YSN0uHaJOZYjBAgxfV7TUE7JE+z4BzZdQwb5hKaYbayKN5KrYV7ODb2rAA==} + + '@changesets/write@0.4.0': + resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + + '@clack/core@0.5.0': + resolution: {integrity: sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==} + + '@clack/prompts@0.11.0': + resolution: {integrity: sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==} + + '@cloudflare/kv-asset-handler@0.4.1': + resolution: {integrity: sha512-Nu8ahitGFFJztxUml9oD/DLb7Z28C8cd8F46IVQ7y5Btz575pvMY8AqZsXkX7Gds29eCKdMgIHjIvzskHgPSFg==} + engines: {node: '>=18.0.0'} + + '@codemirror/autocomplete@6.20.0': + resolution: {integrity: sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==} + + '@codemirror/commands@6.10.0': + resolution: {integrity: sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w==} + + '@codemirror/language@6.11.3': + resolution: {integrity: sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==} + + '@codemirror/lint@6.9.2': + resolution: {integrity: sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==} + + '@codemirror/state@6.5.2': + resolution: {integrity: sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==} + + '@codemirror/view@6.38.8': + resolution: {integrity: sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==} + + '@commitlint/cli@19.8.1': + resolution: {integrity: sha512-LXUdNIkspyxrlV6VDHWBmCZRtkEVRpBKxi2Gtw3J54cGWhLCTouVD/Q6ZSaSvd2YaDObWK8mDjrz3TIKtaQMAA==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@19.8.1': + resolution: {integrity: sha512-/AZHJL6F6B/G959CsMAzrPKKZjeEiAVifRyEwXxcT6qtqbPwGw+iQxmNS+Bu+i09OCtdNRW6pNpBvgPrtMr9EQ==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@19.8.1': + resolution: {integrity: sha512-0jvJ4u+eqGPBIzzSdqKNX1rvdbSU1lPNYlfQQRIFnBgLy26BtC0cFnr7c/AyuzExMxWsMOte6MkTi9I3SQ3iGQ==} + engines: {node: '>=v18'} + + '@commitlint/ensure@19.8.1': + resolution: {integrity: sha512-mXDnlJdvDzSObafjYrOSvZBwkD01cqB4gbnnFuVyNpGUM5ijwU/r/6uqUmBXAAOKRfyEjpkGVZxaDsCVnHAgyw==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@19.8.1': + resolution: {integrity: sha512-YfJyIqIKWI64Mgvn/sE7FXvVMQER/Cd+s3hZke6cI1xgNT/f6ZAz5heND0QtffH+KbcqAwXDEE1/5niYayYaQA==} + engines: {node: '>=v18'} + + '@commitlint/format@19.8.1': + resolution: {integrity: sha512-kSJj34Rp10ItP+Eh9oCItiuN/HwGQMXBnIRk69jdOwEW9llW9FlyqcWYbHPSGofmjsqeoxa38UaEA5tsbm2JWw==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@19.8.1': + resolution: {integrity: sha512-AceOhEhekBUQ5dzrVhDDsbMaY5LqtN8s1mqSnT2Kz1ERvVZkNihrs3Sfk1Je/rxRNbXYFzKZSHaPsEJJDJV8dg==} + engines: {node: '>=v18'} + + '@commitlint/lint@19.8.1': + resolution: {integrity: sha512-52PFbsl+1EvMuokZXLRlOsdcLHf10isTPlWwoY1FQIidTsTvjKXVXYb7AvtpWkDzRO2ZsqIgPK7bI98x8LRUEw==} + engines: {node: '>=v18'} + + '@commitlint/load@19.8.1': + resolution: {integrity: sha512-9V99EKG3u7z+FEoe4ikgq7YGRCSukAcvmKQuTtUyiYPnOd9a2/H9Ak1J9nJA1HChRQp9OA/sIKPugGS+FK/k1A==} + engines: {node: '>=v18'} + + '@commitlint/message@19.8.1': + resolution: {integrity: sha512-+PMLQvjRXiU+Ae0Wc+p99EoGEutzSXFVwQfa3jRNUZLNW5odZAyseb92OSBTKCu+9gGZiJASt76Cj3dLTtcTdg==} + engines: {node: '>=v18'} + + '@commitlint/parse@19.8.1': + resolution: {integrity: sha512-mmAHYcMBmAgJDKWdkjIGq50X4yB0pSGpxyOODwYmoexxxiUCy5JJT99t1+PEMK7KtsCtzuWYIAXYAiKR+k+/Jw==} + engines: {node: '>=v18'} + + '@commitlint/read@19.8.1': + resolution: {integrity: sha512-03Jbjb1MqluaVXKHKRuGhcKWtSgh3Jizqy2lJCRbRrnWpcM06MYm8th59Xcns8EqBYvo0Xqb+2DoZFlga97uXQ==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@19.8.1': + resolution: {integrity: sha512-GM0mAhFk49I+T/5UCYns5ayGStkTt4XFFrjjf0L4S26xoMTSkdCf9ZRO8en1kuopC4isDFuEm7ZOm/WRVeElVg==} + engines: {node: '>=v18'} + + '@commitlint/rules@19.8.1': + resolution: {integrity: sha512-Hnlhd9DyvGiGwjfjfToMi1dsnw1EXKGJNLTcsuGORHz6SS9swRgkBsou33MQ2n51/boIDrbsg4tIBbRpEWK2kw==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@19.8.1': + resolution: {integrity: sha512-98Mm5inzbWTKuZQr2aW4SReY6WUukdWXuZhrqf1QdKPZBCCsXuG87c+iP0bwtD6DBnmVVQjgp4whoHRVixyPBg==} + engines: {node: '>=v18'} + + '@commitlint/top-level@19.8.1': + resolution: {integrity: sha512-Ph8IN1IOHPSDhURCSXBz44+CIu+60duFwRsg6HqaISFHQHbmBtxVw4ZrFNIYUzEP7WwrNPxa2/5qJ//NK1FGcw==} + engines: {node: '>=v18'} + + '@commitlint/types@19.8.1': + resolution: {integrity: sha512-/yCrWGCoA1SVKOks25EGadP9Pnj0oAIHGpl2wH2M2Y46dPM2ueb8wyCVOD7O3WCTkaJ0IkKvzhl1JY7+uCT2Dw==} + engines: {node: '>=v18'} + + '@cspell/cspell-bundled-dicts@8.19.4': + resolution: {integrity: sha512-2ZRcZP/ncJ5q953o8i+R0fb8+14PDt5UefUNMrFZZHvfTI0jukAASOQeLY+WT6ASZv6CgbPrApAdbppy9FaXYQ==} + engines: {node: '>=18'} + + '@cspell/cspell-json-reporter@8.19.4': + resolution: {integrity: sha512-pOlUtLUmuDdTIOhDTvWxxta0Wm8RCD/p1V0qUqeP6/Ups1ajBI4FWEpRFd7yMBTUHeGeSNicJX5XeX7wNbAbLQ==} + engines: {node: '>=18'} + + '@cspell/cspell-pipe@8.19.4': + resolution: {integrity: sha512-GNAyk+7ZLEcL2fCMT5KKZprcdsq3L1eYy3e38/tIeXfbZS7Sd1R5FXUe6CHXphVWTItV39TvtLiDwN/2jBts9A==} + engines: {node: '>=18'} + + '@cspell/cspell-resolver@8.19.4': + resolution: {integrity: sha512-S8vJMYlsx0S1D60glX8H2Jbj4mD8519VjyY8lu3fnhjxfsl2bDFZvF3ZHKsLEhBE+Wh87uLqJDUJQiYmevHjDg==} + engines: {node: '>=18'} + + '@cspell/cspell-service-bus@8.19.4': + resolution: {integrity: sha512-uhY+v8z5JiUogizXW2Ft/gQf3eWrh5P9036jN2Dm0UiwEopG/PLshHcDjRDUiPdlihvA0RovrF0wDh4ptcrjuQ==} + engines: {node: '>=18'} + + '@cspell/cspell-types@8.19.4': + resolution: {integrity: sha512-ekMWuNlFiVGfsKhfj4nmc8JCA+1ZltwJgxiKgDuwYtR09ie340RfXFF6YRd2VTW5zN7l4F1PfaAaPklVz6utSg==} + engines: {node: '>=18'} + + '@cspell/dict-ada@4.1.1': + resolution: {integrity: sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==} + + '@cspell/dict-al@1.1.1': + resolution: {integrity: sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==} + + '@cspell/dict-aws@4.0.16': + resolution: {integrity: sha512-a681zShZbtTo947NvTYGLer95ZDQw1ROKvIFydak1e0OlfFCsNdtcYTupn0nbbYs53c9AO7G2DU8AcNEAnwXPA==} + + '@cspell/dict-bash@4.2.2': + resolution: {integrity: sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==} + + '@cspell/dict-companies@3.2.7': + resolution: {integrity: sha512-fEyr3LmpFKTaD0LcRhB4lfW1AmULYBqzg4gWAV0dQCv06l+TsA+JQ+3pZJbUcoaZirtgsgT3dL3RUjmGPhUH0A==} + + '@cspell/dict-cpp@6.0.14': + resolution: {integrity: sha512-dkmpSwvVfVdtoZ4mW/CK2Ep1v8mJlp6uiKpMNbSMOdJl4kq28nQS4vKNIX3B2bJa0Ha5iHHu+1mNjiLeO3g7Xg==} + + '@cspell/dict-cryptocurrencies@5.0.5': + resolution: {integrity: sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==} + + '@cspell/dict-csharp@4.0.7': + resolution: {integrity: sha512-H16Hpu8O/1/lgijFt2lOk4/nnldFtQ4t8QHbyqphqZZVE5aS4J/zD/WvduqnLY21aKhZS6jo/xF5PX9jyqPKUA==} + + '@cspell/dict-css@4.0.18': + resolution: {integrity: sha512-EF77RqROHL+4LhMGW5NTeKqfUd/e4OOv6EDFQ/UQQiFyWuqkEKyEz0NDILxOFxWUEVdjT2GQ2cC7t12B6pESwg==} + + '@cspell/dict-dart@2.3.1': + resolution: {integrity: sha512-xoiGnULEcWdodXI6EwVyqpZmpOoh8RA2Xk9BNdR7DLamV/QMvEYn8KJ7NlRiTSauJKPNkHHQ5EVHRM6sTS7jdg==} + + '@cspell/dict-data-science@2.0.12': + resolution: {integrity: sha512-vI/mg6cI28IkFcpeINS7cm5M9HWemmXSTnxJiu3nmc4VAGx35SXIEyuLGBcsVzySvDablFYf4hsEpmg1XpVsUQ==} + + '@cspell/dict-django@4.1.5': + resolution: {integrity: sha512-AvTWu99doU3T8ifoMYOMLW2CXKvyKLukPh1auOPwFGHzueWYvBBN+OxF8wF7XwjTBMMeRleVdLh3aWCDEX/ZWg==} + + '@cspell/dict-docker@1.1.16': + resolution: {integrity: sha512-UiVQ5RmCg6j0qGIxrBnai3pIB+aYKL3zaJGvXk1O/ertTKJif9RZikKXCEgqhaCYMweM4fuLqWSVmw3hU164Iw==} + + '@cspell/dict-dotnet@5.0.10': + resolution: {integrity: sha512-ooar8BP/RBNP1gzYfJPStKEmpWy4uv/7JCq6FOnJLeD1yyfG3d/LFMVMwiJo+XWz025cxtkM3wuaikBWzCqkmg==} + + '@cspell/dict-elixir@4.0.8': + resolution: {integrity: sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==} + + '@cspell/dict-en-common-misspellings@2.1.8': + resolution: {integrity: sha512-vDsjRFPQGuAADAiitf82z9Mz3DcqKZi6V5hPAEIFkLLKjFVBcjUsSq59SfL59ElIFb76MtBO0BLifdEbBj+DoQ==} + + '@cspell/dict-en-gb@1.1.33': + resolution: {integrity: sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==} + + '@cspell/dict-en_us@4.4.24': + resolution: {integrity: sha512-JE+/H2YicHJTneRmgH4GSI21rS+1yGZVl1jfOQgl8iHLC+yTTMtCvueNDMK94CgJACzYAoCsQB70MqiFJJfjLQ==} + + '@cspell/dict-filetypes@3.0.14': + resolution: {integrity: sha512-KSXaSMYYNMLLdHEnju1DyRRH3eQWPRYRnOXpuHUdOh2jC44VgQoxyMU7oB3NAhDhZKBPCihabzECsAGFbdKfEA==} + + '@cspell/dict-flutter@1.1.1': + resolution: {integrity: sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==} + + '@cspell/dict-fonts@4.0.5': + resolution: {integrity: sha512-BbpkX10DUX/xzHs6lb7yzDf/LPjwYIBJHJlUXSBXDtK/1HaeS+Wqol4Mlm2+NAgZ7ikIE5DQMViTgBUY3ezNoQ==} + + '@cspell/dict-fsharp@1.1.1': + resolution: {integrity: sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==} + + '@cspell/dict-fullstack@3.2.7': + resolution: {integrity: sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==} + + '@cspell/dict-gaming-terms@1.1.2': + resolution: {integrity: sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==} + + '@cspell/dict-git@3.0.7': + resolution: {integrity: sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==} + + '@cspell/dict-golang@6.0.24': + resolution: {integrity: sha512-rY7PlC3MsHozmjrZWi0HQPUl0BVCV0+mwK0rnMT7pOIXqOe4tWCYMULDIsEk4F0gbIxb5badd2dkCPDYjLnDgA==} + + '@cspell/dict-google@1.0.9': + resolution: {integrity: sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==} + + '@cspell/dict-haskell@4.0.6': + resolution: {integrity: sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==} + + '@cspell/dict-html-symbol-entities@4.0.4': + resolution: {integrity: sha512-afea+0rGPDeOV9gdO06UW183Qg6wRhWVkgCFwiO3bDupAoyXRuvupbb5nUyqSTsLXIKL8u8uXQlJ9pkz07oVXw==} + + '@cspell/dict-html@4.0.12': + resolution: {integrity: sha512-JFffQ1dDVEyJq6tCDWv0r/RqkdSnV43P2F/3jJ9rwLgdsOIXwQbXrz6QDlvQLVvNSnORH9KjDtenFTGDyzfCaA==} + + '@cspell/dict-java@5.0.12': + resolution: {integrity: sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==} + + '@cspell/dict-julia@1.1.1': + resolution: {integrity: sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==} + + '@cspell/dict-k8s@1.0.12': + resolution: {integrity: sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==} + + '@cspell/dict-kotlin@1.1.1': + resolution: {integrity: sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==} + + '@cspell/dict-latex@4.0.4': + resolution: {integrity: sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==} + + '@cspell/dict-lorem-ipsum@4.0.5': + resolution: {integrity: sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==} + + '@cspell/dict-lua@4.0.8': + resolution: {integrity: sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==} + + '@cspell/dict-makefile@1.0.5': + resolution: {integrity: sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==} + + '@cspell/dict-markdown@2.0.12': + resolution: {integrity: sha512-ufwoliPijAgWkD/ivAMC+A9QD895xKiJRF/fwwknQb7kt7NozTLKFAOBtXGPJAB4UjhGBpYEJVo2elQ0FCAH9A==} + peerDependencies: + '@cspell/dict-css': ^4.0.18 + '@cspell/dict-html': ^4.0.12 + '@cspell/dict-html-symbol-entities': ^4.0.4 + '@cspell/dict-typescript': ^3.2.3 + + '@cspell/dict-monkeyc@1.0.11': + resolution: {integrity: sha512-7Q1Ncu0urALI6dPTrEbSTd//UK0qjRBeaxhnm8uY5fgYNFYAG+u4gtnTIo59S6Bw5P++4H3DiIDYoQdY/lha8w==} + + '@cspell/dict-node@5.0.8': + resolution: {integrity: sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==} + + '@cspell/dict-npm@5.2.24': + resolution: {integrity: sha512-R+s2SC7tQCxmV6nZxkmwdR2ruSDerAkbBjIAuAcH9ZXdpacTghSDcHJtSyBYnbcLFq8Bdp3cGB+J6vCRbw3pyw==} + + '@cspell/dict-php@4.1.0': + resolution: {integrity: sha512-dTDeabyOj7eFvn2Q4Za3uVXM2+SzeFMqX8ly2P0XTo4AzbCmI2hulFD/QIADwWmwiRrInbbf8cxwFHNIYrXl4w==} + + '@cspell/dict-powershell@5.0.15': + resolution: {integrity: sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==} + + '@cspell/dict-public-licenses@2.0.15': + resolution: {integrity: sha512-cJEOs901H13Pfy0fl4dCD1U+xpWIMaEPq8MeYU83FfDZvellAuSo4GqWCripfIqlhns/L6+UZEIJSOZnjgy7Wg==} + + '@cspell/dict-python@4.2.22': + resolution: {integrity: sha512-rgF7DuleVK2lkzlw33jjEfxS2a0CU5kwAhOqf5B6XkuaPbqZ/0g0LBCdwglAGccYu7sBuvxRS8Yubk+ytSAFTg==} + + '@cspell/dict-r@2.1.1': + resolution: {integrity: sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==} + + '@cspell/dict-ruby@5.0.9': + resolution: {integrity: sha512-H2vMcERMcANvQshAdrVx0XoWaNX8zmmiQN11dZZTQAZaNJ0xatdJoSqY8C8uhEMW89bfgpN+NQgGuDXW2vmXEw==} + + '@cspell/dict-rust@4.0.12': + resolution: {integrity: sha512-z2QiH+q9UlNhobBJArvILRxV8Jz0pKIK7gqu4TgmEYyjiu1TvnGZ1tbYHeu9w3I/wOP6UMDoCBTty5AlYfW0mw==} + + '@cspell/dict-scala@5.0.8': + resolution: {integrity: sha512-YdftVmumv8IZq9zu1gn2U7A4bfM2yj9Vaupydotyjuc+EEZZSqAafTpvW/jKLWji2TgybM1L2IhmV0s/Iv9BTw==} + + '@cspell/dict-shell@1.1.2': + resolution: {integrity: sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==} + + '@cspell/dict-software-terms@5.1.14': + resolution: {integrity: sha512-Eu9h090hxHJiqzVFS0WxOZbYXnmb7F1RFIUEg4Nru+D/78bXVDH4b8BiKGVFNRljaieNQRAHaryzdaKJRCH6ZA==} + + '@cspell/dict-sql@2.2.1': + resolution: {integrity: sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==} + + '@cspell/dict-svelte@1.0.7': + resolution: {integrity: sha512-hGZsGqP0WdzKkdpeVLBivRuSNzOTvN036EBmpOwxH+FTY2DuUH7ecW+cSaMwOgmq5JFSdTcbTNFlNC8HN8lhaQ==} + + '@cspell/dict-swift@2.0.6': + resolution: {integrity: sha512-PnpNbrIbex2aqU1kMgwEKvCzgbkHtj3dlFLPMqW1vSniop7YxaDTtvTUO4zA++ugYAEL+UK8vYrBwDPTjjvSnA==} + + '@cspell/dict-terraform@1.1.3': + resolution: {integrity: sha512-gr6wxCydwSFyyBKhBA2xkENXtVFToheqYYGFvlMZXWjviynXmh+NK/JTvTCk/VHk3+lzbO9EEQKee6VjrAUSbA==} + + '@cspell/dict-typescript@3.2.3': + resolution: {integrity: sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==} + + '@cspell/dict-vue@3.0.5': + resolution: {integrity: sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA==} + + '@cspell/dynamic-import@8.19.4': + resolution: {integrity: sha512-0LLghC64+SiwQS20Sa0VfFUBPVia1rNyo0bYeIDoB34AA3qwguDBVJJkthkpmaP1R2JeR/VmxmJowuARc4ZUxA==} + engines: {node: '>=18.0'} + + '@cspell/filetypes@8.19.4': + resolution: {integrity: sha512-D9hOCMyfKtKjjqQJB8F80PWsjCZhVGCGUMiDoQpcta0e+Zl8vHgzwaC0Ai4QUGBhwYEawHGiWUd7Y05u/WXiNQ==} + engines: {node: '>=18'} + + '@cspell/strong-weak-map@8.19.4': + resolution: {integrity: sha512-MUfFaYD8YqVe32SQaYLI24/bNzaoyhdBIFY5pVrvMo1ZCvMl8AlfI2OcBXvcGb5aS5z7sCNCJm11UuoYbLI1zw==} + engines: {node: '>=18'} + + '@cspell/url@8.19.4': + resolution: {integrity: sha512-Pa474iBxS+lxsAL4XkETPGIq3EgMLCEb9agj3hAd2VGMTCApaiUvamR4b+uGXIPybN70piFxvzrfoxsG2uIP6A==} + engines: {node: '>=18.0'} + + '@css-render/plugin-bem@0.15.14': + resolution: {integrity: sha512-QK513CJ7yEQxm/P3EwsI+d+ha8kSOcjGvD6SevM41neEMxdULE+18iuQK6tEChAWMOQNQPLG/Rw3Khb69r5neg==} + peerDependencies: + css-render: ~0.15.14 + + '@css-render/vue3-ssr@0.15.14': + resolution: {integrity: sha512-//8027GSbxE9n3QlD73xFY6z4ZbHbvrOVB7AO6hsmrEzGbg+h2A09HboUyDgu+xsmj7JnvJD39Irt+2D0+iV8g==} + peerDependencies: + vue: ^3.5.24 + + '@csstools/cascade-layer-name-parser@2.0.5': + resolution: {integrity: sha512-p1ko5eHgV+MgXFVa4STPKpvPxr6ReS8oS2jzTukjR74i5zJNyWO1ZM1m8YKBXnzDKWfBN1ztLYlHxbVemDD88A==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@csstools/media-query-list-parser@3.0.1': + resolution: {integrity: sha512-HNo8gGD02kHmcbX6PvCoUuOQvn4szyB9ca63vZHKX5A81QytgDG4oxG4IaEfHTlEZSZ6MjPEMWIVU+zF2PZcgw==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.1 + '@csstools/css-tokenizer': ^3.0.1 + + '@csstools/media-query-list-parser@4.0.3': + resolution: {integrity: sha512-HAYH7d3TLRHDOUQK4mZKf9k9Ph/m8Akstg66ywKR4SFAigjs3yBiUeZtFxywiTm5moZMAp/5W/ZuFnNXXYLuuQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/postcss-alpha-function@1.0.1': + resolution: {integrity: sha512-isfLLwksH3yHkFXfCI2Gcaqg7wGGHZZwunoJzEZk0yKYIokgre6hYVFibKL3SYAoR1kBXova8LB+JoO5vZzi9w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-cascade-layers@5.0.2': + resolution: {integrity: sha512-nWBE08nhO8uWl6kSAeCx4im7QfVko3zLrtgWZY4/bP87zrSPpSyN/3W3TDqz1jJuH+kbKOHXg5rJnK+ZVYcFFg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-function-display-p3-linear@1.0.1': + resolution: {integrity: sha512-E5qusdzhlmO1TztYzDIi8XPdPoYOjoTY6HBYBCYSj+Gn4gQRBlvjgPQXzfzuPQqt8EhkC/SzPKObg4Mbn8/xMg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-function@4.0.12': + resolution: {integrity: sha512-yx3cljQKRaSBc2hfh8rMZFZzChaFgwmO2JfFgFr1vMcF3C/uyy5I4RFIBOIWGq1D+XbKCG789CGkG6zzkLpagA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-mix-function@3.0.12': + resolution: {integrity: sha512-4STERZfCP5Jcs13P1U5pTvI9SkgLgfMUMhdXW8IlJWkzOOOqhZIjcNhWtNJZes2nkBDsIKJ0CJtFtuaZ00moag==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-color-mix-variadic-function-arguments@1.0.2': + resolution: {integrity: sha512-rM67Gp9lRAkTo+X31DUqMEq+iK+EFqsidfecmhrteErxJZb6tUoJBVQca1Vn1GpDql1s1rD1pKcuYzMsg7Z1KQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-content-alt-text@2.0.8': + resolution: {integrity: sha512-9SfEW9QCxEpTlNMnpSqFaHyzsiRpZ5J5+KqCu1u5/eEJAWsMhzT40qf0FIbeeglEvrGRMdDzAxMIz3wqoGSb+Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-contrast-color-function@2.0.12': + resolution: {integrity: sha512-YbwWckjK3qwKjeYz/CijgcS7WDUCtKTd8ShLztm3/i5dhh4NaqzsbYnhm4bjrpFpnLZ31jVcbK8YL77z3GBPzA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-exponential-functions@2.0.9': + resolution: {integrity: sha512-abg2W/PI3HXwS/CZshSa79kNWNZHdJPMBXeZNyPQFbbj8sKO3jXxOt/wF7juJVjyDTc6JrvaUZYFcSBZBhaxjw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-font-format-keywords@4.0.0': + resolution: {integrity: sha512-usBzw9aCRDvchpok6C+4TXC57btc4bJtmKQWOHQxOVKen1ZfVqBUuCZ/wuqdX5GHsD0NRSr9XTP+5ID1ZZQBXw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-gamut-mapping@2.0.11': + resolution: {integrity: sha512-fCpCUgZNE2piVJKC76zFsgVW1apF6dpYsqGyH8SIeCcM4pTEsRTWTLCaJIMKFEundsCKwY1rwfhtrio04RJ4Dw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-gradients-interpolation-method@5.0.12': + resolution: {integrity: sha512-jugzjwkUY0wtNrZlFeyXzimUL3hN4xMvoPnIXxoZqxDvjZRiSh+itgHcVUWzJ2VwD/VAMEgCLvtaJHX+4Vj3Ow==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-hwb-function@4.0.12': + resolution: {integrity: sha512-mL/+88Z53KrE4JdePYFJAQWFrcADEqsLprExCM04GDNgHIztwFzj0Mbhd/yxMBngq0NIlz58VVxjt5abNs1VhA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-ic-unit@4.0.4': + resolution: {integrity: sha512-yQ4VmossuOAql65sCPppVO1yfb7hDscf4GseF0VCA/DTDaBc0Wtf8MTqVPfjGYlT5+2buokG0Gp7y0atYZpwjg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-initial@2.0.1': + resolution: {integrity: sha512-L1wLVMSAZ4wovznquK0xmC7QSctzO4D0Is590bxpGqhqjboLXYA16dWZpfwImkdOgACdQ9PqXsuRroW6qPlEsg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-is-pseudo-class@5.0.3': + resolution: {integrity: sha512-jS/TY4SpG4gszAtIg7Qnf3AS2pjcUM5SzxpApOrlndMeGhIbaTzWBzzP/IApXoNWEW7OhcjkRT48jnAUIFXhAQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-light-dark-function@2.0.11': + resolution: {integrity: sha512-fNJcKXJdPM3Lyrbmgw2OBbaioU7yuKZtiXClf4sGdQttitijYlZMD5K7HrC/eF83VRWRrYq6OZ0Lx92leV2LFA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-float-and-clear@3.0.0': + resolution: {integrity: sha512-SEmaHMszwakI2rqKRJgE+8rpotFfne1ZS6bZqBoQIicFyV+xT1UF42eORPxJkVJVrH9C0ctUgwMSn3BLOIZldQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-overflow@2.0.0': + resolution: {integrity: sha512-spzR1MInxPuXKEX2csMamshR4LRaSZ3UXVaRGjeQxl70ySxOhMpP2252RAFsg8QyyBXBzuVOOdx1+bVO5bPIzA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-overscroll-behavior@2.0.0': + resolution: {integrity: sha512-e/webMjoGOSYfqLunyzByZj5KKe5oyVg/YSbie99VEaSDE2kimFm0q1f6t/6Jo+VVCQ/jbe2Xy+uX+C4xzWs4w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-resize@3.0.0': + resolution: {integrity: sha512-DFbHQOFW/+I+MY4Ycd/QN6Dg4Hcbb50elIJCfnwkRTCX05G11SwViI5BbBlg9iHRl4ytB7pmY5ieAFk3ws7yyg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-logical-viewport-units@3.0.4': + resolution: {integrity: sha512-q+eHV1haXA4w9xBwZLKjVKAWn3W2CMqmpNpZUk5kRprvSiBEGMgrNH3/sJZ8UA3JgyHaOt3jwT9uFa4wLX4EqQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-media-minmax@2.0.9': + resolution: {integrity: sha512-af9Qw3uS3JhYLnCbqtZ9crTvvkR+0Se+bBqSr7ykAnl9yKhk6895z9rf+2F4dClIDJWxgn0iZZ1PSdkhrbs2ig==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.5': + resolution: {integrity: sha512-zhAe31xaaXOY2Px8IYfoVTB3wglbJUVigGphFLj6exb7cjZRH9A6adyE22XfFK3P2PzwRk0VDeTJmaxpluyrDg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-nested-calc@4.0.0': + resolution: {integrity: sha512-jMYDdqrQQxE7k9+KjstC3NbsmC063n1FTPLCgCRS2/qHUbHM0mNy9pIn4QIiQGs9I/Bg98vMqw7mJXBxa0N88A==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-normalize-display-values@4.0.0': + resolution: {integrity: sha512-HlEoG0IDRoHXzXnkV4in47dzsxdsjdz6+j7MLjaACABX2NfvjFS6XVAnpaDyGesz9gK2SC7MbNwdCHusObKJ9Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-oklab-function@4.0.12': + resolution: {integrity: sha512-HhlSmnE1NKBhXsTnNGjxvhryKtO7tJd1w42DKOGFD6jSHtYOrsJTQDKPMwvOfrzUAk8t7GcpIfRyM7ssqHpFjg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-progressive-custom-properties@4.2.1': + resolution: {integrity: sha512-uPiiXf7IEKtUQXsxu6uWtOlRMXd2QWWy5fhxHDnPdXKCQckPP3E34ZgDoZ62r2iT+UOgWsSbM4NvHE5m3mAEdw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-random-function@2.0.1': + resolution: {integrity: sha512-q+FQaNiRBhnoSNo+GzqGOIBKoHQ43lYz0ICrV+UudfWnEF6ksS6DsBIJSISKQT2Bvu3g4k6r7t0zYrk5pDlo8w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-relative-color-syntax@3.0.12': + resolution: {integrity: sha512-0RLIeONxu/mtxRtf3o41Lq2ghLimw0w9ByLWnnEVuy89exmEEq8bynveBxNW3nyHqLAFEeNtVEmC1QK9MZ8Huw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-scope-pseudo-class@4.0.1': + resolution: {integrity: sha512-IMi9FwtH6LMNuLea1bjVMQAsUhFxJnyLSgOp/cpv5hrzWmrUYU5fm0EguNDIIOHUqzXode8F/1qkC/tEo/qN8Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-sign-functions@1.1.4': + resolution: {integrity: sha512-P97h1XqRPcfcJndFdG95Gv/6ZzxUBBISem0IDqPZ7WMvc/wlO+yU0c5D/OCpZ5TJoTt63Ok3knGk64N+o6L2Pg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-stepped-value-functions@4.0.9': + resolution: {integrity: sha512-h9btycWrsex4dNLeQfyU3y3w40LMQooJWFMm/SK9lrKguHDcFl4VMkncKKoXi2z5rM9YGWbUQABI8BT2UydIcA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-text-decoration-shorthand@4.0.3': + resolution: {integrity: sha512-KSkGgZfx0kQjRIYnpsD7X2Om9BUXX/Kii77VBifQW9Ih929hK0KNjVngHDH0bFB9GmfWcR9vJYJJRvw/NQjkrA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-trigonometric-functions@4.0.9': + resolution: {integrity: sha512-Hnh5zJUdpNrJqK9v1/E3BbrQhaDTj5YiX7P61TOvUhoDHnUmsNNxcDAgkQ32RrcWx9GVUvfUNPcUkn8R3vIX6A==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/postcss-unset-value@4.0.0': + resolution: {integrity: sha512-cBz3tOCI5Fw6NIFEwU3RiwK6mn3nKegjpJuzCndoGq3BZPkUjnsq7uQmIeMNeMbMk7YD2MfKcgCpZwX5jyXqCA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@csstools/selector-resolve-nested@3.1.0': + resolution: {integrity: sha512-mf1LEW0tJLKfWyvn5KdDrhpxHyuxpbNwTIwOYLIvsTffeyOf85j5oIzfG0yosxDgx/sswlqBnESYUcQH0vgZ0g==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@csstools/selector-specificity@5.0.0': + resolution: {integrity: sha512-PCqQV3c4CoVm3kdPhyeZ07VmBRdH2EpMFA/pd9OASpOEC3aXNGoqPDAZ80D0cLpMBxnmk0+yNhGsEx31hq7Gtw==} + engines: {node: '>=18'} + peerDependencies: + postcss-selector-parser: ^7.0.0 + + '@csstools/utilities@2.0.0': + resolution: {integrity: sha512-5VdOr0Z71u+Yp3ozOx8T11N703wIFGVRgOWbOZMKgglPJsWA54MRIoMNVMa7shUToIhx5J8vX4sOZgD2XiihiQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + '@ctrl/tinycolor@4.2.0': + resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} + engines: {node: '>=14'} + + '@docsearch/css@3.8.2': + resolution: {integrity: sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ==} + + '@docsearch/js@3.8.2': + resolution: {integrity: sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ==} + + '@docsearch/react@3.8.2': + resolution: {integrity: sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg==} + peerDependencies: + '@types/react': '>= 16.8.0 < 19.0.0' + react: '>= 16.8.0 < 19.0.0' + react-dom: '>= 16.8.0 < 19.0.0' + search-insights: '>= 1 < 3' + peerDependenciesMeta: + '@types/react': + optional: true + react: + optional: true + react-dom: + optional: true + search-insights: + optional: true + + '@dual-bundle/import-meta-resolve@4.2.1': + resolution: {integrity: sha512-id+7YRUgoUX6CgV0DtuhirQWodeeA7Lf4i2x71JS/vtA5pRb/hIGWlw+G6MeXvsM+MXrz0VAydTGElX1rAfgPg==} + + '@element-plus/icons-vue@2.3.2': + resolution: {integrity: sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==} + peerDependencies: + vue: ^3.5.24 + + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} + + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/unitless@0.8.1': + resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + + '@es-joy/jsdoccomment@0.50.2': + resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} + engines: {node: '>=18'} + + '@es-joy/jsdoccomment@0.76.0': + resolution: {integrity: sha512-g+RihtzFgGTx2WYCuTHbdOXJeAlGnROws0TeALx9ow/ZmOROOZkVg5wp/B44n0WJgI4SQFP1eWM2iRPlU2Y14w==} + engines: {node: '>=20.11.0'} + + '@es-joy/resolve.exports@1.2.0': + resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} + engines: {node: '>=10'} + + '@esbuild/aix-ppc64@0.25.3': + resolution: {integrity: sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.3': + resolution: {integrity: sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.3': + resolution: {integrity: sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.3': + resolution: {integrity: sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.3': + resolution: {integrity: sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.3': + resolution: {integrity: sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.3': + resolution: {integrity: sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.3': + resolution: {integrity: sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.3': + resolution: {integrity: sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.3': + resolution: {integrity: sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.3': + resolution: {integrity: sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.3': + resolution: {integrity: sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.3': + resolution: {integrity: sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.3': + resolution: {integrity: sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.3': + resolution: {integrity: sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.3': + resolution: {integrity: sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.3': + resolution: {integrity: sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.3': + resolution: {integrity: sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.3': + resolution: {integrity: sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.3': + resolution: {integrity: sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.3': + resolution: {integrity: sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.3': + resolution: {integrity: sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.3': + resolution: {integrity: sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.3': + resolution: {integrity: sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.3': + resolution: {integrity: sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.1': + resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@floating-ui/vue@1.1.9': + resolution: {integrity: sha512-BfNqNW6KA83Nexspgb9DZuz578R7HT8MZw1CfK9I6Ah4QReNWEJsXWHN+SdmOVLNGmTPDi+fDT535Df5PzMLbQ==} + + '@form-create/ant-design-vue@3.2.33': + resolution: {integrity: sha512-FRJ6Jzpm2RT4QL4A69ew9A28CtymtPFAyN5GVtinhIv6kHkIW73PvxfYlppODjT1K06Et7oBowTh1JiJbHvUSw==} + peerDependencies: + vue: ^3.5.24 + + '@form-create/antd-designer@3.4.0': + resolution: {integrity: sha512-55wFRbsLJ02Fj4+CLJK2HJ628xsQRafzlmz2yhHFk99O8eMl523QpxD/0O4yH7e+azqakxW6H5WoFexG78qSgA==} + peerDependencies: + vue: ^3.5.24 + + '@form-create/component-antdv-frame@3.2.31': + resolution: {integrity: sha512-1pg+OsmZLnhIZJvg4dC14R0KNaRVGGN1pxZkY2hf0iFeoKViEMnhsRKAqJzjAf3uGE/2jSyXpjRpRPZi8Egwww==} + + '@form-create/component-antdv-group@3.2.31': + resolution: {integrity: sha512-jblPbg61v9Ic/c52jGE5WbVNT4t0Eok005jh1RuEa9yIvashoH6TEOLJFFogDMdkjB/WvHuRyxHX2jI9a7fuwQ==} + + '@form-create/component-antdv-upload@3.2.31': + resolution: {integrity: sha512-pTaYM31LnEocHYCrBNqp2qE67NbhjNEG0qO4OJgLjsf8i66Pd47BTB3SLkay2daAH1o1ziRNkoilsQrcHTV3og==} + + '@form-create/component-elm-checkbox@3.2.31': + resolution: {integrity: sha512-XQT66E+nRXA2KGZwU1TujSGtICevDUUwks8K+79xs214lofva+rMaRFtrpZYXFgt0+molMUSOl/wc9dOrNr+1Q==} + + '@form-create/component-elm-frame@3.2.31': + resolution: {integrity: sha512-8ub7SnIRVV6YVIBXHps1A7hi7ivrjm8v1qNIcUOndUliXjP90PZ02YUZkVNh12aX8LNhGtuq8kLZqtdSdh78uA==} + + '@form-create/component-elm-group@3.2.31': + resolution: {integrity: sha512-EzAatuXCYfJzm+5yCcG1cgdN0dkJmpYF+opb5NV7Xk3DBY9Ek9xaL0Y0PFvxHsCCxnluXhwyJho7xPiIreYBXQ==} + + '@form-create/component-elm-radio@3.2.31': + resolution: {integrity: sha512-hm/+tuBPf6bH1iw1xP80JXGe9rhUTovMcQpDfLMNkrdXrLZJ618sKXzID1nId03QKYcocnOHvkbX0NuNPila+A==} + + '@form-create/component-elm-select@3.2.31': + resolution: {integrity: sha512-olUL1rt31xn5MYF13b/5KGWXf0iJuo5z3l5xowP2xmfzWV9OfX8fRNToXNurZj9Y2HQbFBAA/uy78TFHqKh4gQ==} + + '@form-create/component-elm-tree@3.2.31': + resolution: {integrity: sha512-cLWwqT+3ES5FSRywQ7HIh05jvEeMPhoQnIxcSbLnzB24tS2eM2x5DhbhjJ02wsZGPvfcO4m3BBp0fgv1RKww1g==} + + '@form-create/component-elm-upload@3.2.31': + resolution: {integrity: sha512-2XJr7x8gPf+U0FnPYLyPt0+qLG9+lZSO7tlInT0fw7TJF9pWzrtxicnX6sWvd4EoWCNzYU13HXL33Aq1zP2IeA==} + + '@form-create/component-subform@3.1.34': + resolution: {integrity: sha512-OJcFH/7MTHx7JLEjDK/weS27qfuFWAI+OK+gXTJ2jIt9aZkGWF/EWkjetiJLt5a0KMw4Z15wOS2XCY9pVK9vlA==} + + '@form-create/component-wangeditor@3.2.14': + resolution: {integrity: sha512-N/U/hFBdBu2OIguxoKe1Kslq5fW6XmtyhKDImLfKLn1xI6X5WUtt3r7QTaUPcVUl2vntpM9wJ/FBdG17RzF/Dg==} + + '@form-create/core@3.2.33': + resolution: {integrity: sha512-4TBFtJFkpq8gkeBNpx+Gelyl5Pxk76aTztr/dA37isIsrgsJi/5iL50lRdBGDTK88rj5puPaQ7M/R5tgTQKOrg==} + peerDependencies: + vue: ^3.5.24 + + '@form-create/designer@3.4.0': + resolution: {integrity: sha512-g1vUqX977lIHuWtAIhgLKZGZw87YClA1sJnLOFkweGA5CBJDhqNhvC4N0M1idkuKmCJtS+aweCYmrL2FapjDsw==} + peerDependencies: + vue: ^3.5.24 + + '@form-create/element-ui@3.2.33': + resolution: {integrity: sha512-h2Oj/De35Z0zklhbXmRV578NKXp2ydEH9Pml7F0AaBYw8QKZQrsMtCFfF+mOl/mOVmO0sOWTtujrkNqm1W82fQ==} + peerDependencies: + vue: ^3.5.24 + + '@form-create/utils@3.2.31': + resolution: {integrity: sha512-YcisHXuQEF0r9y2VKS2CB2nMQ2q8EkKvQXp4nPR9sjbsCabPZOXq2n2MrkWDMhwQNwep08/0NCf7z0wslHzyWQ==} + + '@gera2ld/jsx-dom@2.2.2': + resolution: {integrity: sha512-EOqf31IATRE6zS1W1EoWmXZhGfLAoO9FIlwTtHduSrBdud4npYBxYAkv8dZ5hudDPwJeeSjn40kbCL4wAzr8dA==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@ibm/telemetry-js@1.10.2': + resolution: {integrity: sha512-F8+/NNUwtm8BuFz18O9KPvIFTFDo8GUSoyhPxPjEpk7nEyEzWGfhIiEPhL00B2NdHRLDSljh3AiCfSnL/tutiQ==} + hasBin: true + + '@iconify-json/logos@1.2.10': + resolution: {integrity: sha512-qxaXKJ6fu8jzTMPQdHtNxlfx6tBQ0jXRbHZIYy5Ilh8Lx9US9FsAdzZWUR8MXV8PnWTKGDFO4ZZee9VwerCyMA==} + + '@iconify-json/octicon@1.2.19': + resolution: {integrity: sha512-q1a9fpyg0Cw/Bt9hEfP86eJlgKtMXzNIRQnsbPZi1MBoHlPyi056TdzV72zY/F+oJSJ8b5Ub8njL2fWs/iLJAg==} + + '@iconify-json/simple-icons@1.2.60': + resolution: {integrity: sha512-KlwLBKCdMCqfySdkAA+jehdUx6VSjnj6lvzQKus7HjkPSQ6QP58d6xiptkIp0jd/Hw3PW2++nRuGvCvSYaF0Mg==} + + '@iconify-json/vscode-icons@1.2.36': + resolution: {integrity: sha512-hs17ocmWK5pGnF5KgFRgHBZVRnMVAIpwPDDvLWSwRiM/uHwIS0Jo0y93DvcHXEsP+d3sqogTGhgkgj3on3eabQ==} + + '@iconify/json@2.2.410': + resolution: {integrity: sha512-0IhW9Sfudf3cPQHoCwr2gJMMUUkLW01WIkGoP9PbwVKXl1I/KTRHtM9IchLufT8M86QHBWRcinApzkL60TH9vA==} + + '@iconify/tailwind@1.2.0': + resolution: {integrity: sha512-KgpIHWOTcRYw1XcoUqyNSrmYyfLLqZYu3AmP8zdfLk0F5TqRO8YerhlvlQmGfn7rJXgPeZN569xPAJnJ53zZxA==} + + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.0.2': + resolution: {integrity: sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==} + + '@iconify/vue@5.0.0': + resolution: {integrity: sha512-C+KuEWIF5nSBrobFJhT//JS87OZ++QDORB6f2q2Wm6fl2mueSTpFBeBsveK0KW9hWiZ4mNiPjsh6Zs4jjdROSg==} + peerDependencies: + vue: ^3.5.24 + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@internationalized/date@3.10.0': + resolution: {integrity: sha512-oxDR/NTEJ1k+UFVQElaNIk65E/Z83HK1z1WI3lQyhTtnNg4R5oVXaPzK3jcpKG8UHKDVuDQHzn+wsxSz8RP3aw==} + + '@internationalized/number@3.6.5': + resolution: {integrity: sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==} + + '@intlify/bundle-utils@10.0.1': + resolution: {integrity: sha512-WkaXfSevtpgtUR4t8K2M6lbR7g03mtOxFeh+vXp5KExvPqS12ppaRj1QxzwRuRI5VUto54A22BjKoBMLyHILWQ==} + engines: {node: '>= 18'} + peerDependencies: + petite-vue-i18n: '*' + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/core-base@11.2.2': + resolution: {integrity: sha512-0mCTBOLKIqFUP3BzwuFW23hYEl9g/wby6uY//AC5hTgQfTsM2srCYF2/hYGp+a5DZ/HIFIgKkLJMzXTt30r0JQ==} + engines: {node: '>= 16'} + + '@intlify/message-compiler@11.2.2': + resolution: {integrity: sha512-XS2p8Ff5JxWsKhgfld4/MRQzZRQ85drMMPhb7Co6Be4ZOgqJX1DzcZt0IFgGTycgqL8rkYNwgnD443Q+TapOoA==} + engines: {node: '>= 16'} + + '@intlify/shared@11.2.2': + resolution: {integrity: sha512-OtCmyFpSXxNu/oET/aN6HtPCbZ01btXVd0f3w00YsHOb13Kverk1jzA2k47pAekM55qbUw421fvPF1yxZ+gicw==} + engines: {node: '>= 16'} + + '@intlify/unplugin-vue-i18n@6.0.8': + resolution: {integrity: sha512-Vvm3KhjE6TIBVUQAk37rBiaYy2M5OcWH0ZcI1XKEsOTeN1o0bErk+zeuXmcrcMc/73YggfI8RoxOUz9EB/69JQ==} + engines: {node: '>= 18'} + peerDependencies: + petite-vue-i18n: '*' + vue: ^3.5.24 + vue-i18n: '*' + peerDependenciesMeta: + petite-vue-i18n: + optional: true + vue-i18n: + optional: true + + '@intlify/vue-i18n-extensions@8.0.0': + resolution: {integrity: sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==} + engines: {node: '>= 18'} + peerDependencies: + '@intlify/shared': ^9.0.0 || ^10.0.0 || ^11.0.0 + '@vue/compiler-dom': ^3.0.0 + vue: ^3.5.24 + vue-i18n: ^9.0.0 || ^10.0.0 || ^11.0.0 + peerDependenciesMeta: + '@intlify/shared': + optional: true + '@vue/compiler-dom': + optional: true + vue: + optional: true + vue-i18n: + optional: true + + '@ioredis/commands@1.4.0': + resolution: {integrity: sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==} + + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jspm/generator@2.7.6': + resolution: {integrity: sha512-rAUFW/+7kORaUngC+jevgK00mzD30wC4tErjneVZ4LDHuUYa44OQ/o78Hd3pYlf+tHocMyW7rQD52pt8jhW6lA==} + + '@jspm/import-map@1.2.2': + resolution: {integrity: sha512-QrM7+lkgVE8t9NZSm9fDSzheguEOVCOxueKxSwgoOt7yUWesWKDFmmL9bQFURl4IqMcpPz7VV2Tvw9s9emaJzQ==} + + '@juggle/resize-observer@3.4.0': + resolution: {integrity: sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==} + + '@keyv/bigmap@1.3.0': + resolution: {integrity: sha512-KT01GjzV6AQD5+IYrcpoYLkCu1Jod3nau1Z7EsEuViO3TZGRacSbO9MfHmbJ1WaOXFtWLxPVj169cn2WNKPkIg==} + engines: {node: '>= 18'} + peerDependencies: + keyv: ^5.5.4 + + '@keyv/serialize@1.1.1': + resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} + + '@lezer/common@1.3.0': + resolution: {integrity: sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==} + + '@lezer/highlight@1.2.3': + resolution: {integrity: sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==} + + '@lezer/lr@1.4.3': + resolution: {integrity: sha512-yenN5SqAxAPv/qMnpWW0AT7l+SxVrgG+u0tNsRQWqbrz66HIl8DnEbBObvy21J5K7+I1v7gsAnlE2VQ5yYVSeA==} + + '@lezer/markdown@1.6.0': + resolution: {integrity: sha512-AXb98u3M6BEzTnreBnGtQaF7xFTiMA92Dsy5tqEjpacbjRxDSFdN4bKJo9uvU4cEEOS7D2B9MT7kvDgOEIzJSw==} + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/find-root@3.1.0': + resolution: {integrity: sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==} + engines: {node: '>=20.0.0'} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@manypkg/get-packages@3.1.0': + resolution: {integrity: sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==} + engines: {node: '>=20.0.0'} + + '@manypkg/tools@2.1.0': + resolution: {integrity: sha512-0FOIepYR4ugPYaHwK7hDeHDkfPOBVvayt9QpvRbi2LT/h2b0GaE/gM9Gag7fsnyYyNaTZ2IGyOuVg07IYepvYQ==} + engines: {node: '>=20.0.0'} + + '@mapbox/node-pre-gyp@2.0.3': + resolution: {integrity: sha512-uwPAhccfFJlsfCxMYTwOdVfOz3xqyj8xYL3zJj8f0pb30tLohnnFPhLuqp4/qoEz8sNxe4SESZedcBojRefIzg==} + engines: {node: '>=18'} + hasBin: true + + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + + '@microsoft/api-extractor-model@7.32.1': + resolution: {integrity: sha512-u4yJytMYiUAnhcNQcZDTh/tVtlrzKlyKrQnLOV+4Qr/5gV+cpufWzCYAB1Q23URFqD6z2RoL2UYncM9xJVGNKA==} + + '@microsoft/api-extractor@7.55.1': + resolution: {integrity: sha512-l8Z+8qrLkZFM3HM95Dbpqs6G39fpCa7O5p8A7AkA6hSevxkgwsOlLrEuPv0ADOyj5dI1Af5WVDiwpKG/ya5G3w==} + hasBin: true + + '@microsoft/fetch-event-source@2.0.1': + resolution: {integrity: sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==} + + '@microsoft/tsdoc-config@0.18.0': + resolution: {integrity: sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==} + + '@microsoft/tsdoc@0.16.0': + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolebase/ui@2.18.2': + resolution: {integrity: sha512-xxfRacF9cqQ5/umMhvhr0y2W4SkhzTmrrAHJ0UAAu/pIWfV/JPE9Hj0buH06bK7ZEUur+036gxkKlStI6UtDBw==} + peerDependencies: + vitepress: ^1.5.0 || ^2.0.0-alpha.1 + vue: ^3.5.24 + + '@nolebase/vitepress-plugin-git-changelog@2.18.2': + resolution: {integrity: sha512-TT1nxP+iS+5K8OLtkEr1EZ4bhCa4iqggly0JTD7TsO01hcwzMovr0lRaxPTo/4i/0bwMiPcaKxpKJ2zJIiu4Kw==} + peerDependencies: + vitepress: ^1.5.0 || ^2.0.0-alpha.1 + + '@npmcli/fs@5.0.0': + resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@nuxt/kit@3.20.1': + resolution: {integrity: sha512-TIslaylfI5kd3AxX5qts0qyrIQ9Uq3HAA1bgIIJ+c+zpDfK338YS+YrCWxBBzDMECRCbAS58mqAd2MtJfG1ENA==} + engines: {node: '>=18.12.0'} + + '@nuxt/kit@4.2.1': + resolution: {integrity: sha512-lLt8KLHyl7IClc3RqRpRikz15eCfTRlAWL9leVzPyg5N87FfKE/7EWgWvpiL/z4Tf3dQCIqQb88TmHE0JTIDvA==} + engines: {node: '>=18.12.0'} + + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@parcel/watcher-wasm@2.5.1': + resolution: {integrity: sha512-RJxlQQLkaMMIuWRozy+z2vEqbaQlCuaCgVZIUCzQLYggY22LZbP5Y1+ia+FD724Ids9e+XIyOLXLrLgQSHIthw==} + engines: {node: '>= 10.0.0'} + bundledDependencies: + - napi-wasm + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@playwright/test@1.57.0': + resolution: {integrity: sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==} + engines: {node: '>=18'} + hasBin: true + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/constants@1001.3.1': + resolution: {integrity: sha512-2hf0s4pVrVEH8RvdJJ7YRKjQdiG8m0iAT26TTqXnCbK30kKwJW69VLmP5tED5zstmDRXcOeH5eRcrpkdwczQ9g==} + engines: {node: '>=18.12'} + + '@pnpm/error@1000.0.5': + resolution: {integrity: sha512-GjH0TPjbVNrPnl/BAGoFuBLJ2sFfXNKbS33lll/Ehe9yw0fyc8Kdw7kO9if37yQqn6vaa4dAHKkPllum7f/IPQ==} + engines: {node: '>=18.12'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.3.1': + resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} + engines: {node: '>=12'} + + '@pnpm/types@1001.0.1': + resolution: {integrity: sha512-v5X09E6LkJFOOw9FgGITpAs7nQJtx6u3N0SNtyIC5mSeIC5SebMrrelpCz6QUTJvyXBEa1AWj2dZhYfLj59xhA==} + engines: {node: '>=18.12'} + + '@pnpm/workspace.read-manifest@1000.2.7': + resolution: {integrity: sha512-i+vg9gcUUWPfW+MInFeo8/CYHNe31qqLdByHiXjs9GbI1P52c5AFH3Klnx6HglNpb15ZcTj6GU2kVZEfn18Gyw==} + engines: {node: '>=18.12'} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@poppinss/colors@4.1.5': + resolution: {integrity: sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==} + + '@poppinss/dumper@0.6.5': + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} + + '@poppinss/exception@1.2.2': + resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==} + + '@publint/pack@0.1.2': + resolution: {integrity: sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw==} + engines: {node: '>=18'} + + '@rolldown/pluginutils@1.0.0-beta.50': + resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} + + '@rolldown/pluginutils@1.0.0-beta.52': + resolution: {integrity: sha512-/L0htLJZbaZFL1g9OHOblTxbCYIGefErJjtYOwgl9ZqNx27P3L0SDfjhhHIss32gu5NWgnxuT2a2Hnnv6QGHKA==} + + '@rollup/plugin-alias@5.1.1': + resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-babel@5.3.1': + resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==} + engines: {node: '>= 10.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + + '@rollup/plugin-commonjs@28.0.9': + resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-inject@5.0.5': + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@15.3.1': + resolution: {integrity: sha512-tgg6b91pAybXHJQMAAwW9VuWBO6Thi+q7BCNARLwSqlmsHz0XYURtGvh/AuwSADXSI4h/2uHbs7s4FzlZDGSGA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-replace@2.4.2': + resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==} + peerDependencies: + rollup: ^1.20.0 || ^2.0.0 + + '@rollup/plugin-replace@6.0.3': + resolution: {integrity: sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-terser@0.4.4': + resolution: {integrity: sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@3.1.0': + resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==} + engines: {node: '>= 8.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0 + + '@rollup/pluginutils@4.2.1': + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + + '@rushstack/node-core-library@5.19.0': + resolution: {integrity: sha512-BxAopbeWBvNJ6VGiUL+5lbJXywTdsnMeOS8j57Cn/xY10r6sV/gbsTlfYKjzVCUBZATX2eRzJHSMCchsMTGN6A==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/problem-matcher@0.1.1': + resolution: {integrity: sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.6.0': + resolution: {integrity: sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==} + + '@rushstack/terminal@0.19.4': + resolution: {integrity: sha512-f4XQk02CrKfrMgyOfhYd3qWI944dLC21S4I/LUhrlAP23GTMDNG6EK5effQtFkISwUKCgD9vMBrJZaPSUquxWQ==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@5.1.4': + resolution: {integrity: sha512-H0I6VdJ6sOUbktDFpP2VW5N29w8v4hRoNZOQz02vtEi6ZTYL1Ju8u+TcFiFawUDrUsx/5MQTUhd79uwZZVwVlA==} + + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + + '@shikijs/core@2.5.0': + resolution: {integrity: sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg==} + + '@shikijs/engine-javascript@2.5.0': + resolution: {integrity: sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w==} + + '@shikijs/engine-oniguruma@2.5.0': + resolution: {integrity: sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw==} + + '@shikijs/langs@2.5.0': + resolution: {integrity: sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w==} + + '@shikijs/themes@2.5.0': + resolution: {integrity: sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw==} + + '@shikijs/transformers@2.5.0': + resolution: {integrity: sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg==} + + '@shikijs/types@2.5.0': + resolution: {integrity: sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw==} + + '@shikijs/vscode-textmate@10.0.2': + resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + + '@simonwep/pickr@1.8.2': + resolution: {integrity: sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==} + + '@sindresorhus/base62@1.0.0': + resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} + engines: {node: '>=18'} + + '@sindresorhus/is@7.1.1': + resolution: {integrity: sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ==} + engines: {node: '>=18'} + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + + '@speed-highlight/core@1.2.12': + resolution: {integrity: sha512-uilwrK0Ygyri5dToHYdZSjcvpS2ZwX0w5aSt3GCEN9hrjxWCoeV4Z2DTXuxjwbntaLQIEEAlCeNQss5SoHvAEA==} + + '@stylistic/stylelint-plugin@3.1.3': + resolution: {integrity: sha512-85fsmzgsIVmyG3/GFrjuYj6Cz8rAM7IZiPiXCMiSMfoDOC1lOrzrXPDk24WqviAghnPqGpx8b0caK2PuewWGFg==} + engines: {node: ^18.12 || >=20.9} + peerDependencies: + stylelint: ^16.8.0 + + '@surma/rollup-plugin-off-main-thread@2.2.3': + resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} + + '@svelte-put/shortcut@4.1.0': + resolution: {integrity: sha512-wImNEIkbxAIWFqlfuhcbC+jRPDeRa/uJGIXHMEVVD+jqL9xCwWNnkGQJ6Qb2XVszuRLHlb8SGZDL3Io/h3vs8w==} + peerDependencies: + svelte: ^5.1.0 + + '@sveltejs/acorn-typescript@1.0.7': + resolution: {integrity: sha512-znp1A/Y1Jj4l/Zy7PX5DZKBE0ZNY+5QBngiE21NJkfSTyzzC5iKNWOtwFXKtIrn7MXEFBck4jD95iBNkGjK92Q==} + peerDependencies: + acorn: ^8.9.0 + + '@swc/helpers@0.5.17': + resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + + '@sxzz/popperjs-es@2.11.7': + resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} + + '@tailwindcss/nesting@0.0.0-insiders.565cd3e': + resolution: {integrity: sha512-WhHoFBx19TnH/c+xLwT/sxei6+4RpdfiyG3MYXfmLaMsADmVqBkF7B6lDalgZD9YdM459MF7DtxVbWkOrV7IaQ==} + peerDependencies: + postcss: ^8.2.15 + + '@tailwindcss/typography@0.5.19': + resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + + '@tanstack/store@0.8.0': + resolution: {integrity: sha512-Om+BO0YfMZe//X2z0uLF2j+75nQga6TpTJgLJQBiq85aOyZNIhkCgleNcud2KQg4k4v9Y9l+Uhru3qWMPGTOzQ==} + + '@tanstack/virtual-core@3.13.12': + resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} + + '@tanstack/vue-store@0.8.0': + resolution: {integrity: sha512-YLsinYboBLIjNkxDpAn1ydaMS35dKq3M3a788JRCJi4/stWcN7Swp0pxxJ+p0IwKSY4tBXx7vMz22OYWQ1QsUQ==} + peerDependencies: + '@vue/composition-api': ^1.2.1 + vue: ^3.5.24 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + '@tanstack/vue-virtual@3.13.12': + resolution: {integrity: sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==} + peerDependencies: + vue: ^3.5.24 + + '@tinyflow-ai/ui@1.1.9': + resolution: {integrity: sha512-Y20B5YKJJ9T6iw6aLPDIteziekq5+9rHrMKGco4hmwLrGfzAjbfdWAk9alMrQ/9MAdeXRxDsBXTSsqYQROy+DQ==} + + '@tinyflow-ai/vue@1.1.9': + resolution: {integrity: sha512-6UHG5AqlimsOZOvjcLldtT/lk2VYNUO8wX6i7TRBZignDdJlUsJwWZHhtF27Crj3ol9mIGnXr7cPBe6Y9P4iFw==} + peerDependencies: + vue: ^3.5.24 + + '@tinymce/tinymce-vue@6.3.0': + resolution: {integrity: sha512-DSP8Jhd3XqCCliTnusfbmz3D8GqQ4iRzkc4aadYHDcJPVjkaqopJ61McOdH82CSy599vGLkPjGzqJYWJkRMiUA==} + peerDependencies: + tinymce: ^8.0.0 || ^7.0.0 || ^6.0.0 || ^5.5.1 + vue: ^3.5.24 + peerDependenciesMeta: + tinymce: + optional: true + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/archiver@6.0.4': + resolution: {integrity: sha512-ULdQpARQ3sz9WH4nb98mJDYA0ft2A8C4f4fovvUcFwINa1cgGjY36JCAYuP5YypRq4mco1lJp1/7jEMS2oR0Hg==} + + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + + '@types/bintrees@1.0.6': + resolution: {integrity: sha512-pZWT4Bz+tWwxlDspSjdoIza4PE5lbGI4Xvs3FZV/2v5m5SDA8LwNpU8AXxlndmARO7OaQ1Vf3zFenOsNMzaRkQ==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/codemirror@5.60.17': + resolution: {integrity: sha512-AZq2FIsUHVMlp7VSe2hTfl5w4pcUkoFkM3zVsRKsn1ca8CXRDYvnin04+HP2REkwsxemuHqvDofdlhUWNpbwfw==} + + '@types/conventional-commits-parser@5.0.2': + resolution: {integrity: sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==} + + '@types/crypto-js@4.2.2': + resolution: {integrity: sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@0.0.39': + resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/html-minifier-terser@7.0.2': + resolution: {integrity: sha512-mm2HqV22l8lFQh4r2oSsOEVea+m0qqxEmwpc9kC1p/XzmjLWrReR9D/GRs8Pex2NX/imyEH9c5IU/7tMBQCHOA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/katex@0.16.7': + resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} + + '@types/linkify-it@5.0.0': + resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash.clonedeep@4.5.9': + resolution: {integrity: sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==} + + '@types/lodash.get@4.4.9': + resolution: {integrity: sha512-J5dvW98sxmGnamqf+/aLP87PYXyrha9xIgc2ZlHl6OHMFR2Ejdxep50QfU0abO1+CH6+ugx+8wEUN1toImAinA==} + + '@types/lodash.isequal@4.5.8': + resolution: {integrity: sha512-uput6pg4E/tj2LGxCZo9+y27JNyB2OZuuI/T5F+ylVDYuqICLG2/ktjxx0v6GvVntAf8TvEzeQLcV0ffRirXuA==} + + '@types/lodash.set@4.3.9': + resolution: {integrity: sha512-KOxyNkZpbaggVmqbpr82N2tDVTx05/3/j0f50Es1prxrWB0XYf9p3QNxqcbWb7P1Q9wlvsUSlCFnwlPCIJ46PQ==} + + '@types/lodash@4.17.21': + resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==} + + '@types/markdown-it@14.1.2': + resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mdurl@2.0.0': + resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + + '@types/minimatch@3.0.5': + resolution: {integrity: sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} + + '@types/nprogress@0.2.3': + resolution: {integrity: sha512-k7kRA033QNtC+gLc4VPlfnue58CM1iQLgn1IMAU8VPHGOj7oIHPp9UlhedEnD/Gl8evoCjwkZjlBORtZ3JByUA==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/postcss-import@14.0.3': + resolution: {integrity: sha512-raZhRVTf6Vw5+QbmQ7LOHSDML71A5rj4+EqDzAbrZPfxfoGzFxMHRCq16VlddGIZpHELw0BG4G0YE2ANkdZiIQ==} + + '@types/qrcode@1.5.6': + resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} + + '@types/qs@6.14.0': + resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} + + '@types/readdir-glob@1.1.5': + resolution: {integrity: sha512-raiuEPUYqXu+nvtY2Pe8s8FEmZ3x5yAH4VkLdihcPdalvsHltomrRC9BzuStrJ9yk06470hS0Crw0f1pXqD+Hg==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/sortablejs@1.15.9': + resolution: {integrity: sha512-7HP+rZGE2p886PKV9c9OJzLBI6BBJu1O7lJGYnPyG3fS4/duUCcngkNCjsLwIMV+WMqANe3tt4irrXHSIe68OQ==} + + '@types/tern@0.23.9': + resolution: {integrity: sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==} + + '@types/tinycolor2@1.4.6': + resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/validator@13.15.10': + resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} + + '@types/video.js@7.3.58': + resolution: {integrity: sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ==} + + '@types/web-bluetooth@0.0.16': + resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} + + '@types/web-bluetooth@0.0.21': + resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + + '@typescript-eslint/eslint-plugin@8.48.0': + resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.48.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.48.0': + resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.48.0': + resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/scope-manager@8.48.0': + resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.48.0': + resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.48.0': + resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/types@8.48.0': + resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/typescript-estree@8.48.0': + resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/utils@8.48.0': + resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/visitor-keys@8.48.0': + resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@vee-validate/zod@4.15.1': + resolution: {integrity: sha512-329Z4TDBE5Vx0FdbA8S4eR9iGCFFUNGbxjpQ20ff5b5wGueScjocUIx9JHPa79LTG06RnlUR4XogQsjN4tecKA==} + peerDependencies: + zod: ^3.24.0 + + '@vercel/nft@0.30.4': + resolution: {integrity: sha512-wE6eAGSXScra60N2l6jWvNtVK0m+sh873CpfZW4KI2v8EHuUQp+mSEi4T+IcdPCSEDgCdAS/7bizbhQlkjzrSA==} + engines: {node: '>=18'} + hasBin: true + + '@videojs-player/vue@1.0.0': + resolution: {integrity: sha512-WonTezRfKu3fYdQLt/ta+nuKH6gMZUv8l40Jke/j4Lae7IqeO/+lLAmBnh3ni88bwR+vkFXIlZ2Ci7VKInIYJg==} + peerDependencies: + '@types/video.js': 7.x + video.js: 7.x + vue: ^3.5.24 + + '@videojs/http-streaming@2.16.3': + resolution: {integrity: sha512-91CJv5PnFBzNBvyEjt+9cPzTK/xoVixARj2g7ZAvItA+5bx8VKdk5RxCz/PP2kdzz9W+NiDUMPkdmTsosmy69Q==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + video.js: ^6 || ^7 + + '@videojs/vhs-utils@3.0.5': + resolution: {integrity: sha512-PKVgdo8/GReqdx512F+ombhS+Bzogiofy1LgAj4tN8PfdBx3HSS7V5WfJotKTqtOWGwVfSWsrYN/t09/DSryrw==} + engines: {node: '>=8', npm: '>=5'} + + '@videojs/xhr@2.6.0': + resolution: {integrity: sha512-7J361GiN1tXpm+gd0xz2QWr3xNWBE+rytvo8J3KuggFaLg+U37gZQ2BuPLcnkfGffy2e+ozY70RHC8jt7zjA6Q==} + + '@vite-pwa/vitepress@1.0.1': + resolution: {integrity: sha512-INBxiNLZpef349KSmQ6zHWB4uqIgZgvJnwzH3bedW/7d/Ej0lK5HP95fiBdIc2wHUtmR3Znnegmt3zLESVWrpA==} + peerDependencies: + '@vite-pwa/assets-generator': ^1.0.0 + vite-plugin-pwa: ^1.1.0 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true + + '@vitejs/plugin-vue-jsx@5.1.2': + resolution: {integrity: sha512-3a2BOryRjG/Iih87x87YXz5c8nw27eSlHytvSKYfp8ZIsp5+FgFQoKeA7k2PnqWpjJrv6AoVTMnvmuKUXb771A==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vue: ^3.5.24 + + '@vitejs/plugin-vue@5.2.4': + resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 + vue: ^3.5.24 + + '@vitejs/plugin-vue@6.0.2': + resolution: {integrity: sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vue: ^3.5.24 + + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + + '@volar/language-core@2.4.23': + resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} + + '@volar/language-core@2.4.26': + resolution: {integrity: sha512-hH0SMitMxnB43OZpyF1IFPS9bgb2I3bpCh76m2WEK7BE0A0EzpYsRp0CCH2xNKshr7kacU5TQBLYn4zj7CG60A==} + + '@volar/source-map@2.4.23': + resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==} + + '@volar/source-map@2.4.26': + resolution: {integrity: sha512-JJw0Tt/kSFsIRmgTQF4JSt81AUSI1aEye5Zl65EeZ8H35JHnTvFGmpDOBn5iOxd48fyGE+ZvZBp5FcgAy/1Qhw==} + + '@volar/typescript@2.4.23': + resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==} + + '@volar/typescript@2.4.26': + resolution: {integrity: sha512-N87ecLD48Sp6zV9zID/5yuS1+5foj0DfuYGdQ6KHj/IbKvyKv1zNX6VCmnKYwtmHadEO6mFc2EKISiu3RDPAvA==} + + '@vue/babel-helper-vue-transform-on@1.5.0': + resolution: {integrity: sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==} + + '@vue/babel-helper-vue-transform-on@2.0.1': + resolution: {integrity: sha512-uZ66EaFbnnZSYqYEyplWvn46GhZ1KuYSThdT68p+am7MgBNbQ3hphTL9L+xSIsWkdktwhPYLwPgVWqo96jDdRA==} + + '@vue/babel-plugin-jsx@1.5.0': + resolution: {integrity: sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-jsx@2.0.1': + resolution: {integrity: sha512-a8CaLQjD/s4PVdhrLD/zT574ZNPnZBOY+IhdtKWRB4HRZ0I2tXBi5ne7d9eCfaYwp5gU5+4KIyFTV1W1YL9xZA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + peerDependenciesMeta: + '@babel/core': + optional: true + + '@vue/babel-plugin-resolve-type@1.5.0': + resolution: {integrity: sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/babel-plugin-resolve-type@2.0.1': + resolution: {integrity: sha512-ybwgIuRGRRBhOU37GImDoWQoz+TlSqap65qVI6iwg/J7FfLTLmMf97TS7xQH9I7Qtr/gp161kYVdhr1ZMraSYQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/compiler-core@3.5.25': + resolution: {integrity: sha512-vay5/oQJdsNHmliWoZfHPoVZZRmnSWhug0BYT34njkYTPqClh3DNWLkZNJBVSjsNMrg0CCrBfoKkjZQPM/QVUw==} + + '@vue/compiler-dom@3.5.25': + resolution: {integrity: sha512-4We0OAcMZsKgYoGlMjzYvaoErltdFI2/25wqanuTu+S4gismOTRTBPi4IASOjxWdzIwrYSjnqONfKvuqkXzE2Q==} + + '@vue/compiler-sfc@3.5.25': + resolution: {integrity: sha512-PUgKp2rn8fFsI++lF2sO7gwO2d9Yj57Utr5yEsDf3GNaQcowCLKL7sf+LvVFvtJDXUp/03+dC6f2+LCv5aK1ag==} + + '@vue/compiler-ssr@3.5.25': + resolution: {integrity: sha512-ritPSKLBcParnsKYi+GNtbdbrIE1mtuFEJ4U1sWeuOMlIziK5GtOL85t5RhsNy4uWIXPgk+OUdpnXiTdzn8o3A==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/devtools-api@6.6.4': + resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} + + '@vue/devtools-api@7.7.9': + resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} + + '@vue/devtools-core@8.0.5': + resolution: {integrity: sha512-dpCw8nl0GDBuiL9SaY0mtDxoGIEmU38w+TQiYEPOLhW03VDC0lfNMYXS/qhl4I0YlysGp04NLY4UNn6xgD0VIQ==} + peerDependencies: + vue: ^3.5.24 + + '@vue/devtools-kit@7.7.9': + resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} + + '@vue/devtools-kit@8.0.5': + resolution: {integrity: sha512-q2VV6x1U3KJMTQPUlRMyWEKVbcHuxhqJdSr6Jtjz5uAThAIrfJ6WVZdGZm5cuO63ZnSUz0RCsVwiUUb0mDV0Yg==} + + '@vue/devtools-shared@7.7.9': + resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} + + '@vue/devtools-shared@8.0.5': + resolution: {integrity: sha512-bRLn6/spxpmgLk+iwOrR29KrYnJjG9DGpHGkDFG82UM21ZpJ39ztUT9OXX3g+usW7/b2z+h46I9ZiYyB07XMXg==} + + '@vue/language-core@2.2.0': + resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/language-core@3.1.5': + resolution: {integrity: sha512-FMcqyzWN+sYBeqRMWPGT2QY0mUasZMVIuHvmb5NT3eeqPrbHBYtCP8JWEUCDCgM+Zr62uuWY/qoeBrPrzfa78w==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.5.25': + resolution: {integrity: sha512-5xfAypCQepv4Jog1U4zn8cZIcbKKFka3AgWHEFQeK65OW+Ys4XybP6z2kKgws4YB43KGpqp5D/K3go2UPPunLA==} + + '@vue/runtime-core@3.5.25': + resolution: {integrity: sha512-Z751v203YWwYzy460bzsYQISDfPjHTl+6Zzwo/a3CsAf+0ccEjQ8c+0CdX1WsumRTHeywvyUFtW6KvNukT/smA==} + + '@vue/runtime-dom@3.5.25': + resolution: {integrity: sha512-a4WrkYFbb19i9pjkz38zJBg8wa/rboNERq3+hRRb0dHiJh13c+6kAbgqCPfMaJ2gg4weWD3APZswASOfmKwamA==} + + '@vue/server-renderer@3.5.25': + resolution: {integrity: sha512-UJaXR54vMG61i8XNIzTSf2Q7MOqZHpp8+x3XLGtE3+fL+nQd+k7O5+X3D/uWrnQXOdMw5VPih+Uremcw+u1woQ==} + peerDependencies: + vue: ^3.5.24 + + '@vue/shared@3.5.25': + resolution: {integrity: sha512-AbOPdQQnAnzs58H2FrrDxYj/TJfmeS2jdfEEhgiKINy+bnOANmVizIEgq1r+C5zsbs6l1CCQxtcj71rwNQ4jWg==} + + '@vue/test-utils@2.4.6': + resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} + + '@vueuse/core@12.8.2': + resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==} + + '@vueuse/core@13.9.0': + resolution: {integrity: sha512-ts3regBQyURfCE2BcytLqzm8+MmLlo5Ln/KLoxDVcsZ2gzIwVNnQpQOL/UKV8alUqjSZOlpFZcRNsLRqj+OzyA==} + peerDependencies: + vue: ^3.5.24 + + '@vueuse/core@14.1.0': + resolution: {integrity: sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw==} + peerDependencies: + vue: ^3.5.24 + + '@vueuse/core@9.13.0': + resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} + + '@vueuse/integrations@12.8.2': + resolution: {integrity: sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/integrations@14.1.0': + resolution: {integrity: sha512-eNQPdisnO9SvdydTIXnTE7c29yOsJBD/xkwEyQLdhDC/LKbqrFpXHb3uS//7NcIrQO3fWVuvMGp8dbK6mNEMCA==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^5 + drauu: ^0.4 + focus-trap: ^7 + fuse.js: ^7 + idb-keyval: ^6 + jwt-decode: ^4 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^7 || ^8 + vue: ^3.5.24 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + + '@vueuse/metadata@12.8.2': + resolution: {integrity: sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A==} + + '@vueuse/metadata@13.9.0': + resolution: {integrity: sha512-1AFRvuiGphfF7yWixZa0KwjYH8ulyjDCC0aFgrGRz8+P4kvDFSdXLVfTk5xAN9wEuD1J6z4/myMoYbnHoX07zg==} + + '@vueuse/metadata@14.1.0': + resolution: {integrity: sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA==} + + '@vueuse/metadata@9.13.0': + resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} + + '@vueuse/motion@3.0.3': + resolution: {integrity: sha512-4B+ITsxCI9cojikvrpaJcLXyq0spj3sdlzXjzesWdMRd99hhtFI6OJ/1JsqwtF73YooLe0hUn/xDR6qCtmn5GQ==} + peerDependencies: + vue: ^3.5.24 + + '@vueuse/shared@12.8.2': + resolution: {integrity: sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w==} + + '@vueuse/shared@13.9.0': + resolution: {integrity: sha512-e89uuTLMh0U5cZ9iDpEI2senqPGfbPRTHM/0AaQkcxnpqjkZqDYP8rpfm7edOz8s+pOCOROEy1PIveSW8+fL5g==} + peerDependencies: + vue: ^3.5.24 + + '@vueuse/shared@14.1.0': + resolution: {integrity: sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw==} + peerDependencies: + vue: ^3.5.24 + + '@vueuse/shared@9.13.0': + resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + + '@vxe-ui/core@4.2.15': + resolution: {integrity: sha512-kpregErCgRpz3joGeVOrnOADHOOG/454kGXVy/JqBb4w7ENzCk1OmqDCrvCjaugP09FE0exaTiSdfcv3/+kRQg==} + peerDependencies: + vue: ^3.5.24 + + '@xmldom/xmldom@0.8.11': + resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} + engines: {node: '>=10.0.0'} + + '@xyflow/svelte@1.4.2': + resolution: {integrity: sha512-E6mw8wt3NXS5imGJLWKrdOEOGHAkbFhsVwzb9MPG8ohkPjQ8lMeDM9o3fBSoDNp7xr16s7Q3AOVS2JzVyoY2og==} + peerDependencies: + svelte: ^5.25.0 + + '@xyflow/system@0.0.73': + resolution: {integrity: sha512-C2ymH2V4mYDkdVSiRx0D7R0s3dvfXiupVBcko6tXP5K4tVdSBMo22/e3V9yRNdn+2HQFv44RFKzwOyCcUUDAVQ==} + + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn-import-attributes@1.9.5: + resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} + peerDependencies: + acorn: ^8 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + aes-decrypter@3.1.3: + resolution: {integrity: sha512-VkG9g4BbhMBy+N5/XodDeV6F02chEk9IpgRTq/0bS80y4dzy79VH2Gtms02VXomf3HmyRe3yyJYkJ990ns+d6A==} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + + ajv@8.13.0: + resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + algoliasearch@5.45.0: + resolution: {integrity: sha512-wrj4FGr14heLOYkBKV3Fbq5ZBGuIFeDJkTilYq/G+hH1CSlQBtYvG2X1j67flwv0fUeQJwnWxxRIunSemAZirA==} + engines: {node: '>= 14.0.0'} + + alien-signals@0.4.14: + resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + + alien-signals@3.1.1: + resolution: {integrity: sha512-ogkIWbVrLwKtHY6oOAXaYkAxP+cTH7V5FZ5+Tm4NZFd8VDZ6uNMDrfzqctTZ42eTMCSR3ne3otpcxmqSnFfPYA==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@7.2.0: + resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + ansis@4.2.0: + resolution: {integrity: sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==} + engines: {node: '>=14'} + + ant-design-vue@4.2.6: + resolution: {integrity: sha512-t7eX13Yj3i9+i5g9lqFyYneoIb3OzTvQjq9Tts1i+eiOd3Eva/6GagxBSXM1fOCjqemIu0FYVE1ByZ/38epR3Q==} + engines: {node: '>=12.22.0'} + peerDependencies: + vue: ^3.5.24 + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + archiver-utils@5.0.2: + resolution: {integrity: sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==} + engines: {node: '>= 14'} + + archiver@7.0.1: + resolution: {integrity: sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==} + engines: {node: '>= 14'} + + are-docs-informative@0.0.2: + resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} + engines: {node: '>=14'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-differ@3.0.0: + resolution: {integrity: sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==} + engines: {node: '>=8'} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + array-move@4.0.0: + resolution: {integrity: sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + array-timsort@1.0.3: + resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} + + array-tree-filter@2.1.0: + resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + async-sema@3.1.1: + resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} + + async-validator@4.2.5: + resolution: {integrity: sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + + atomically@2.1.0: + resolution: {integrity: sha512-+gDffFXRW6sl/HCwbta7zK4uNqbPjv4YJEAdz7Vu+FLQHe77eZ4bvbJGi4hE0QPeJlMYMA3piXEr1UL3dAwx7Q==} + + autolinker@3.16.2: + resolution: {integrity: sha512-JiYl7j2Z19F9NdTmirENSUUIIL/9MytEWtmzhfmsKPCp9E+G35Y0UNCMoM9tFigxT59qSc8Ml2dlZXOCVTYwuA==} + + autoprefixer@10.4.22: + resolution: {integrity: sha512-ARe0v/t9gO28Bznv6GgqARmVqcWOV3mfgUPn9becPHMiD3o9BwlRgaeccZnwTpZ7Zwqrm+c1sUSsMxIzQzc8Xg==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axios-mock-adapter@2.1.0: + resolution: {integrity: sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w==} + peerDependencies: + axios: '>= 0.17.0' + + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + b4a@1.7.3: + resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==} + peerDependencies: + react-native-b4a: '*' + peerDependenciesMeta: + react-native-b4a: + optional: true + + babel-plugin-polyfill-corejs2@0.4.14: + resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.13.0: + resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.5: + resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + bare-events@2.8.2: + resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} + peerDependencies: + bare-abort-controller: '*' + peerDependenciesMeta: + bare-abort-controller: + optional: true + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + baseline-browser-mapping@2.8.31: + resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} + hasBin: true + + benz-amr-recorder@1.1.5: + resolution: {integrity: sha512-NepctcNTsZHK8NxBb5uKO5p8S+xkbm+vD6GLSkCYdJeEsriexvgumLHpDkanX4QJBcLRMVtg16buWMs+gUPB3g==} + + benz-recorderjs@1.0.5: + resolution: {integrity: sha512-EwedOQo9KLti7HxDi/eZY51PSRbAXnOdEZmLvJ6ro3QQSoF9Y3AXBt57MIllGvVz5vtFYMeikG+GD7qTm3+p9w==} + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bintrees@1.0.2: + resolution: {integrity: sha512-VOMgTMwjAaUG580SXn3LacVgjurrbMme7ZZNYGSSV7mmtY6QQRh0Eg3pwIcntQ77DErK1L0NxkbetjcoXzVwKw==} + + birpc@2.8.0: + resolution: {integrity: sha512-Bz2a4qD/5GRhiHSwj30c/8kC8QGj12nNDwz3D4ErQ4Xhy35dsSDvF+RA/tWpjyU0pdGtSDiEk6B5fBGE1qNVhw==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + + bpmn-js-properties-panel@5.23.0: + resolution: {integrity: sha512-4B27LM8oV14A2QWRvazV17h4NxbkNERcqU+AGJmxKImMlLhu9893MWR+pCdTQCTphBdBkuD8ksWm+1wVCedJ7g==} + peerDependencies: + '@bpmn-io/properties-panel': '>= 3.7' + bpmn-js: '>= 11.5' + camunda-bpmn-js-behaviors: '>= 0.4' + diagram-js: '>= 11.9' + + bpmn-js-token-simulation@0.36.3: + resolution: {integrity: sha512-HyiExdi+vENiStn284gIUQkQliiWly4dk2kY9PJILwwuTIoKtvg1zw8LGr9ReNUiScibNbpkt45bR25Oqfq9wA==} + engines: {node: '>= 16'} + + bpmn-js@17.11.1: + resolution: {integrity: sha512-ywCeTg5kvN8lYkU+fHE+YXTGlfKc55lRBn7zW3k1//toeMNPy/PS/uQiujRWdFhMrH5dbtDvlwWukNw2pjWw8Q==} + + bpmn-moddle@8.1.0: + resolution: {integrity: sha512-yI5OAFfYVJwViKTsTsonVfCBPtB3MlefADUORwNIxxBOMp21vnoxuxsdgUWlPH/dvAEZh/+mr8UtqOBNu8NC5Q==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.0: + resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-crc32@1.0.0: + resolution: {integrity: sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==} + engines: {node: '>=8.0.0'} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + builtin-modules@5.0.0: + resolution: {integrity: sha512-bkXY9WsVpY7CvMhKSR6pZilZu9Ln5WDrKVBUXf2S443etkmEO4V58heTecXcUIsNsi4Rx8JUO4NfX1IcQl4deg==} + engines: {node: '>=18.20'} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + c12@3.3.2: + resolution: {integrity: sha512-QkikB2X5voO1okL3QsES0N690Sn/K9WokXqUsDQsWy5SnYb+psYQFGA10iy1bZHj3fjISKsI67Q90gruvWWM3A==} + peerDependencies: + magicast: '*' + peerDependenciesMeta: + magicast: + optional: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacache@20.0.3: + resolution: {integrity: sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==} + engines: {node: ^20.17.0 || >=22.9.0} + + cacheable@2.3.0: + resolution: {integrity: sha512-HHiAvOBmlcR2f3SQ7kdlYD8+AUJG+wlFZ/Ze8tl1Vzvz0MdOh8IYA/EFU4ve8t1/sZ0j4MGi7ST5MoTwHessQA==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsite@1.0.0: + resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + camunda-bpmn-js-behaviors@1.11.3: + resolution: {integrity: sha512-9cj9hnwmP9+PDgLQHjXMQkHNgu+me0DrFsOBw/AmbS64s8WWzoGdam27l/ua7ar+7FpP4WZhoopQrhCy4Md99g==} + peerDependencies: + bpmn-js: '>= 9' + camunda-bpmn-moddle: '>= 7' + zeebe-bpmn-moddle: '>= 0.18' + + camunda-bpmn-moddle@7.0.1: + resolution: {integrity: sha512-Br8Diu6roMpziHdpl66Dhnm0DTnCFMrSD9zwLV08LpD52QA0UsXxU87XfHf08HjuB7ly0Hd1bvajZRpf9hbmYQ==} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + chalk-template@1.1.2: + resolution: {integrity: sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==} + engines: {node: '>=14.16'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + change-case@5.4.4: + resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + + 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==} + + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + cheerio-select@2.1.0: + resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==} + + cheerio@1.0.0-rc.12: + resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} + engines: {node: '>= 6'} + + cheerio@1.1.2: + resolution: {integrity: sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==} + engines: {node: '>=20.18.1'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + ci-info@4.3.1: + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} + + circular-dependency-scanner@2.3.0: + resolution: {integrity: sha512-J1a6SeOL1pfaVdCVECg4h1cLxZipjnibha2uutY7gG7Ax8lre03sWdK1raJew0P+8AGv9/zRF5DCWHkceOYPBQ==} + hasBin: true + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + + clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + + clear-module@4.1.2: + resolution: {integrity: sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==} + engines: {node: '>=8'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + clipboard@2.0.11: + resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==} + + clipboardy@4.0.0: + resolution: {integrity: sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==} + engines: {node: '>=18'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + + codemirror@5.65.20: + resolution: {integrity: sha512-i5dLDDxwkFCbhjvL2pNjShsojoL3XHyDwsGv1jqETUoW+lzpBKKqNTUWgQwVAOa0tUm4BwekT455ujafi8payA==} + + codemirror@6.65.7: + resolution: {integrity: sha512-HcfnUFJwI2FvH73YWVbbMh7ObWxZiHIycEhv9ZEXy6e8ZKDjtZKbbYFUtsLN46HFXPvU5V2Uvc2d55Z//oFW5A==} + deprecated: This is an accidentally mis-tagged instance of 5.65.7 + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + comment-json@4.4.1: + resolution: {integrity: sha512-r1To31BQD5060QdkC+Iheai7gHwoSZobzunqkf2/kQ6xIAfJyrKNAFUwdKvkK7Qgu7pVTKQEa7ok7Ed3ycAJgg==} + engines: {node: '>= 6'} + + comment-parser@1.4.1: + resolution: {integrity: sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==} + engines: {node: '>= 12.0.0'} + + commitlint-plugin-function-rules@4.1.1: + resolution: {integrity: sha512-ibvzq+9K6xWDn3+6GvJM0A007WTXuff+Z0qvVG4i36zNIvJ6fN+rsGWNK05MU8mMGrqQ0aOKKbc3QIQkOO+f5Q==} + engines: {node: '>=20'} + peerDependencies: + '@commitlint/lint': '>=19 <21' + + common-tags@1.8.2: + resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==} + engines: {node: '>=4.0.0'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + compatx@0.2.0: + resolution: {integrity: sha512-6gLRNt4ygsi5NyMVhceOCFv14CIdDFN7fQjX1U4+47qVE/+kjPoXMK65KWK+dWxmFzMTuKazoQ9sch6pM0p5oA==} + + component-event@0.2.1: + resolution: {integrity: sha512-wGA++isMqiDq1jPYeyv2as/Bt/u+3iLW0rEa+8NQ82jAv3TgqMiCM+B2SaBdn2DfLilLjjq736YcezihRYhfxw==} + + compress-commons@6.0.2: + resolution: {integrity: sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==} + engines: {node: '>= 14'} + + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@7.1.0: + resolution: {integrity: sha512-N4oog6YJWbR9kGyXvS7jEykLDXIE2C0ILYqNBZBp9iwiJpoCBWYsuAdW6PPFn6w06jjnC+3JstVvWHO4cZqvRg==} + engines: {node: '>=18'} + + connect-history-api-fallback@1.6.0: + resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==} + engines: {node: '>=0.8'} + + consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-changelog-conventionalcommits@7.0.2: + resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} + engines: {node: '>=16'} + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie-es@1.2.2: + resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} + + cookie-es@2.0.0: + resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + copy-anything@4.0.5: + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} + engines: {node: '>=18'} + + core-js-compat@3.47.0: + resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} + + core-js-pure@3.47.0: + resolution: {integrity: sha512-BcxeDbzUrRnXGYIVAGFtcGQVNpFcUhVjr6W7F8XktvQW2iJP9e66GP6xdKotCRFlrxBvNIBrhwKteRXqMV86Nw==} + + core-js@3.47.0: + resolution: {integrity: sha512-c3Q2VVkGAUyupsjRnaNX6u8Dq2vAdzm9iuPj5FW0fRxzlxgq9Q39MDq10IvmQSpLgHQNyQzQmOo6bgGHmH3NNg==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig-typescript-loader@6.2.0: + resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + + crc32-stream@6.0.0: + resolution: {integrity: sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==} + engines: {node: '>= 14'} + + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + + croner@9.1.0: + resolution: {integrity: sha512-p9nwwR4qyT5W996vBZhdvBCnMhicY5ytZkR4D1Xj0wuTDEiMnjwR57Q3RXYY/s0EpX6Ay3vgIcfaR+ewGHsi+g==} + engines: {node: '>=18.0'} + + cropperjs@1.6.2: + resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==} + + cross-env@7.0.3: + resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==} + engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'} + hasBin: true + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crossws@0.3.5: + resolution: {integrity: sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==} + + crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + + crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + + cspell-config-lib@8.19.4: + resolution: {integrity: sha512-LtFNZEWVrnpjiTNgEDsVN05UqhhJ1iA0HnTv4jsascPehlaUYVoyucgNbFeRs6UMaClJnqR0qT9lnPX+KO1OLg==} + engines: {node: '>=18'} + + cspell-dictionary@8.19.4: + resolution: {integrity: sha512-lr8uIm7Wub8ToRXO9f6f7in429P1Egm3I+Ps3ZGfWpwLTCUBnHvJdNF/kQqF7PL0Lw6acXcjVWFYT7l2Wdst2g==} + engines: {node: '>=18'} + + cspell-gitignore@8.19.4: + resolution: {integrity: sha512-KrViypPilNUHWZkMV0SM8P9EQVIyH8HvUqFscI7+cyzWnlglvzqDdV4N5f+Ax5mK+IqR6rTEX8JZbCwIWWV7og==} + engines: {node: '>=18'} + hasBin: true + + cspell-glob@8.19.4: + resolution: {integrity: sha512-042uDU+RjAz882w+DXKuYxI2rrgVPfRQDYvIQvUrY1hexH4sHbne78+OMlFjjzOCEAgyjnm1ktWUCCmh08pQUw==} + engines: {node: '>=18'} + + cspell-grammar@8.19.4: + resolution: {integrity: sha512-lzWgZYTu/L7DNOHjxuKf8H7DCXvraHMKxtFObf8bAzgT+aBmey5fW2LviXUkZ2Lb2R0qQY+TJ5VIGoEjNf55ow==} + engines: {node: '>=18'} + hasBin: true + + cspell-io@8.19.4: + resolution: {integrity: sha512-W48egJqZ2saEhPWf5ftyighvm4mztxEOi45ILsKgFikXcWFs0H0/hLwqVFeDurgELSzprr12b6dXsr67dV8amg==} + engines: {node: '>=18'} + + cspell-lib@8.19.4: + resolution: {integrity: sha512-NwfdCCYtIBNQuZcoMlMmL3HSv2olXNErMi/aOTI9BBAjvCHjhgX5hbHySMZ0NFNynnN+Mlbu5kooJ5asZeB3KA==} + engines: {node: '>=18'} + + cspell-trie-lib@8.19.4: + resolution: {integrity: sha512-yIPlmGSP3tT3j8Nmu+7CNpkPh/gBO2ovdnqNmZV+LNtQmVxqFd2fH7XvR1TKjQyctSH1ip0P5uIdJmzY1uhaYg==} + engines: {node: '>=18'} + + cspell@8.19.4: + resolution: {integrity: sha512-toaLrLj3usWY0Bvdi661zMmpKW2DVLAG3tcwkAv4JBTisdIRn15kN/qZDrhSieUEhVgJgZJDH4UKRiq29mIFxA==} + engines: {node: '>=18'} + hasBin: true + + css-blank-pseudo@7.0.1: + resolution: {integrity: sha512-jf+twWGDf6LDoXDUode+nc7ZlrqfaNphrBIBrcmeP3D8yw1uPaix1gCC8LUQUGQ6CycuK2opkbFFWFuq/a94ag==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + css-declaration-sorter@7.3.0: + resolution: {integrity: sha512-LQF6N/3vkAMYF4xoHLJfG718HRJh34Z8BnNhd6bosOMIVjMlhuZK5++oZa3uYAgrI5+7x2o27gUqTR2U/KjUOQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + + css-functions-list@3.2.3: + resolution: {integrity: sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==} + engines: {node: '>=12 || >=16'} + + css-has-pseudo@7.0.3: + resolution: {integrity: sha512-oG+vKuGyqe/xvEMoxAQrhi7uY16deJR3i7wwhBerVrGQKSqUC5GiOVxTpM9F9B9hw0J+eKeOWLH7E9gZ1Dr5rA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + css-prefers-color-scheme@10.0.0: + resolution: {integrity: sha512-VCtXZAWivRglTZditUfB4StnsWr6YVZ2PRtuxQLKTNRdtAf8tpzaVPE9zXIF3VaSc7O70iK/j1+NXxyQCqdPjQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + css-render@0.15.14: + resolution: {integrity: sha512-9nF4PdUle+5ta4W5SyZdLCCmFd37uVimSjg1evcTqKJCyvCEEj12WKzOSBNak6r4im4J4iYXKH1OWpUV5LBYFg==} + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-select@5.2.2: + resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + + 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.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + engines: {node: '>= 6'} + + cssdb@8.4.2: + resolution: {integrity: sha512-PzjkRkRUS+IHDJohtxkIczlxPPZqRo0nXplsYXOMBRPjcVRjj1W4DfvRgshUYTVuUigU7ptVYkFJQ7abUB0nyg==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@7.0.10: + resolution: {integrity: sha512-6ZBjW0Lf1K1Z+0OKUAUpEN62tSXmYChXWi2NAA0afxEVsj9a+MbcB1l5qel6BHJHmULai2fCGRthCeKSFbScpA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + cssnano-utils@5.0.1: + resolution: {integrity: sha512-ZIP71eQgG9JwjVZsTPSqhc6GHgEr53uJ7tK5///VfyWj6Xp2DBmixWHqJgPno+PqATzn48pL42ww9x5SSGmhZg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + cssnano@7.1.2: + resolution: {integrity: sha512-HYOPBsNvoiFeR1eghKD5C3ASm64v9YVyJB4Ivnl2gqKoQYvjjN/G0rztvKQq8OxocUtC6sjqY8jwYngIB4AByA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + 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.0.11: + resolution: {integrity: sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + cz-git@1.12.0: + resolution: {integrity: sha512-LaZ+8whPPUOo6Y0Zy4nIbf6JOleV3ejp41sT6N4RPKiKKA+ICWf4ueeIlxIO8b6JtdlDxRzHH/EcRji07nDxcg==} + engines: {node: '>=v12.20.0'} + + czg@1.12.0: + resolution: {integrity: sha512-LGltcoZ5m7vhe3WNw16QXqd5WurnHegx9V15MwZJtFAU2AVCYLCqDbwxPUgZOnAcdzzooq33ONcU148HOQsjdA==} + engines: {node: '>=v12.20.0'} + hasBin: true + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-flextree@2.1.2: + resolution: {integrity: sha512-gJiHrx5uTTHq44bjyIb3xpbmmdZcWLYPKeO9EPVOq8EylMFOiH2+9sWqKAiQ4DcFuOZTAxPOQyv0Rnmji/g15A==} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@1.1.9: + resolution: {integrity: sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + dataloader@1.4.0: + resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + + date-fns-tz@3.2.0: + resolution: {integrity: sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==} + peerDependencies: + date-fns: ^3.0.0 || ^4.0.0 + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + + db0@0.3.4: + resolution: {integrity: sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw==} + peerDependencies: + '@electric-sql/pglite': '*' + '@libsql/client': '*' + better-sqlite3: '*' + drizzle-orm: '*' + mysql2: '*' + sqlite3: '*' + peerDependenciesMeta: + '@electric-sql/pglite': + optional: true + '@libsql/client': + optional: true + better-sqlite3: + optional: true + drizzle-orm: + optional: true + mysql2: + optional: true + sqlite3: + optional: true + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} + engines: {node: '>=18'} + + default-browser@5.4.0: + resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} + engines: {node: '>=18'} + + default-passive-events@2.0.0: + resolution: {integrity: sha512-eMtt76GpDVngZQ3ocgvRcNCklUMwID1PaNbCNxfpDXuiOXttSh0HzBbda1HU9SIUsDc02vb7g9+3I5tlqe/qMQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@2.0.0: + resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} + engines: {node: '>=8'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + delegate@3.2.0: + resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==} + + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + + depcheck@1.4.7: + resolution: {integrity: sha512-1lklS/bV5chOxwNKA/2XUUk/hPORp8zihZsXflr8x0kLwmcZ9Y9BsS6Hs3ssvA+2wUVbG0U2Ciqvm1SokNjPkA==} + engines: {node: '>=10'} + hasBin: true + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + deps-regex@0.2.0: + resolution: {integrity: sha512-PwuBojGMQAYbWkMXOY9Pd/NWCDNHVH12pnS7WHqZkTSeMESe4hwnKKRp0yR87g37113x4JPbo/oIvXY+s/f56Q==} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + destr@2.0.5: + resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} + + detect-file@1.0.0: + resolution: {integrity: sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==} + engines: {node: '>=0.10.0'} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devalue@5.5.0: + resolution: {integrity: sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w==} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diagram-js-direct-editing@3.2.0: + resolution: {integrity: sha512-+pyxeQGBSdLiZX0/tmmsm2qZSvm9YtVzod5W3RMHSTR7VrkUMD6E7EX/W9JQv3ebxO7oIdqFmytmNDDpSHnYEw==} + peerDependencies: + diagram-js: '*' + + diagram-js@12.8.1: + resolution: {integrity: sha512-LF9BiwjbOPpZd0ez5VSlYRbdbEA59YQX43bWvNDp1rLMv0xwZ5yIg4oaYDK82nIQ0kH1tjvoQRpNevMTCgQVyw==} + + diagram-js@14.11.3: + resolution: {integrity: sha512-Seq9BHAXfzKS60L4v4Gvgvv72wOtvrfJQAyyPm9pntSZDMzjoodPSXnEUPud1G2zVCMGEUUW++s0reEdaWgkXA==} + + didi@10.2.2: + resolution: {integrity: sha512-l8NYkYFXV1izHI65EyT8EXOjUZtKmQkHLTT89cSP7HU5J/G7AOj0dXKtLc04EXYlga99PBY18IPjOeZ+c3DI4w==} + engines: {node: '>= 16'} + + didi@9.0.2: + resolution: {integrity: sha512-q2+aj+lnJcUweV7A9pdUrwFr4LHVmRPwTmQLtHPFz4aT7IBoryN6Iy+jmFku+oIzr5ebBkvtBCOb87+dJhb7bg==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + diff-sequences@27.5.1: + resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-align@1.12.4: + resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==} + + dom-scroll-into-view@2.0.1: + resolution: {integrity: sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + dom-walk@0.1.2: + resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==} + + dom-zindex@1.0.6: + resolution: {integrity: sha512-FKWIhiU96bi3xpP9ewRMgANsoVmMUBnMnmpCT6dPMZOunVYJQmJhSRruoI0XSPoHeIif3kyEuiHbFrOJwEJaEA==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domify@1.4.2: + resolution: {integrity: sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==} + + domify@2.0.0: + resolution: {integrity: sha512-rmvrrmWQPD/X1A/nPBfIVg4r05792QdG9Z4Prk6oQG0F9zBMDkr0GKAdds1wjb2dq1rTz/ywc4ZxpZbgz0tttg==} + engines: {node: '>=18'} + + dompurify@3.3.0: + resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dot-prop@10.1.0: + resolution: {integrity: sha512-MVUtAugQMOff5RnBy2d9N31iG0lNwg1qAoAOn7pOK5wf94WIaE3My2p3uwTQuvS2AcqchkcR3bHByjaM0mmi7Q==} + engines: {node: '>=20'} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dot-prop@9.0.0: + resolution: {integrity: sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==} + engines: {node: '>=18'} + + dotenv-expand@8.0.3: + resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==} + engines: {node: '>=12'} + + dotenv@16.0.3: + resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} + engines: {node: '>=12'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + engines: {node: '>=12'} + + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + echarts@6.0.0: + resolution: {integrity: sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ==} + + editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.262: + resolution: {integrity: sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==} + + element-plus@2.11.8: + resolution: {integrity: sha512-2wzSj2uubFU1f0t/gHkkE1d09mUgV18fSZX5excw3Ar6hyWcxph4E57U8dgYLDt7HwkKYv1BiqPyBdy0WqWlOA==} + peerDependencies: + vue: ^3.5.24 + + emoji-regex-xs@1.0.0: + resolution: {integrity: sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg==} + + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + encoding-sniffer@0.2.1: + resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + env-paths@3.0.0: + resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + + errx@0.1.0: + resolution: {integrity: sha512-fZmsRiDNv07K6s2KkKFTiD2aIvECa7++PKyD5NC32tpRw46qZA3sOz+aM+/V9V0GDHxVTKLziveV4JhzBHDp9Q==} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + es-toolkit@1.42.0: + resolution: {integrity: sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA==} + + esbuild@0.25.3: + resolution: {integrity: sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-goat@4.0.0: + resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} + engines: {node: '>=12'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + + eslint-compat-utils@0.5.1: + resolution: {integrity: sha512-3z3vFexKIEnjHE3zCMRo6fn/e44U7T1khUjg+Hp0ZQMCigh28rALD0nPFBcGZuiLC5rLZa2ubQHDRln09JfU2Q==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-compat-utils@0.6.5: + resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==} + engines: {node: '>=12'} + peerDependencies: + eslint: '>=6.0.0' + + eslint-config-turbo@2.6.1: + resolution: {integrity: sha512-iLTBigkgIANO89UFz/S4hGl0Ay+x/zO0lJVV9fo2F0r7n8USJxGtpyJ8QWPVBTubcanFRHbjk+e5mWatpvV0fw==} + peerDependencies: + eslint: '>6.6.0' + turbo: '>2.0.0' + + eslint-import-context@0.1.9: + resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + peerDependencies: + unrs-resolver: ^1.0.0 + peerDependenciesMeta: + unrs-resolver: + optional: true + + eslint-json-compat-utils@0.2.1: + resolution: {integrity: sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg==} + engines: {node: '>=12'} + peerDependencies: + '@eslint/json': '*' + eslint: '*' + jsonc-eslint-parser: ^2.4.0 + peerDependenciesMeta: + '@eslint/json': + optional: true + + eslint-plugin-command@3.3.1: + resolution: {integrity: sha512-fBVTXQ2y48TVLT0+4A6PFINp7GcdIailHAXbvPBixE7x+YpYnNQhFZxTdvnb+aWk+COgNebQKen/7m4dmgyWAw==} + peerDependencies: + eslint: '*' + + eslint-plugin-es-x@7.8.0: + resolution: {integrity: sha512-7Ds8+wAAoV3T+LAKeu39Y5BzXCrGKrcISfgKEqTS4BDN8SFEDQd0S43jiQ8vIa3wUKD07qitZdfzlenSi8/0qQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '>=8' + + eslint-plugin-eslint-comments@3.2.0: + resolution: {integrity: sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==} + engines: {node: '>=6.5.0'} + peerDependencies: + eslint: '>=4.19.1' + + eslint-plugin-import-x@4.16.1: + resolution: {integrity: sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/utils': ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + eslint-import-resolver-node: '*' + peerDependenciesMeta: + '@typescript-eslint/utils': + optional: true + eslint-import-resolver-node: + optional: true + + eslint-plugin-jsdoc@61.4.1: + resolution: {integrity: sha512-3c1QW/bV25sJ1MsIvsvW+EtLtN6yZMduw7LVQNVt72y2/5BbV5Pg5b//TE5T48LRUxoEQGaZJejCmcj3wCxBzw==} + engines: {node: '>=20.11.0'} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-plugin-jsonc@2.21.0: + resolution: {integrity: sha512-HttlxdNG5ly3YjP1cFMP62R4qKLxJURfBZo2gnMY+yQojZxkLyOpY1H1KRTKBmvQeSG9pIpSGEhDjE17vvYosg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + eslint-plugin-n@17.23.1: + resolution: {integrity: sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: '>=8.23.0' + + eslint-plugin-no-only-tests@3.3.0: + resolution: {integrity: sha512-brcKcxGnISN2CcVhXJ/kEQlNa0MEfGRtwKtWA16SkqXHKitaKIMrfemJKLKX1YqDU5C/5JY3PvZXd5jEW04e0Q==} + engines: {node: '>=5.0.0'} + + eslint-plugin-perfectionist@4.15.1: + resolution: {integrity: sha512-MHF0cBoOG0XyBf7G0EAFCuJJu4I18wy0zAoT1OHfx2o6EOx1EFTIzr2HGeuZa1kDcusoX0xJ9V7oZmaeFd773Q==} + engines: {node: ^18.0.0 || >=20.0.0} + peerDependencies: + eslint: '>=8.45.0' + + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-regexp@2.10.0: + resolution: {integrity: sha512-ovzQT8ESVn5oOe5a7gIDPD5v9bCSjIFJu57sVPDqgPRXicQzOnYfFN21WoQBQF18vrhT5o7UMKFwJQVVjyJ0ng==} + engines: {node: ^18 || >=20} + peerDependencies: + eslint: '>=8.44.0' + + eslint-plugin-turbo@2.6.1: + resolution: {integrity: sha512-nxwJD6ReZTxEmq/ZNJXI/Mfa3aXctTpQpJrQZESDC1bxjXXcz4i040P7wG6RsRRsQ+9eYB9D+KdCqERpwNZGzw==} + peerDependencies: + eslint: '>6.6.0' + turbo: '>2.0.0' + + eslint-plugin-unicorn@62.0.0: + resolution: {integrity: sha512-HIlIkGLkvf29YEiS/ImuDZQbP12gWyx5i3C6XrRxMvVdqMroCI9qoVYCoIl17ChN+U89pn9sVwLxhIWj5nEc7g==} + engines: {node: ^20.10.0 || >=21.0.0} + peerDependencies: + eslint: '>=9.38.0' + + eslint-plugin-unused-imports@4.3.0: + resolution: {integrity: sha512-ZFBmXMGBYfHttdRtOG9nFFpmUvMtbHSjsKrS20vdWdbfiVYsO3yA2SGYy9i9XmZJDfMGBflZGBCm70SEnFQtOA==} + peerDependencies: + '@typescript-eslint/eslint-plugin': ^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0 + eslint: ^9.0.0 || ^8.0.0 + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + + eslint-plugin-vitest@0.5.4: + resolution: {integrity: sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==} + engines: {node: ^18.0.0 || >= 20.0.0} + peerDependencies: + '@typescript-eslint/eslint-plugin': '*' + eslint: ^8.57.0 || ^9.0.0 + vitest: '*' + peerDependenciesMeta: + '@typescript-eslint/eslint-plugin': + optional: true + vitest: + optional: true + + eslint-plugin-vue@10.6.1: + resolution: {integrity: sha512-OMvDAFbewocYrJamF1EoSWoT4xa7/QRb/yYouEZMiroTE+WRmFUreR+kAFQHqM45W3kg5oljVfUYfH9HEwX1Bg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@stylistic/eslint-plugin': ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + '@typescript-eslint/parser': ^7.0.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + vue-eslint-parser: ^10.0.0 + peerDependenciesMeta: + '@stylistic/eslint-plugin': + optional: true + '@typescript-eslint/parser': + optional: true + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.39.1: + resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: ^2.6.1 + peerDependenciesMeta: + jiti: + optional: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrap@2.2.0: + resolution: {integrity: sha512-WBmtxe7R9C5mvL4n2le8nMUe4mD5V9oiK2vJpQ9I3y20ENPUomPcphBXE8D1x/Bm84oN1V+lOfgXxtqmxTp3Xg==} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@1.0.1: + resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events-universal@1.0.1: + resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + evtd@0.2.4: + resolution: {integrity: sha512-qaeGN5bx63s/AXgQo8gj6fBkxge+OoLddLniox5qtLAEY5HSnuSlISXVPxnSae1dWblvTh4/HoMIB+mbMsvZzw==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + execa@9.6.0: + resolution: {integrity: sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==} + engines: {node: ^18.19.0 || >=20.5.0} + + expand-tilde@2.0.2: + resolution: {integrity: sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==} + engines: {node: '>=0.10.0'} + + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-equals@5.3.3: + resolution: {integrity: sha512-/boTcHZeIAQ2r/tL11voclBHDeP9WPxLt+tyAbVSyyXuUFyh0Tne7gJZTqGbxnvj79TjLdCXLOY7UIPhyG5MTw==} + engines: {node: '>=6.0.0'} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-string-compare@3.0.0: + resolution: {integrity: sha512-PY66/8HelapGo5nqMN17ZTKqJj1nppuS1OoC9Y0aI2jsUDlZDEYhMODTpb68wVCq+xMbaEbPGXRd7qutHzkRXA==} + engines: {node: ^14.13.1 || >=16.0.0} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fast-xml-parser@4.5.3: + resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} + hasBin: true + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + feelers@1.4.0: + resolution: {integrity: sha512-CGa/7ILuqoqTaeYeoKsg/4tzu2es9sEEJTmSjdu0lousZBw4V9gcYhHYFNmbrSrKmbAVfOzj6/DsymGJWFIOeg==} + + feelin@3.2.0: + resolution: {integrity: sha512-GFDbHsTYk7YXO1tyw1dOjb7IODeAZvNIosdGZThUwPx5XcD/XhO0hnPZXsIbAzSsIdrgGlTEEdby9fZ2gixysA==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-entry-cache@11.1.1: + resolution: {integrity: sha512-TPVFSDE7q91Dlk1xpFLvFllf8r0HyOMOlnWy7Z2HBku5H3KhIeOGInexrIeg2D64DosVB/JXkrrk6N/7Wriq4A==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + file-entry-cache@9.1.0: + resolution: {integrity: sha512-/pqPFG+FdxWQj+/WSuzXSDaNzxgTLr/OrR1QuqfEZzDakpdYE70PwUxL7BPUa8hpjbvY1+qvCl8k+8Tq34xJgg==} + engines: {node: '>=18'} + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + filelist@1.0.4: + resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + findup-sync@5.0.0: + resolution: {integrity: sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==} + engines: {node: '>= 10.13.0'} + + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat-cache@5.0.0: + resolution: {integrity: sha512-JrqFmyUl2PnPi1OvLyTVHnQvwQ0S+e6lGSwu8OkAZlSaNIZciTY2H/cOOROxsBA1m/LZNHDsqAgDZt6akWcjsQ==} + engines: {node: '>=18'} + + flat-cache@6.1.19: + resolution: {integrity: sha512-l/K33newPTZMTGAnnzaiqSl6NnH7Namh8jBNjrgjprWxGmZUuxx/sJNIRaijOh3n7q7ESbhNZC+pvVZMFdeU4A==} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + focus-trap@7.6.6: + resolution: {integrity: sha512-v/Z8bvMCajtx4mEXmOo7QEsIzlIOqRXTIwgUfsFOF9gEsespdbD0AkPIka1bSXZ8Y8oZ+2IVDQZePkTfEHZl7Q==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + formdata-node@6.0.3: + resolution: {integrity: sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==} + engines: {node: '>= 18'} + + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} + + framesync@6.1.2: + resolution: {integrity: sha512-jBTqhX6KaQVDyus8muwZbBeGGP0XgujBRbQ7gM7BRdS3CadCZIHiawyzYLnafYcvZIh5j8WE7cxZKFn7dXhu9g==} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + engines: {node: '>=14.14'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + 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==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensequence@7.0.0: + resolution: {integrity: sha512-47Frx13aZh01afHJTB3zTtKIlFI6vWY+MYCN9Qpew6i52rfKjnhCF/l1YlC8UmEMvvntZZ6z4PiCcmyuedR2aQ==} + engines: {node: '>=18'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.4.0: + resolution: {integrity: sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-own-enumerable-property-symbols@3.0.2: + resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==} + + get-port-please@3.2.0: + resolution: {integrity: sha512-I9QVvBw5U/hw3RmWpYKRumUeaDgxTPd401x364rLmWBJcOQ753eov1eTgzDqRG9bqFIfDc7gfzcQEWrUri3o1A==} + + get-port@7.1.0: + resolution: {integrity: sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==} + engines: {node: '>=16'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + giget@2.0.0: + resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==} + hasBin: true + + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + hasBin: true + + glob@11.1.0: + resolution: {integrity: sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==} + engines: {node: 20 || >=22} + hasBin: true + + glob@13.0.0: + resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} + engines: {node: 20 || >=22} + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + global-modules@1.0.0: + resolution: {integrity: sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==} + engines: {node: '>=0.10.0'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@1.0.2: + resolution: {integrity: sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==} + engines: {node: '>=0.10.0'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + global@4.4.0: + resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@15.15.0: + resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} + engines: {node: '>=18'} + + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@14.1.0: + resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} + engines: {node: '>=18'} + + globby@15.0.0: + resolution: {integrity: sha512-oB4vkQGqlMl682wL1IlWd02tXCbquGWM4voPEI85QmNKCaw8zGTm1f1rubFgkg3Eli2PtKlFgrnmUqasbQWlkw==} + engines: {node: '>=20'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + + good-listener@1.2.2: + resolution: {integrity: sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graph-cycles@3.0.0: + resolution: {integrity: sha512-9qxd8BQK0tZJ3cRUW0Yn2q6P/9h+bgz/4UYuscpdxPz6851NNBA1IwoYDRHDpKzCqmr1UHlGHcp5+0WzpY9F7A==} + engines: {node: ^14.13.1 || >=16.0.0} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + gray-matter@4.0.3: + resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==} + engines: {node: '>=6.0'} + + gzip-size@7.0.0: + resolution: {integrity: sha512-O1Ld7Dr+nqPnmGpdhzLmMTQ4vAsD+rHwMm1NLUmoUFFymBOMKxCCrtDxqdBRYXdeEPEi3SyoR4TizJLQrnKBNA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + h3@1.15.4: + resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} + + hammerjs@2.0.8: + resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==} + engines: {node: '>=0.8.0'} + + happy-dom@17.6.3: + resolution: {integrity: sha512-UVIHeVhxmxedbWPCfgS55Jg2rDfwf2BCKeylcPSqazLz5w3Kri7Q4xdBJubsr/+VUzFLh0VjIvh13RaDA2/Xug==} + engines: {node: '>=20.0.0'} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + 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'} + + hashery@1.2.0: + resolution: {integrity: sha512-43XJKpwle72Ik5Zpam7MuzRWyNdwwdf6XHlh8wCj2PggvWf+v/Dm5B0dxGZOmddidgeO6Ofu9As/o231Ti/9PA==} + engines: {node: '>=20'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + 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==} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + hey-listen@1.0.8: + resolution: {integrity: sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==} + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + homedir-polyfill@1.0.3: + resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} + engines: {node: '>=0.10.0'} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + hookified@1.13.0: + resolution: {integrity: sha512-6sPYUY8olshgM/1LDNW4QZQN0IqgKhtl/1C8koNZBJrKLBk3AZl6chQtNwpNztvfiApHMEwMHek5rv993PRbWw==} + + htm@3.1.1: + resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==} + + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + + html-minifier-terser@6.1.0: + resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} + engines: {node: '>=12'} + hasBin: true + + html-minifier-terser@7.2.0: + resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + html-void-elements@3.0.0: + resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + + http-shutdown@1.2.2: + resolution: {integrity: sha512-S9wWkJ/VSY9/k4qcjG318bqJNruzE4HySUhFYknwmu6LBP97KLLfwNf+n4V1BHurvFNkSKLFnK/RsuUnRTf9Vw==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + httpxy@0.1.7: + resolution: {integrity: sha512-pXNx8gnANKAndgga5ahefxc++tJvNL87CXoRwxn1cJE2ZkWEojF3tNfQIEhZX/vfpt+wzeAzpUI4qkediX1MLQ==} + + human-id@4.1.2: + resolution: {integrity: sha512-v/J+4Z/1eIJovEBdlV5TYj1IR+ZiohcYGRY+qN/oC9dAfKzVT023N/Bgw37hrKCoVRBvk3bqyzpr2PP5YeTMSg==} + hasBin: true + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + human-signals@8.0.1: + resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} + engines: {node: '>=18.18.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + + ids@1.0.5: + resolution: {integrity: sha512-XQ0yom/4KWTL29sLG+tyuycy7UmeaM/79GRtSJq6IG9cJGIPeBz5kwDCguie3TwxaMNIc3WtPi0cTa1XYHicpw==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immutable@5.1.4: + resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + individual@2.0.0: + resolution: {integrity: sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g==} + + inherits-browser@0.1.0: + resolution: {integrity: sha512-CJHHvW3jQ6q7lzsXPpapLdMx5hDpSF3FSh45pwsj6bKxJJ8Nl8v43i5yXnr3BdfOimGHKyniewQtnAIp3vyJJw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + ioredis@5.8.2: + resolution: {integrity: sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==} + engines: {node: '>=12.22.0'} + + iron-webcrypto@1.2.1: + resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-buffer@2.0.5: + resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} + engines: {node: '>=4'} + + is-builtin-module@5.0.0: + resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} + engines: {node: '>=18.20'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-ci@4.1.0: + resolution: {integrity: sha512-Ab9bQDQ11lWootZUI5qxgN2ZXwxNI5hTwnsvOc1wyxQ7zQ8OkEDw79mI0+9jI3x432NfwbVRru+3noJfXF6lSQ==} + hasBin: true + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-docker@2.2.1: + resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} + engines: {node: '>=8'} + hasBin: true + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + + is-function@1.0.2: + resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-in-ci@1.0.0: + resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} + engines: {node: '>=18'} + hasBin: true + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-installed-globally@1.0.0: + resolution: {integrity: sha512-K55T22lfpQ63N4KEN57jZUAaAYqYHEe8veb/TycJRk9DdSCLLcovXz/mL6mOnhQaZsQGwPhuFopdQIlqGSEjiQ==} + engines: {node: '>=18'} + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-npm@6.1.0: + resolution: {integrity: sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@1.0.1: + resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==} + engines: {node: '>=0.10.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-plain-object@3.0.1: + resolution: {integrity: sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-regexp@1.0.0: + resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} + engines: {node: '>=0.10.0'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + + is-what@5.5.0: + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} + engines: {node: '>=18'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@2.2.0: + resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} + engines: {node: '>=8'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + is64bit@2.0.0: + resolution: {integrity: sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==} + engines: {node: '>=18'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jackspeak@4.1.1: + resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} + engines: {node: 20 || >=22} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@3.14.2: + resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==} + hasBin: true + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdoc-type-pratt-parser@4.1.0: + resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==} + engines: {node: '>=12.0.0'} + + jsdoc-type-pratt-parser@4.8.0: + resolution: {integrity: sha512-iZ8Bdb84lWRuGHamRXFyML07r21pcwBrLkHEuHgEY5UbCouBwv7ECknDRKzsQIXMiqpPymqtIf8TC/shYKB5rw==} + engines: {node: '>=12.0.0'} + + jsdoc-type-pratt-parser@6.10.0: + resolution: {integrity: sha512-+LexoTRyYui5iOhJGn13N9ZazL23nAHGkXsa1p/C8yeq79WRfLBag6ZZ0FQG2aRoc9yfo59JT9EYCQonOkHKkQ==} + engines: {node: '>=20.0.0'} + + jsencrypt@3.5.4: + resolution: {integrity: sha512-kNjfYEMNASxrDGsmcSQh/rUTmcoRfSUkxnAz+MMywM8jtGu+fFEZ3nJjHM58zscVnwR0fYmG9sGkTDjqUdpiwA==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema@0.4.0: + resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonc-eslint-parser@2.4.1: + resolution: {integrity: sha512-uuPNLJkKN8NXAlZlQ6kmUF9qO+T6Kyd7oV4+/7yy8Jz6+MZNyhPq8EdLpdfnPVzUC8qSf1b4j1azKaGnFsjmsw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + jsonpointer@5.0.1: + resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} + engines: {node: '>=0.10.0'} + + katex@0.16.25: + resolution: {integrity: sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==} + hasBin: true + + keycode@2.2.1: + resolution: {integrity: sha512-Rdgz9Hl9Iv4QKi8b0OlCRQEzp4AgVxyCtz5S/+VIHezDmrDhkp2N2TqBWOLz0/gbeREXOOiI9/4b8BY9uw2vFg==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + keyv@5.5.4: + resolution: {integrity: sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + knitwork@1.3.0: + resolution: {integrity: sha512-4LqMNoONzR43B1W0ek0fhXMsDNW/zxa1NdFAVMY+k28pgZLovR4G3PB5MrpTxCy1QaZCqNoiaKPr5w5qZHfSNw==} + + known-css-properties@0.36.0: + resolution: {integrity: sha512-A+9jP+IUmuQsNdsLdcg6Yt7voiMF/D4K83ew0OpJtpu+l34ef7LaohWV0Rc6KNvzw6ZDizkqfyB5JznZnzuKQA==} + + known-css-properties@0.37.0: + resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + ky@1.14.0: + resolution: {integrity: sha512-Rczb6FMM6JT0lvrOlP5WUOCB7s9XKxzwgErzhKlKde1bEV90FXplV1o87fpt4PU/asJFiqjYJxAJyzJhcrxOsQ==} + engines: {node: '>=18'} + + latest-version@9.0.0: + resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} + engines: {node: '>=18'} + + lazystream@1.0.1: + resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} + engines: {node: '>= 0.6.3'} + + lefthook-darwin-arm64@1.13.6: + resolution: {integrity: sha512-m6Lb77VGc84/Qo21Lhq576pEvcgFCnvloEiP02HbAHcIXD0RTLy9u2yAInrixqZeaz13HYtdDaI7OBYAAdVt8A==} + cpu: [arm64] + os: [darwin] + + lefthook-darwin-x64@1.13.6: + resolution: {integrity: sha512-CoRpdzanu9RK3oXR1vbEJA5LN7iB+c7hP+sONeQJzoOXuq4PNKVtEaN84Gl1BrVtCNLHWFAvCQaZPPiiXSy8qg==} + cpu: [x64] + os: [darwin] + + lefthook-freebsd-arm64@1.13.6: + resolution: {integrity: sha512-X4A7yfvAJ68CoHTqP+XvQzdKbyd935sYy0bQT6Ajz7FL1g7hFiro8dqHSdPdkwei9hs8hXeV7feyTXbYmfjKQQ==} + cpu: [arm64] + os: [freebsd] + + lefthook-freebsd-x64@1.13.6: + resolution: {integrity: sha512-ai2m+Sj2kGdY46USfBrCqLKe9GYhzeq01nuyDYCrdGISePeZ6udOlD1k3lQKJGQCHb0bRz4St0r5nKDSh1x/2A==} + cpu: [x64] + os: [freebsd] + + lefthook-linux-arm64@1.13.6: + resolution: {integrity: sha512-cbo4Wtdq81GTABvikLORJsAWPKAJXE8Q5RXsICFUVznh5PHigS9dFW/4NXywo0+jfFPCT6SYds2zz4tCx6DA0Q==} + cpu: [arm64] + os: [linux] + + lefthook-linux-x64@1.13.6: + resolution: {integrity: sha512-uJl9vjCIIBTBvMZkemxCE+3zrZHlRO7Oc+nZJ+o9Oea3fu+W82jwX7a7clw8jqNfaeBS+8+ZEQgiMHWCloTsGw==} + cpu: [x64] + os: [linux] + + lefthook-openbsd-arm64@1.13.6: + resolution: {integrity: sha512-7r153dxrNRQ9ytRs2PmGKKkYdvZYFPre7My7XToSTiRu5jNCq++++eAKVkoyWPduk97dGIA+YWiEr5Noe0TK2A==} + cpu: [arm64] + os: [openbsd] + + lefthook-openbsd-x64@1.13.6: + resolution: {integrity: sha512-Z+UhLlcg1xrXOidK3aLLpgH7KrwNyWYE3yb7ITYnzJSEV8qXnePtVu8lvMBHs/myzemjBzeIr/U/+ipjclR06g==} + cpu: [x64] + os: [openbsd] + + lefthook-windows-arm64@1.13.6: + resolution: {integrity: sha512-Uxef6qoDxCmUNQwk8eBvddYJKSBFglfwAY9Y9+NnnmiHpWTjjYiObE9gT2mvGVpEgZRJVAatBXc+Ha5oDD/OgQ==} + cpu: [arm64] + os: [win32] + + lefthook-windows-x64@1.13.6: + resolution: {integrity: sha512-mOZoM3FQh3o08M8PQ/b3IYuL5oo36D9ehczIw1dAgp1Ly+Tr4fJ96A+4SEJrQuYeRD4mex9bR7Ps56I73sBSZA==} + cpu: [x64] + os: [win32] + + lefthook@1.13.6: + resolution: {integrity: sha512-ojj4/4IJ29Xn4drd5emqVgilegAPN3Kf0FQM2p/9+lwSTpU+SZ1v4Ig++NF+9MOa99UKY8bElmVrLhnUUNFh5g==} + hasBin: true + + less@4.4.2: + resolution: {integrity: sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==} + engines: {node: '>=14'} + hasBin: true + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lezer-feel@1.9.0: + resolution: {integrity: sha512-x8z6pCih3I3BOq3kBbhw6VUOU9Sg61PBJ1nigTgDl1yM5f0OPzEjK7GRJXutrSJDiUK8zwgqBvUJFlSfGLNZUg==} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + + listhen@1.9.0: + resolution: {integrity: sha512-I8oW2+QL5KJo8zXNWX046M134WchxsXC7SawLPvRQpogCbkyQIaFxPE89A2HiwR7vAK2Dm2ERBAmyjTYGYEpBg==} + hasBin: true + + listr2@8.3.3: + resolution: {integrity: sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ==} + engines: {node: '>=18.0.0'} + + local-pkg@1.1.2: + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} + engines: {node: '>=14'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + locko@1.1.0: + resolution: {integrity: sha512-pYB2dzRY93fJkg2RIl41AMNgTQftEjyTK9vlPrGOJvuGQsOjb267VJBw15BjiN3RBd1oBoKkOu9E2dRdFKIfAA==} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash-unified@1.0.3: + resolution: {integrity: sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==} + peerDependencies: + '@types/lodash-es': '*' + lodash: '*' + lodash-es: '*' + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.defaults@4.2.0: + resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} + + lodash.isarguments@3.1.0: + resolution: {integrity: sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lucide-vue-next@0.553.0: + resolution: {integrity: sha512-0tg9XT+VCElTT+7EXXbBRhWe1nU7Doa32Xv/dHP5/LCleFVgV6cAqziM3C7AetqmsYIsfAtNwRYdtvs4Ds7aUg==} + peerDependencies: + vue: ^3.5.24 + + luxon@3.7.2: + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} + engines: {node: '>=12'} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + m3u8-parser@4.8.0: + resolution: {integrity: sha512-UqA2a/Pw3liR6Df3gwxrqghCP17OpPlQj6RBPLYygf/ZSQ4MoSgvdvhvt35qV+3NaaA0FSZx93Ix+2brT1U7cA==} + + magic-string@0.25.9: + resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + mark.js@8.11.1: + resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + + markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} + hasBin: true + + marked@17.0.1: + resolution: {integrity: sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg==} + engines: {node: '>= 20'} + hasBin: true + + markmap-common@0.16.0: + resolution: {integrity: sha512-q3nlNDMKuWXTm3VwZFY9V5zteL/+iBLZanUK5vS+e26bUbzTSG5VtAzsyJbmgJm1WhwmIIAxbXEnp6JdvtTduA==} + + markmap-html-parser@0.16.1: + resolution: {integrity: sha512-/Mgm4g1qMQ8uEOz8h8K+jPspdgjfw29NqmfTLZSt8yG+vW7fWWduPjGRFc5axAZxCzP7PTzZLEuOxAqOwEg8Bg==} + peerDependencies: + markmap-common: '*' + + markmap-lib@0.16.1: + resolution: {integrity: sha512-jD8VsB67m677IRehGSwwVJDlC6PS+xzDKsJOwdvjZ+ndfXrHa1lyqfvR6mIwvGGUIciF86YEITSKL9hQTHE4Rw==} + peerDependencies: + markmap-common: '*' + + markmap-toolbar@0.17.2: + resolution: {integrity: sha512-WQ05P2xvQmZT0ybRUE0uRzrs30aXlJ6/yEUsA6A9nYEwm8T9jSwBxIM/5zYlkH/XzUcsRRxtCa4k1IWR74gkpQ==} + peerDependencies: + markmap-common: '*' + + markmap-view@0.16.0: + resolution: {integrity: sha512-JOiSEThs8B4bAP9E6rcCWOz2SsMwCBFaR76wLARRVb04C/qLiLmvrm675kNPq4lRBAwtugHCYvjG0otpSlB4Cw==} + peerDependencies: + markmap-common: '*' + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + + 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.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + + mdn-data@2.25.0: + resolution: {integrity: sha512-T2LPsjgUE/tgMmRXREVmwsux89DwWfNjiynOeXuLd2mX6jphGQ2YE3Ukz7LQ2VOFKiVZU/Ee1GqzHiipZCjymw==} + + mdurl@2.0.0: + resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} + + medium-zoom@1.1.0: + resolution: {integrity: sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==} + + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + 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==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + + mime@4.1.0: + resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} + engines: {node: '>=16'} + hasBin: true + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + min-dash@4.2.3: + resolution: {integrity: sha512-VLMYQI5+FcD9Ad24VcB08uA83B07OhueAlZ88jBK6PyupTvEJwllTMUqMy0wPGYs7pZUEtEEMWdHB63m3LtEcg==} + + min-document@2.19.2: + resolution: {integrity: sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==} + + min-dom@4.2.1: + resolution: {integrity: sha512-TMoL8SEEIhUWYgkj7XMSgxmwSyGI+4fP2KFFGnN3FbHfbGHVdsLYSz8LoIsgPhz4dWRmLvxWWSMgzZMJW5sZuA==} + + min-dom@5.1.1: + resolution: {integrity: sha512-GaKUlguMAofd3OJsB0OkP17i5kucKqErgVCJxPawO9l5NwIPnr28SAr99zzlzMCWWljISBYrnZVWdE2Q92YGFQ==} + + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@7.4.6: + resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} + engines: {node: '>=10'} + + minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minisearch@7.2.0: + resolution: {integrity: sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg==} + + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + + mkdist@2.4.1: + resolution: {integrity: sha512-Ezk0gi04GJBkqMfsksICU5Rjoemc4biIekwgrONWVPor2EO/N9nBgN6MZXAf7Yw4mDDhrNyKbdETaHNevfumKg==} + hasBin: true + peerDependencies: + sass: ^1.92.1 + typescript: '>=5.9.2' + vue: ^3.5.24 + vue-sfc-transformer: ^0.1.1 + vue-tsc: ^1.8.27 || ^2.0.21 || ^3.0.0 + peerDependenciesMeta: + sass: + optional: true + typescript: + optional: true + vue: + optional: true + vue-sfc-transformer: + optional: true + vue-tsc: + optional: true + + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + + moddle-xml@10.1.0: + resolution: {integrity: sha512-erWckwLt+dYskewKXJso9u+aAZ5172lOiYxSOqKCPTy7L/xmqH1PoeoA7eVC7oJTt3PqF5TkZzUmbjGH6soQBg==} + + moddle@6.2.3: + resolution: {integrity: sha512-bLVN+ZHL3aKnhxc19XtjUfvdJsS3EsiEJC7bT6YPD11qYmTzvsxrGgyYz1Ouof7TZuGw0lDJ1OLmEnxcpQWk3Q==} + + mpd-parser@0.22.1: + resolution: {integrity: sha512-fwBebvpyPUU8bOzvhX0VQZgSohncbgYwUyJJoTSNpmy7ccD2ryiCvM7oRkn/xQH5cv73/xU7rJSNCLjdGFor0Q==} + hasBin: true + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + 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==} + + multimatch@5.0.0: + resolution: {integrity: sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==} + engines: {node: '>=10'} + + mux.js@6.0.1: + resolution: {integrity: sha512-22CHb59rH8pWGcPGW5Og7JngJ9s+z4XuSlYvnxhLuc58cA1WqGDQPzuG8I+sPm1/p0CdgpzVTaKW408k5DNn8w==} + engines: {node: '>=8', npm: '>=5'} + hasBin: true + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + naive-ui@2.43.2: + resolution: {integrity: sha512-YlLMnGrwGTOc+zMj90sG3ubaH5/7czsgLgGcjTLA981IUaz8r6t4WIujNt8r9PNr+dqv6XNEr0vxkARgPPjfBQ==} + peerDependencies: + vue: ^3.5.24 + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.1.6: + resolution: {integrity: sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==} + engines: {node: ^18 || >=20} + hasBin: true + + nanopop@2.4.2: + resolution: {integrity: sha512-NzOgmMQ+elxxHeIha+OG/Pv3Oc3p4RU2aBhwWwAqDpXrdTbtRylbRLQztLy8dMMwfl6pclznBdfUhccEn9ZIzw==} + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + natural-orderby@5.0.0: + resolution: {integrity: sha512-kKHJhxwpR/Okycz4HhQKKlhWe4ASEfPgkSWNmKFHd7+ezuQlxkA5cM3+XkBPvm1gmHen3w53qsYAv+8GwRrBlg==} + engines: {node: '>=18'} + + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + + nitropack@2.12.9: + resolution: {integrity: sha512-t6qqNBn2UDGMWogQuORjbL2UPevB8PvIPsPHmqvWpeGOlPr4P8Oc5oA8t3wFwGmaolM2M/s2SwT23nx9yARmOg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + xml2js: ^0.6.2 + peerDependenciesMeta: + xml2js: + optional: true + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-cleanup@2.1.2: + resolution: {integrity: sha512-qN8v/s2PAJwGUtr1/hYTpNKlD6Y9rc4p8KSmJXyGdYGZsDGKXrGThikLFP9OCHFeLeEpQzPwiAtdIvBLqm//Hw==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch-cache@5.1.0: + resolution: {integrity: sha512-4j3rRHNGIKGX7VzXSrBT0bh7+wFuyJv1DxCfCLDHsnDahJWoD9lXe3BzL3BJg/GEIJiM7KIvqVs3byW1GFtRsQ==} + engines: {node: '>=18.19.0'} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + node-forge@1.3.2: + resolution: {integrity: sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==} + engines: {node: '>= 6.13.0'} + + node-gyp-build@4.8.4: + resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} + hasBin: true + + node-html-parser@5.4.2: + resolution: {integrity: sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==} + + node-mock-http@1.0.3: + resolution: {integrity: sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==} + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-wheel-es@1.2.0: + resolution: {integrity: sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + npm-run-path@6.0.0: + resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} + engines: {node: '>=18'} + + npm2url@0.2.4: + resolution: {integrity: sha512-arzGp/hQz0Ey+ZGhF64XVH7Xqwd+1Q/po5uGiBbzph8ebX6T0uvt3N7c1nBHQNsQVykQgHhqoRTX7JFcHecGuw==} + + 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.2: + resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-deep-merge@2.0.0: + resolution: {integrity: sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object-refs@0.3.0: + resolution: {integrity: sha512-eP0ywuoWOaDoiake/6kTJlPJhs+k0qNm4nYRzXLNHj6vh+5M3i9R1epJTdxIPGlhWc4fNRQ7a6XJNCX+/L4FOQ==} + + object-refs@0.4.0: + resolution: {integrity: sha512-6kJqKWryKZmtte6QYvouas0/EIJKPI1/MMIuRsiBlNuhIMfqYTggzX2F1AJ2+cDs288xyi9GL7FyasHINR98BQ==} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} + + ohash@2.0.11: + resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + oniguruma-to-es@3.1.1: + resolution: {integrity: sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ==} + + open@10.2.0: + resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} + engines: {node: '>=18'} + + open@8.4.2: + resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} + engines: {node: '>=12'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} + engines: {node: '>=18'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-map@7.0.4: + resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} + engines: {node: '>=18'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + package-json@10.0.1: + resolution: {integrity: sha512-ua1L4OgXSBdsu1FPb7F3tYH0F48a6kxvod4pLUlGY9COeJAJQNX/sNH2IiEmsxw7lqYiAwrdHMjz1FctOsyDQg==} + engines: {node: '>=18'} + + package-manager-detector@0.2.11: + resolution: {integrity: sha512-BEnLolu+yuz22S56CU1SUKq3XC3PkwD5wv4ikR4MfGvnRVcmzXR9DwSlW2fEamyTPyXHomBJRzgapeuBvRNzJQ==} + + package-manager-detector@1.5.0: + resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==} + + pako@2.1.0: + resolution: {integrity: sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parent-module@2.0.0: + resolution: {integrity: sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==} + engines: {node: '>=8'} + + parse-imports-exports@0.2.4: + resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-ms@4.0.0: + resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} + engines: {node: '>=18'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + parse-passwd@1.0.0: + resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} + engines: {node: '>=0.10.0'} + + parse-statements@1.0.11: + resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} + + parse5-htmlparser2-tree-adapter@7.1.0: + resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==} + + parse5-parser-stream@7.1.2: + resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-intersection@2.2.1: + resolution: {integrity: sha512-9u8xvMcSfuOiStv9bPdnRJQhGQXLKurew94n4GPQCdH1nj9QKC9ObbNoIpiRq8skiOBxKkt277PgOoFgAt3/rA==} + + path-intersection@3.1.0: + resolution: {integrity: sha512-3xS3lvv/vuwm5aH2BVvNRvnvwR2Drde7jQClKpCXTYXIMMjcw/EnMhzCgeHwqbCpzi760PEfAkU53vSIlrNr9A==} + engines: {node: '>= 14.20'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + path-type@6.0.0: + resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==} + engines: {node: '>=18'} + + pathe@0.2.0: + resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + perfect-debounce@2.0.0: + resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pinia-plugin-persistedstate@4.7.1: + resolution: {integrity: sha512-WHOqh2esDlR3eAaknPbqXrkkj0D24h8shrDPqysgCFR6ghqP/fpFfJmMPJp0gETHsvrh9YNNg6dQfo2OEtDnIQ==} + peerDependencies: + '@nuxt/kit': '>=3.0.0' + '@pinia/nuxt': '>=0.10.0' + pinia: ^3.0.3 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + '@pinia/nuxt': + optional: true + pinia: + optional: true + + pinia@3.0.4: + resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==} + peerDependencies: + typescript: '>=4.5.0' + vue: ^3.5.24 + peerDependenciesMeta: + typescript: + optional: true + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkcs7@1.0.4: + resolution: {integrity: sha512-afRERtHn54AlwaF2/+LFszyAANTCggGilmcmILUzEjvs3XgFZT+xE6+QWQcAGmu4xajy+Xtj7acLOPdx5/eXWQ==} + hasBin: true + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + + playwright-core@1.57.0: + resolution: {integrity: sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.57.0: + resolution: {integrity: sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==} + engines: {node: '>=18'} + hasBin: true + + please-upgrade-node@3.2.0: + resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + popmotion@11.0.5: + resolution: {integrity: sha512-la8gPM1WYeFznb/JqF4GiTkRRPZsfaj2+kCxqQgr2MJylMmIKUwBfWW8Wa5fml/8gmtlD5yI01MP1QCZPWmppA==} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss-antd-fixes@0.2.0: + resolution: {integrity: sha512-WuV4Ip5DnLg8q+wmNXUq13wZZcA5Lrj+em3Jznl4cxItZDgFp4wEh+5ba59TvFeHfwNuTEZ6YNDsYP9DG6NurQ==} + peerDependencies: + postcss: ^8.0.0 + + postcss-attribute-case-insensitive@7.0.1: + resolution: {integrity: sha512-Uai+SupNSqzlschRyNx3kbCTWgY/2hcwtHEI/ej2LJWc9JJ77qKgGptd8DHwY1mXtZ7Aoh4z4yxfwMBue9eNgw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-calc@10.1.1: + resolution: {integrity: sha512-NYEsLHh8DgG/PRH2+G9BTuUdtf9ViS+vdoQ0YA5OQdGsfN4ztiwtDWNtBl9EKeqNMFnIu8IKZ0cLxEQ5r5KVMw==} + engines: {node: ^18.12 || ^20.9 || >=22.0} + peerDependencies: + postcss: ^8.4.38 + + postcss-clamp@4.1.0: + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + + postcss-color-functional-notation@7.0.12: + resolution: {integrity: sha512-TLCW9fN5kvO/u38/uesdpbx3e8AkTYhMvDZYa9JpmImWuTE99bDQ7GU7hdOADIZsiI9/zuxfAJxny/khknp1Zw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-color-hex-alpha@10.0.0: + resolution: {integrity: sha512-1kervM2cnlgPs2a8Vt/Qbe5cQ++N7rkYo/2rz2BkqJZIHQwaVuJgQH38REHrAi4uM0b1fqxMkWYmese94iMp3w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-color-rebeccapurple@10.0.0: + resolution: {integrity: sha512-JFta737jSP+hdAIEhk1Vs0q0YF5P8fFcj+09pweS8ktuGuZ8pPlykHsk6mPxZ8awDl4TrcxUqJo9l1IhVr/OjQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-colormin@7.0.5: + resolution: {integrity: sha512-ekIBP/nwzRWhEMmIxHHbXHcMdzd1HIUzBECaj5KEdLz9DVP2HzT065sEhvOx1dkLjYW7jyD0CngThx6bpFi2fA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-convert-values@7.0.8: + resolution: {integrity: sha512-+XNKuPfkHTCEo499VzLMYn94TiL3r9YqRE3Ty+jP7UX4qjewUONey1t7CG21lrlTLN07GtGM8MqFVp86D4uKJg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-custom-media@11.0.6: + resolution: {integrity: sha512-C4lD4b7mUIw+RZhtY7qUbf4eADmb7Ey8BFA2px9jUbwg7pjTZDl4KY4bvlUV+/vXQvzQRfiGEVJyAbtOsCMInw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-properties@14.0.6: + resolution: {integrity: sha512-fTYSp3xuk4BUeVhxCSJdIPhDLpJfNakZKoiTDx7yRGCdlZrSJR7mWKVOBS4sBF+5poPQFMj2YdXx1VHItBGihQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-custom-selectors@8.0.5: + resolution: {integrity: sha512-9PGmckHQswiB2usSO6XMSswO2yFWVoCAuih1yl9FVcwkscLjRKjwsjM3t+NIWpSU2Jx3eOiK2+t4vVTQaoCHHg==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-dir-pseudo-class@9.0.1: + resolution: {integrity: sha512-tRBEK0MHYvcMUrAuYMEOa0zg9APqirBcgzi6P21OhxtJyJADo/SWBwY1CAwEohQ/6HDaa9jCjLRG7K3PVQYHEA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-discard-comments@7.0.5: + resolution: {integrity: sha512-IR2Eja8WfYgN5n32vEGSctVQ1+JARfu4UH8M7bgGh1bC+xI/obsPJXaBpQF7MAByvgwZinhpHpdrmXtvVVlKcQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-discard-duplicates@7.0.2: + resolution: {integrity: sha512-eTonaQvPZ/3i1ASDHOKkYwAybiM45zFIc7KXils4mQmHLqIswXD9XNOKEVxtTFnsmwYzF66u4LMgSr0abDlh5w==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-discard-empty@7.0.1: + resolution: {integrity: sha512-cFrJKZvcg/uxB6Ijr4l6qmn3pXQBna9zyrPC+sK0zjbkDUZew+6xDltSF7OeB7rAtzaaMVYSdbod+sZOCWnMOg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-discard-overridden@7.0.1: + resolution: {integrity: sha512-7c3MMjjSZ/qYrx3uc1940GSOzN1Iqjtlqe8uoSg+qdVPYyRb0TILSqqmtlSFuE4mTDECwsm397Ya7iXGzfF7lg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-double-position-gradients@6.0.4: + resolution: {integrity: sha512-m6IKmxo7FxSP5nF2l63QbCC3r+bWpFUWmZXZf096WxG0m7Vl1Q1+ruFOhpdDRmKrRS+S3Jtk+TVk/7z0+BVK6g==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-focus-visible@10.0.1: + resolution: {integrity: sha512-U58wyjS/I1GZgjRok33aE8juW9qQgQUNwTSdxQGuShHzwuYdcklnvK/+qOWX1Q9kr7ysbraQ6ht6r+udansalA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-focus-within@9.0.1: + resolution: {integrity: sha512-fzNUyS1yOYa7mOjpci/bR+u+ESvdar6hk8XNK/TRR0fiGTp2QT5N+ducP0n3rfH/m9I7H/EQU6lsa2BrgxkEjw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-font-variant@5.0.0: + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + + postcss-gap-properties@6.0.0: + resolution: {integrity: sha512-Om0WPjEwiM9Ru+VhfEDPZJAKWUd0mV1HmNXqp2C29z80aQ2uP9UVhLc7e3aYMIor/S5cVhoPgYQ7RtfeZpYTRw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-html@1.8.0: + resolution: {integrity: sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==} + engines: {node: ^12 || >=14} + + postcss-image-set-function@7.0.0: + resolution: {integrity: sha512-QL7W7QNlZuzOwBTeXEmbVckNt1FSmhQtbMRvGGqqU4Nf4xk6KUEQhAoWuMzwbSv5jxiRiSZ5Tv7eiDB9U87znA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-import@16.1.1: + resolution: {integrity: sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-lab-function@7.0.12: + resolution: {integrity: sha512-tUcyRk1ZTPec3OuKFsqtRzW2Go5lehW29XA21lZ65XmzQkz43VY2tyWEC202F7W3mILOjw0voOiuxRGTsN+J9w==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: ^2.6.1 + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss-logical@8.1.0: + resolution: {integrity: sha512-pL1hXFQ2fEXNKiNiAgtfA005T9FBxky5zkX6s4GZM2D8RkVgRqz3f4g1JUoq925zXv495qk8UNldDwh8uGEDoA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-media-query-parser@0.2.3: + resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} + + postcss-merge-longhand@7.0.5: + resolution: {integrity: sha512-Kpu5v4Ys6QI59FxmxtNB/iHUVDn9Y9sYw66D6+SZoIk4QTz1prC4aYkhIESu+ieG1iylod1f8MILMs1Em3mmIw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-merge-rules@7.0.7: + resolution: {integrity: sha512-njWJrd/Ms6XViwowaaCc+/vqhPG3SmXn725AGrnl+BgTuRPEacjiLEaGq16J6XirMJbtKkTwnt67SS+e2WGoew==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-minify-font-values@7.0.1: + resolution: {integrity: sha512-2m1uiuJeTplll+tq4ENOQSzB8LRnSUChBv7oSyFLsJRtUgAAJGP6LLz0/8lkinTgxrmJSPOEhgY1bMXOQ4ZXhQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-minify-gradients@7.0.1: + resolution: {integrity: sha512-X9JjaysZJwlqNkJbUDgOclyG3jZEpAMOfof6PUZjPnPrePnPG62pS17CjdM32uT1Uq1jFvNSff9l7kNbmMSL2A==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-minify-params@7.0.5: + resolution: {integrity: sha512-FGK9ky02h6Ighn3UihsyeAH5XmLEE2MSGH5Tc4tXMFtEDx7B+zTG6hD/+/cT+fbF7PbYojsmmWjyTwFwW1JKQQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-minify-selectors@7.0.5: + resolution: {integrity: sha512-x2/IvofHcdIrAm9Q+p06ZD1h6FPcQ32WtCRVodJLDR+WMn8EVHI1kvLxZuGKz/9EY5nAmI6lIQIrpo4tBy5+ug==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-nested@5.0.6: + resolution: {integrity: sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-nested@7.0.2: + resolution: {integrity: sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-nesting@13.0.2: + resolution: {integrity: sha512-1YCI290TX+VP0U/K/aFxzHzQWHWURL+CtHMSbex1lCdpXD1SoR2sYuxDu5aNI9lPoXpKTCggFZiDJbwylU0LEQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-normalize-charset@7.0.1: + resolution: {integrity: sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-display-values@7.0.1: + resolution: {integrity: sha512-E5nnB26XjSYz/mGITm6JgiDpAbVuAkzXwLzRZtts19jHDUBFxZ0BkXAehy0uimrOjYJbocby4FVswA/5noOxrQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-positions@7.0.1: + resolution: {integrity: sha512-pB/SzrIP2l50ZIYu+yQZyMNmnAcwyYb9R1fVWPRxm4zcUFCY2ign7rcntGFuMXDdd9L2pPNUgoODDk91PzRZuQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-repeat-style@7.0.1: + resolution: {integrity: sha512-NsSQJ8zj8TIDiF0ig44Byo3Jk9e4gNt9x2VIlJudnQQ5DhWAHJPF4Tr1ITwyHio2BUi/I6Iv0HRO7beHYOloYQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-string@7.0.1: + resolution: {integrity: sha512-QByrI7hAhsoze992kpbMlJSbZ8FuCEc1OT9EFbZ6HldXNpsdpZr+YXC5di3UEv0+jeZlHbZcoCADgb7a+lPmmQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-timing-functions@7.0.1: + resolution: {integrity: sha512-bHifyuuSNdKKsnNJ0s8fmfLMlvsQwYVxIoUBnowIVl2ZAdrkYQNGVB4RxjfpvkMjipqvbz0u7feBZybkl/6NJg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-unicode@7.0.5: + resolution: {integrity: sha512-X6BBwiRxVaFHrb2WyBMddIeB5HBjJcAaUHyhLrM2FsxSq5TFqcHSsK7Zu1otag+o0ZphQGJewGH1tAyrD0zX1Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-url@7.0.1: + resolution: {integrity: sha512-sUcD2cWtyK1AOL/82Fwy1aIVm/wwj5SdZkgZ3QiUzSzQQofrbq15jWJ3BA7Z+yVRwamCjJgZJN0I9IS7c6tgeQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-normalize-whitespace@7.0.1: + resolution: {integrity: sha512-vsbgFHMFQrJBJKrUFJNZ2pgBeBkC2IvvoHjz1to0/0Xk7sII24T0qFOiJzG6Fu3zJoq/0yI4rKWi7WhApW+EFA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-opacity-percentage@3.0.0: + resolution: {integrity: sha512-K6HGVzyxUxd/VgZdX04DCtdwWJ4NGLG212US4/LA1TLAbHgmAsTWVR86o+gGIbFtnTkfOpb9sCRBx8K7HO66qQ==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-ordered-values@7.0.2: + resolution: {integrity: sha512-AMJjt1ECBffF7CEON/Y0rekRLS6KsePU6PRP08UqYW4UGFRnTXNrByUzYK1h8AC7UWTZdQ9O3Oq9kFIhm0SFEw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-overflow-shorthand@6.0.0: + resolution: {integrity: sha512-BdDl/AbVkDjoTofzDQnwDdm/Ym6oS9KgmO7Gr+LHYjNWJ6ExORe4+3pcLQsLA9gIROMkiGVjjwZNoL/mpXHd5Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-page-break@3.0.4: + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + + postcss-place@10.0.0: + resolution: {integrity: sha512-5EBrMzat2pPAxQNWYavwAfoKfYcTADJ8AXGVPcUZ2UkNloUTWzJQExgrzrDkh3EKzmAx1evfTAzF9I8NGcc+qw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-preset-env@10.4.0: + resolution: {integrity: sha512-2kqpOthQ6JhxqQq1FSAAZGe9COQv75Aw8WbsOvQVNJ2nSevc9Yx/IKZGuZ7XJ+iOTtVon7LfO7ELRzg8AZ+sdw==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-pseudo-class-any-link@10.0.1: + resolution: {integrity: sha512-3el9rXlBOqTFaMFkWDOkHUTQekFIYnaQY55Rsp8As8QQkpiSgIYEcF/6Ond93oHiDsGb4kad8zjt+NPlOC1H0Q==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-reduce-initial@7.0.5: + resolution: {integrity: sha512-RHagHLidG8hTZcnr4FpyMB2jtgd/OcyAazjMhoy5qmWJOx1uxKh4ntk0Pb46ajKM0rkf32lRH4C8c9qQiPR6IA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-reduce-transforms@7.0.1: + resolution: {integrity: sha512-MhyEbfrm+Mlp/36hvZ9mT9DaO7dbncU0CvWI8V93LRkY6IYlu38OPg3FObnuKTUxJ4qA8HpurdQOo5CyqqO76g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-replace-overflow-wrap@4.0.0: + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-safe-parser@7.0.1: + resolution: {integrity: sha512-0AioNCJZ2DPYz5ABT6bddIqlhgwhpHZ/l65YAYo0BCIn0xiDpsnTHz0gnoTGk0OXZW0JRs+cDwL8u/teRdz+8A==} + engines: {node: '>=18.0'} + peerDependencies: + postcss: ^8.4.31 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-not@8.0.1: + resolution: {integrity: sha512-kmVy/5PYVb2UOhy0+LqUYAhKj7DUGDpSWa5LZqlkWJaaAV+dxxsOG3+St0yNLu6vsKD7Dmqx+nWQt0iil89+WA==} + engines: {node: '>=18'} + peerDependencies: + postcss: ^8.4 + + postcss-selector-parser@6.0.10: + resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} + engines: {node: '>=4'} + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-selector-parser@7.1.0: + resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + engines: {node: '>=4'} + + postcss-sorting@8.0.2: + resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} + peerDependencies: + postcss: ^8.4.20 + + postcss-sorting@9.1.0: + resolution: {integrity: sha512-Mn8KJ45HNNG6JBpBizXcyf6LqY/qyqetGcou/nprDnFwBFBLGj0j/sNKV2lj2KMOVOwdXu14aEzqJv8CIV6e8g==} + peerDependencies: + postcss: ^8.4.20 + + postcss-svgo@7.1.0: + resolution: {integrity: sha512-KnAlfmhtoLz6IuU3Sij2ycusNs4jPW+QoFE5kuuUOK8awR6tMxZQrs5Ey3BUz7nFCzT3eqyFgqkyrHiaU2xx3w==} + engines: {node: ^18.12.0 || ^20.9.0 || >= 18} + peerDependencies: + postcss: ^8.4.32 + + postcss-unique-selectors@7.0.4: + resolution: {integrity: sha512-pmlZjsmEAG7cHd7uK3ZiNSW6otSZ13RHuZ/4cDN/bVglS5EpF2r2oxY99SuOHa8m7AWoBCelTS3JPpzsIs8skQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.27.2: + resolution: {integrity: sha512-5SYSgFKSyhCbk6SrXyMpqjb5+MQBgfvEKE/OC+PujcY34sOpqtr+0AZQtPYx5IA6VxynQ7rUPCtKzyovpj9Bpg==} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier-plugin-tailwindcss@0.7.1: + resolution: {integrity: sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==} + engines: {node: '>=20.19'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + + pretty-bytes@6.1.1: + resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==} + engines: {node: ^14.13.1 || >=16.0.0} + + pretty-bytes@7.1.0: + resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} + engines: {node: '>=20'} + + pretty-ms@9.3.0: + resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} + engines: {node: '>=18'} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + publint@0.3.15: + resolution: {integrity: sha512-xPbRAPW+vqdiaKy5sVVY0uFAu3LaviaPO3pZ9FaRx59l9+U/RKR1OEbLhkug87cwiVKxPXyB4txsv5cad67u+A==} + engines: {node: '>=18'} + hasBin: true + + punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pupa@3.3.0: + resolution: {integrity: sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA==} + engines: {node: '>=12.20'} + + qified@0.5.2: + resolution: {integrity: sha512-7gJ6mxcQb9vUBOtbKm5mDevbe2uRcOEVp1g4gb/Q+oLntB3HY8eBhOYRxFI2mlDFlY1e4DOSCptzxarXRvzxCA==} + engines: {node: '>=20'} + + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + radix3@1.1.2: + resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomcolor@0.6.2: + resolution: {integrity: sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + read-yaml-file@2.1.0: + resolution: {integrity: sha512-UkRNRIwnhG+y7hpqnycCL/xbTk7+ia9VuVTC0S+zVbwd65DI9eUpRMfsWIGrCWxTU/mi+JW8cHQCrv+zfCbEPQ==} + engines: {node: '>=10.13'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readdir-glob@1.1.3: + resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + redis-errors@1.2.0: + resolution: {integrity: sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==} + engines: {node: '>=4'} + + redis-parser@3.0.0: + resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} + engines: {node: '>=4'} + + refa@0.12.1: + resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regenerate-unicode-properties@10.2.2: + resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regex-recursion@6.0.2: + resolution: {integrity: sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==} + + regex-utilities@2.3.0: + resolution: {integrity: sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==} + + regex@6.0.1: + resolution: {integrity: sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==} + + regexp-ast-analysis@0.7.1: + resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + regexpu-core@6.4.0: + resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==} + engines: {node: '>=4'} + + registry-auth-token@5.1.0: + resolution: {integrity: sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + regjsgen@0.8.0: + resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} + + regjsparser@0.13.0: + resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} + hasBin: true + + reka-ui@2.6.0: + resolution: {integrity: sha512-NrGMKrABD97l890mFS3TNUzB0BLUfbL3hh0NjcJRIUSUljb288bx3Mzo31nOyUcdiiW0HqFGXJwyCBh9cWgb0w==} + peerDependencies: + vue: ^3.5.24 + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + remarkable-katex@1.2.1: + resolution: {integrity: sha512-Y1VquJBZnaVsfsVcKW2hmjT+pDL7mp8l5WAVlvuvViltrdok2m1AIKmJv8SsH+mBY84PoMw67t3kTWw1dIm8+g==} + + remarkable@2.0.1: + resolution: {integrity: sha512-YJyMcOH5lrR+kZdmB0aJJ4+93bEojRZ1HGDn9Eagu6ibg7aVZhc3OWbbShRid+Q5eAfsEqWxpe+g5W5nYNfNiA==} + engines: {node: '>= 6.0.0'} + hasBin: true + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + require-package-name@2.0.1: + resolution: {integrity: sha512-uuoJ1hU/k6M0779t3VMVIYpb2VMJk05cehCaABFhXaibcbvfgR8wKiozLjVFSzJPmQMRqIcO0HMyTFqfV09V6Q==} + + reserved-identifiers@1.2.0: + resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} + engines: {node: '>=18'} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-dir@1.0.1: + resolution: {integrity: sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@6.1.2: + resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==} + engines: {node: 20 || >=22} + hasBin: true + + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + + rollup-plugin-dts@6.3.0: + resolution: {integrity: sha512-d0UrqxYd8KyZ6i3M2Nx7WOMy708qsV/7fTHMHxCMCBOAe3V/U7OMPu5GkX8hC+cmkHhzGnfeYongl1IgiooddA==} + engines: {node: '>=16'} + peerDependencies: + rollup: ^3.29.4 || ^4 + typescript: ^4.5 || ^5.0 + + rollup-plugin-visualizer@5.14.0: + resolution: {integrity: sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + rolldown: 1.x + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup-plugin-visualizer@6.0.5: + resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + rolldown: 1.x || ^1.0.0-beta + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup@2.79.2: + resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==} + engines: {node: '>=10.0.0'} + hasBin: true + + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rotated-array-set@3.0.0: + resolution: {integrity: sha512-G7689wvCM0szMFXUAhi3GfNGcSPlndg077cdRWoq7UegOAwfU2MJ0jD7s7jB+2ppKA75Kr/O0HwAP9+rRdBctg==} + engines: {node: ^14.13.1 || >=16.0.0} + + run-applescript@7.1.0: + resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rust-result@1.0.0: + resolution: {integrity: sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==} + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-json-parse@4.0.0: + resolution: {integrity: sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass@1.94.2: + resolution: {integrity: sha512-N+7WK20/wOr7CzA2snJcUSSNTCzeCGUTFY3OgeQP3mZ1aj9NMQ0mSTXwlrnd89j33zzQJGqIN52GIOmYrfq46A==} + engines: {node: '>=14.0.0'} + hasBin: true + + sax@1.4.3: + resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} + + saxen@8.1.2: + resolution: {integrity: sha512-xUOiiFbc3Ow7p8KMxwsGICPx46ZQvy3+qfNVhrkwfz3Vvq45eGt98Ft5IQaA1R/7Tb5B5MKh9fUR9x3c3nDTxw==} + + scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + + scslre@0.3.0: + resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==} + engines: {node: ^14.0.0 || >=16.0.0} + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + search-insights@2.17.3: + resolution: {integrity: sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ==} + + section-matter@1.0.0: + resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==} + engines: {node: '>=4'} + + secure-ls@2.0.0: + resolution: {integrity: sha512-Wgtnw0QSm0v7gVKv11nOoeyGS65EThGXnBB7jfd4IhZd2eq3B4AMPcXAL5qJ1h55+Qolun7TONTwX7H5m6e2pQ==} + engines: {node: '>=8.0'} + + seemly@0.3.10: + resolution: {integrity: sha512-2+SMxtG1PcsL0uyhkumlOU6Qo9TAQ/WyH7tthnPIOQB05/12jz9naq6GZ6iZ6ApVsO3rr2gsnTf3++OV63kE1Q==} + + select@1.1.2: + resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==} + + semver-compare@1.0.0: + resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serve-placeholder@2.0.2: + resolution: {integrity: sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==} + + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + shallow-equal@1.2.1: + resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shiki@2.5.0: + resolution: {integrity: sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ==} + + short-tree@3.0.0: + resolution: {integrity: sha512-Yd9NFs/o9QSoH4/wTjxk4Xe0+CIzitDRN1Qg7iBeTSejKjlCg/3PbgiRwDUVuaIxD0RRdv7Iz9jKr7e0HljtUg==} + engines: {node: ^14.13.1 || >=16.0.0} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + signature_pad@3.0.0-beta.4: + resolution: {integrity: sha512-cOf2NhVuTiuNqe2X/ycEmizvCDXk0DoemhsEpnkcGnA4kS5iJYTCqZ9As7tFBbsch45Q1EdX61833+6sjJ8rrw==} + + signature_pad@5.1.2: + resolution: {integrity: sha512-zYmjddQDolKgJnzYRoaMYaGezKaZbwjNBBwk1W7uVY0cyNWW30Izeu9BNVAGEgXvqB6APDJmf783oWTU7W67LQ==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + smob@1.5.0: + resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + + sortablejs@1.14.0: + resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} + + sortablejs@1.15.6: + resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + deprecated: The work that was done in this beta branch won't be included in future versions + + 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==} + + spawndamnit@3.0.1: + resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@4.0.0: + resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} + + spdx-license-ids@3.0.22: + resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + + speakingurl@14.0.1: + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} + engines: {node: '>=0.10.0'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + ssri@13.0.0: + resolution: {integrity: sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==} + engines: {node: ^20.17.0 || >=22.9.0} + + stable-hash-x@0.2.0: + resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} + engines: {node: '>=12.0.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + standard-as-callback@2.1.0: + resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + steady-xml@0.1.0: + resolution: {integrity: sha512-5sk17qO2wWRtonTNoBhoKAB35OSsGJOa3+NEa6D+1GS+de+ujDWxnflMkXBrviOfkNrPTUqduAdXhrMJs89nAw==} + engines: {node: '>=12.0.0'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + streamx@2.23.0: + resolution: {integrity: sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + stringify-object@3.3.0: + resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} + engines: {node: '>=4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-bom-string@1.0.0: + resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==} + engines: {node: '>=0.10.0'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-comments@2.0.1: + resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==} + engines: {node: '>=10'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + + strip-indent@4.1.1: + resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} + engines: {node: '>=12'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + strnum@1.1.2: + resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} + + stubborn-fs@2.0.0: + resolution: {integrity: sha512-Y0AvSwDw8y+nlSNFXMm2g6L51rBGdAQT20J3YSOqxC53Lo3bjWRtr2BKcfYoAf352WYpsZSTURrA0tqhfgudPA==} + + stubborn-utils@1.0.2: + resolution: {integrity: sha512-zOh9jPYI+xrNOyisSelgym4tolKTJCQd5GBhK0+0xJvcYDcwlOoxF/rnFKQ2KRZknXSG9jWAp66fwP6AxN9STg==} + + style-mod@4.1.3: + resolution: {integrity: sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==} + + style-search@0.1.0: + resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + + style-value-types@5.1.2: + resolution: {integrity: sha512-Vs9fNreYF9j6W2VvuDTP7kepALi7sk0xtk2Tu8Yxi9UoajJdEVpNpCov0HsLTqXvNGKX+Uv09pkozVITi1jf3Q==} + + stylehacks@7.0.7: + resolution: {integrity: sha512-bJkD0JkEtbRrMFtwgpJyBbFIwfDDONQ1Ov3sDLZQP8HuJ73kBOyx66H4bOcAbVWmnfLdvQ0AJwXxOMkpujcO6g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.32 + + stylelint-config-html@1.1.0: + resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-recess-order@6.1.0: + resolution: {integrity: sha512-0rGZgJQjUKqv1PXZnRJxr13f3hb3i2snx2nIL6luDWUf4nD3BAweB8noS7jdfBQ56HQG3RKSF0a+MAHfBGFxgw==} + peerDependencies: + stylelint: '>=16' + + stylelint-config-recommended-scss@14.1.0: + resolution: {integrity: sha512-bhaMhh1u5dQqSsf6ri2GVWWQW5iUjBYgcHkh7SgDDn92ijoItC/cfO/W+fpXshgTQWhwFkP1rVcewcv4jaftRg==} + engines: {node: '>=18.12.0'} + peerDependencies: + postcss: ^8.3.3 + stylelint: ^16.6.1 + peerDependenciesMeta: + postcss: + optional: true + + stylelint-config-recommended-vue@1.6.1: + resolution: {integrity: sha512-lLW7hTIMBiTfjenGuDq2kyHA6fBWd/+Df7MO4/AWOxiFeXP9clbpKgg27kHfwA3H7UNMGC7aeP3mNlZB5LMmEQ==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-recommended@14.0.1: + resolution: {integrity: sha512-bLvc1WOz/14aPImu/cufKAZYfXs/A/owZfSMZ4N+16WGXLoX5lOir53M6odBxvhgmgdxCVnNySJmZKx73T93cg==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.1.0 + + stylelint-config-recommended@16.0.0: + resolution: {integrity: sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.16.0 + + stylelint-config-recommended@17.0.0: + resolution: {integrity: sha512-WaMSdEiPfZTSFVoYmJbxorJfA610O0tlYuU2aEwY33UQhSPgFbClrVJYWvy3jGJx+XW37O+LyNLiZOEXhKhJmA==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.23.0 + + stylelint-config-standard@38.0.0: + resolution: {integrity: sha512-uj3JIX+dpFseqd/DJx8Gy3PcRAJhlEZ2IrlFOc4LUxBX/PNMEQ198x7LCOE2Q5oT9Vw8nyc4CIL78xSqPr6iag==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.18.0 + + stylelint-order@6.0.4: + resolution: {integrity: sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==} + peerDependencies: + stylelint: ^14.0.0 || ^15.0.0 || ^16.0.1 + + stylelint-order@7.0.0: + resolution: {integrity: sha512-rSWxx0KscYfxU02wEskKXES9lkRzuuONMMNkZ7SUc6uiF3tDKm7e+sE0Ax/SBlG4TUf1sp1R6f3/SlsPGmzthg==} + engines: {node: '>=20.19.0'} + peerDependencies: + stylelint: ^16.18.0 + + stylelint-prettier@5.0.3: + resolution: {integrity: sha512-B6V0oa35ekRrKZlf+6+jA+i50C4GXJ7X1PPmoCqSUoXN6BrNF6NhqqhanvkLjqw2qgvrS0wjdpeC+Tn06KN3jw==} + engines: {node: '>=18.12.0'} + peerDependencies: + prettier: '>=3.0.0' + stylelint: '>=16.0.0' + + stylelint-scss@6.12.1: + resolution: {integrity: sha512-UJUfBFIvXfly8WKIgmqfmkGKPilKB4L5j38JfsDd+OCg2GBdU0vGUV08Uw82tsRZzd4TbsUURVVNGeOhJVF7pA==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^16.0.2 + + stylelint@16.26.0: + resolution: {integrity: sha512-Y/3AVBefrkqqapVYH3LBF5TSDZ1kw+0XpdKN2KchfuhMK6lQ85S4XOG4lIZLcrcS4PWBmvcY6eS2kCQFz0jukQ==} + engines: {node: '>=18.12.0'} + hasBin: true + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + superjson@2.2.5: + resolution: {integrity: sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==} + engines: {node: '>=16'} + + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svelte@5.45.2: + resolution: {integrity: sha512-yyXdW2u3H0H/zxxWoGwJoQlRgaSJLp+Vhktv12iRw2WRDlKqUPT54Fi0K/PkXqrdkcQ98aBazpy0AH4BCBVfoA==} + engines: {node: '>=18'} + + sver@1.8.4: + resolution: {integrity: sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA==} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + svgo@4.0.0: + resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==} + engines: {node: '>=16'} + hasBin: true + + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + + system-architecture@0.1.0: + resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} + engines: {node: '>=18'} + + tabbable@6.3.0: + resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + + table@6.9.0: + resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} + engines: {node: '>=10.0.0'} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + tailwind-merge@2.6.0: + resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} + + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + + tailwindcss@3.4.18: + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + tar@7.5.2: + resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} + engines: {node: '>=18'} + + tdesign-icons-vue-next@0.4.1: + resolution: {integrity: sha512-uDPuTLRORnGcTyVGNoentNaK4V+ZcBmhYwcY3KqDaQQ5rrPeLMxu0ZVmgOEf0JtF2QZiqAxY7vodNEiLUdoRKA==} + peerDependencies: + vue: ^3.5.24 + + tdesign-vue-next@1.17.3: + resolution: {integrity: sha512-4x1E5qfcE+rROjfozJz0g3Prc2tSCmoXtZ/Q7BG4rW37FqDborcPd+G9wE3E8Rht2z84/kuzK/SZur0tMjIshQ==} + engines: {node: '>= 18'} + peerDependencies: + vue: ^3.5.24 + + temp-dir@2.0.0: + resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==} + engines: {node: '>=8'} + + tempy@0.6.0: + resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==} + engines: {node: '>=10'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + terser@5.44.1: + resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} + engines: {node: '>=10'} + hasBin: true + + text-decoder@1.2.3: + resolution: {integrity: sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==} + + text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + + theme-colors@0.1.0: + resolution: {integrity: sha512-6gTEHQqWlQNiOEGHCSSQmU//E5SnXHJ4H7oHQOD8x77CvNYNQAmt73dqR71mzw5ULV87zaHLxK5pIBnsToFuZw==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tiny-emitter@2.1.0: + resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==} + + tiny-svg@3.1.3: + resolution: {integrity: sha512-9mwnPqXInRsBmH/DO6NMxBE++9LsqpVXQSSTZGc5bomoKKvL5OX/Hlotw7XVXP6XLRcHWIzZpxfovGqWKgCypQ==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinymce@7.9.1: + resolution: {integrity: sha512-zaOHwmiP1EqTeLRXAvVriDb00JYnfEjWGPdKEuac7MiZJ5aiDMZ4Unc98Gmajn+PBljOmO1GKV6G0KwWn3+k8A==} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + tippy.js@6.3.7: + resolution: {integrity: sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + to-valid-identifier@1.0.0: + resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} + engines: {node: '>=20'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + treemate@0.3.11: + resolution: {integrity: sha512-M8RGFoKtZ8dF+iwJfAJTOH/SM4KluKOKRJpjCMhI8bG3qB74zrFoArKZ62ll0Fr3mqkMJiQOmWYkdYgDeITYQg==} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + ts-declaration-location@1.0.7: + resolution: {integrity: sha512-EDyGAwH1gO0Ausm9gV6T2nUvBgXT5kGoCMJPllOaooZ+4VvJiKBdZE7wK18N1deEowhcUptS+5GXZK8U/fvpwA==} + peerDependencies: + typescript: '>=4.0.0' + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@2.3.0: + resolution: {integrity: sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==} + + tslib@2.4.0: + resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + turbo-darwin-64@2.6.1: + resolution: {integrity: sha512-Dm0HwhyZF4J0uLqkhUyCVJvKM9Rw7M03v3J9A7drHDQW0qAbIGBrUijQ8g4Q9Cciw/BXRRd8Uzkc3oue+qn+ZQ==} + cpu: [x64] + os: [darwin] + + turbo-darwin-arm64@2.6.1: + resolution: {integrity: sha512-U0PIPTPyxdLsrC3jN7jaJUwgzX5sVUBsKLO7+6AL+OASaa1NbT1pPdiZoTkblBAALLP76FM0LlnsVQOnmjYhyw==} + cpu: [arm64] + os: [darwin] + + turbo-linux-64@2.6.1: + resolution: {integrity: sha512-eM1uLWgzv89bxlK29qwQEr9xYWBhmO/EGiH22UGfq+uXr+QW1OvNKKMogSN65Ry8lElMH4LZh0aX2DEc7eC0Mw==} + cpu: [x64] + os: [linux] + + turbo-linux-arm64@2.6.1: + resolution: {integrity: sha512-MFFh7AxAQAycXKuZDrbeutfWM5Ep0CEZ9u7zs4Hn2FvOViTCzIfEhmuJou3/a5+q5VX1zTxQrKGy+4Lf5cdpsA==} + cpu: [arm64] + os: [linux] + + turbo-windows-64@2.6.1: + resolution: {integrity: sha512-buq7/VAN7KOjMYi4tSZT5m+jpqyhbRU2EUTTvp6V0Ii8dAkY2tAAjQN1q5q2ByflYWKecbQNTqxmVploE0LVwQ==} + cpu: [x64] + os: [win32] + + turbo-windows-arm64@2.6.1: + resolution: {integrity: sha512-7w+AD5vJp3R+FB0YOj1YJcNcOOvBior7bcHTodqp90S3x3bLgpr7tE6xOea1e8JkP7GK6ciKVUpQvV7psiwU5Q==} + cpu: [arm64] + os: [win32] + + turbo@2.6.1: + resolution: {integrity: sha512-qBwXXuDT3rA53kbNafGbT5r++BrhRgx3sAo0cHoDAeG9g1ItTmUMgltz3Hy7Hazy1ODqNpR+C7QwqL6DYB52yA==} + hasBin: true + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.16.0: + resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + type-fest@5.2.0: + resolution: {integrity: sha512-xxCJm+Bckc6kQBknN7i9fnP/xobQRsRQxR01CztFkp/h++yfVxUUcmMgfR2HttJx/dpWjS9ubVuyspJv24Q9DA==} + engines: {node: '>=20'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + ultrahtml@1.6.0: + resolution: {integrity: sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==} + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + unbuild@3.6.1: + resolution: {integrity: sha512-+U5CdtrdjfWkZhuO4N9l5UhyiccoeMEXIc2Lbs30Haxb+tRwB3VwB8AoZRxlAzORXunenSo+j6lh45jx+xkKgg==} + hasBin: true + peerDependencies: + typescript: ^5.9.2 + peerDependenciesMeta: + typescript: + optional: true + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + unctx@2.4.1: + resolution: {integrity: sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici@7.16.0: + resolution: {integrity: sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==} + engines: {node: '>=20.18.1'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + + unicode-canonical-property-names-ecmascript@2.0.1: + resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.2.1: + resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.2.0: + resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==} + engines: {node: '>=4'} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unicorn-magic@0.3.0: + resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} + engines: {node: '>=18'} + + unimport@5.5.0: + resolution: {integrity: sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==} + engines: {node: '>=18.12.0'} + + unique-filename@5.0.0: + resolution: {integrity: sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==} + engines: {node: ^20.17.0 || >=22.9.0} + + unique-slug@6.0.0: + resolution: {integrity: sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==} + engines: {node: ^20.17.0 || >=22.9.0} + + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + 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.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + unplugin-element-plus@0.11.1: + resolution: {integrity: sha512-pj6+SCBWfHrCrkaXDcDwHvT6u2XURFOwDFd8V5Nweq4xtr/91sAg/xNoih96FK3y89nqnHQ6qINilSGH2sUtmg==} + engines: {node: '>=20.19.0'} + + unplugin-utils@0.3.1: + resolution: {integrity: sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==} + engines: {node: '>=20.19.0'} + + 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'} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + unstorage@1.17.3: + resolution: {integrity: sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6.0.3 || ^7.0.0 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + + untun@0.1.3: + resolution: {integrity: sha512-4luGP9LMYszMRZwsvyUd9MrxgEGZdZuZgpVQHEEX0lCYFESasVRvZd0EYpCkOIbJKHMuv0LskpXc/8Un+MJzEQ==} + hasBin: true + + untyped@2.0.0: + resolution: {integrity: sha512-nwNCjxJTjNuLCgFr42fEak5OcLuB3ecca+9ksPFNvtfYSLpjf+iJqSIaSnIile6ZPbKYxI5k2AfXqeopGudK/g==} + hasBin: true + + unwasm@0.3.11: + resolution: {integrity: sha512-Vhp5gb1tusSQw5of/g3Q697srYgMXvwMgXMjcG4ZNga02fDX9coxJ9fAb0Ci38hM2Hv/U1FXRPGgjP2BYqhNoQ==} + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + update-notifier@7.3.1: + resolution: {integrity: sha512-+dwUY4L35XFYEzE+OAL3sarJdUioVovq+8f7lcIJ7wnmnYQV5UD1Y/lcwaMSyaQ6Bj3JMj1XSTjZbNLHn/19yA==} + engines: {node: '>=18'} + + uqr@0.1.2: + resolution: {integrity: sha512-MJu7ypHq6QasgF5YRTjqscSzQp/W11zoUk6kvmlH+fmWEs63Y0Eib13hYFwAzagRJcVY8WVnlV+eBDUGMJ5IbA==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url-toolkit@2.2.5: + resolution: {integrity: sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + validator@13.15.23: + resolution: {integrity: sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==} + engines: {node: '>= 0.10'} + + vdirs@0.1.8: + resolution: {integrity: sha512-H9V1zGRLQZg9b+GdMk8MXDN2Lva0zx72MPahDKc30v+DtwKjfyOSXWRIX4t2mhDubM1H09gPhWeth/BJWPHGUw==} + peerDependencies: + vue: ^3.5.24 + + vee-validate@4.15.1: + resolution: {integrity: sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==} + peerDependencies: + vue: ^3.5.24 + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + video.js@7.21.7: + resolution: {integrity: sha512-T2s3WFAht7Zjr2OSJamND9x9Dn2O+Z5WuHGdh8jI5SYh5mkMdVTQ7vSRmA5PYpjXJ2ycch6jpMjkJEIEU2xxqw==} + + videojs-font@3.2.0: + resolution: {integrity: sha512-g8vHMKK2/JGorSfqAZQUmYYNnXmfec4MLhwtEFS+mMs2IDY398GLysy6BH6K+aS1KMNu/xWZ8Sue/X/mdQPliA==} + + videojs-vtt.js@0.15.5: + resolution: {integrity: sha512-yZbBxvA7QMYn15Lr/ZfhhLPrNpI/RmCSCqgIff57GC2gIrV5YfyzLfLyZMj0NnZSAz8syB4N0nHXpZg9MyrMOQ==} + + vite-dev-rpc@1.1.0: + resolution: {integrity: sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==} + peerDependencies: + vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0 + + vite-hot-client@2.1.0: + resolution: {integrity: sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==} + peerDependencies: + vite: ^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite-plugin-compression@0.5.1: + resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} + peerDependencies: + vite: '>=2.0.0' + + vite-plugin-dts@4.5.4: + resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==} + peerDependencies: + typescript: '*' + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite-plugin-html@3.2.2: + resolution: {integrity: sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q==} + peerDependencies: + vite: '>=2.0.0' + + vite-plugin-inspect@11.3.3: + resolution: {integrity: sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==} + engines: {node: '>=14'} + peerDependencies: + '@nuxt/kit': '*' + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + '@nuxt/kit': + optional: true + + vite-plugin-lazy-import@1.0.7: + resolution: {integrity: sha512-mE6oAObOb4wqso4AoUGi9cLjdR+4vay1RCaKJvziBuFPlziZl7J0aw2hsqRTokLVRx3bli0a0VyjMOwsNDv58A==} + + vite-plugin-pwa@1.1.0: + resolution: {integrity: sha512-VsSpdubPzXhHWVINcSx6uHRMpOHVHQcHsef1QgkOlEoaIDAlssFEW88LBq1a59BuokAhsh2kUDJbaX1bZv4Bjw==} + engines: {node: '>=16.0.0'} + peerDependencies: + '@vite-pwa/assets-generator': ^1.0.0 + vite: ^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + workbox-build: ^7.3.0 + workbox-window: ^7.3.0 + peerDependenciesMeta: + '@vite-pwa/assets-generator': + optional: true + + vite-plugin-vue-devtools@8.0.5: + resolution: {integrity: sha512-p619BlKFOqQXJ6uDWS1vUPQzuJOD6xJTfftj57JXBGoBD/yeQCowR7pnWcr/FEX4/HVkFbreI6w2uuGBmQOh6A==} + engines: {node: '>=v14.21.3'} + peerDependencies: + vite: ^6.0.0 || ^7.0.0-0 + + vite-plugin-vue-inspector@5.3.2: + resolution: {integrity: sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==} + peerDependencies: + vite: ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 + + vite@5.4.21: + resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vite@7.2.4: + resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: ^2.6.1 + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitepress-plugin-group-icons@1.6.5: + resolution: {integrity: sha512-+pg4+GKDq2fLqKb1Sat5p1p4SuIZ5tEPxu8HjpwoeecZ/VaXKy6Bdf0wyjedjaTAyZQzXbvyavJegqAcQ+B0VA==} + peerDependencies: + vite: '>=3' + peerDependenciesMeta: + vite: + optional: true + + vitepress@1.6.4: + resolution: {integrity: sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg==} + hasBin: true + peerDependencies: + markdown-it-mathjax3: ^4 + postcss: ^8 + peerDependenciesMeta: + markdown-it-mathjax3: + optional: true + postcss: + optional: true + + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + vooks@0.2.12: + resolution: {integrity: sha512-iox0I3RZzxtKlcgYaStQYKEzWWGAduMmq+jS7OrNdQo1FgGfPMubGL3uGHOU9n97NIvfFDBGnpSvkWyb/NSn/Q==} + peerDependencies: + vue: ^3.5.24 + + vscode-languageserver-textdocument@1.0.12: + resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==} + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + vue-component-type-helpers@2.2.12: + resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} + + vue-demi@0.14.10: + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} + engines: {node: '>=12'} + hasBin: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.5.24 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + + vue-dompurify-html@5.3.0: + resolution: {integrity: sha512-HJQGBHbfSPcb6Mu97McdKbX7TqRHZa6Ji8OCpCNyuHca5QvQZ8IiuwghFPSO8OkSQfqXPNPKFMZdCOrnGGmOSQ==} + peerDependencies: + vue: ^3.5.24 + + vue-eslint-parser@10.2.0: + resolution: {integrity: sha512-CydUvFOQKD928UzZhTp4pr2vWz1L+H99t7Pkln2QSPdvmURT0MoC4wUccfCnuEaihNsu9aYYyk+bep8rlfkUXw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + vue-i18n@11.2.2: + resolution: {integrity: sha512-ULIKZyRluUPRCZmihVgUvpq8hJTtOqnbGZuv4Lz+byEKZq4mU0g92og414l6f/4ju+L5mORsiUuEPYrAuX2NJg==} + engines: {node: '>= 16'} + peerDependencies: + vue: ^3.5.24 + + vue-json-viewer@3.0.4: + resolution: {integrity: sha512-pnC080rTub6YjccthVSNQod2z9Sl5IUUq46srXtn6rxwhW8QM4rlYn+CTSLFKXWfw+N3xv77Cioxw7B4XUKIbQ==} + peerDependencies: + vue: ^3.5.24 + + vue-router@4.6.3: + resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==} + peerDependencies: + vue: ^3.5.24 + + vue-tippy@6.7.1: + resolution: {integrity: sha512-gdHbBV5/Vc8gH87hQHLA7TN1K4BlLco3MAPrTb70ZYGXxx+55rAU4a4mt0fIoP+gB3etu1khUZ6c29Br1n0CiA==} + peerDependencies: + vue: ^3.5.24 + + vue-tsc@3.1.5: + resolution: {integrity: sha512-L/G9IUjOWhBU0yun89rv8fKqmKC+T0HfhrFjlIml71WpfBv9eb4E9Bev8FMbyueBIU9vxQqbd+oOsVcDa5amGw==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue-types@3.0.2: + resolution: {integrity: sha512-IwUC0Aq2zwaXqy74h4WCvFCUtoV0iSWr0snWnE9TnU18S66GAQyqQbRf2qfJtUuiFsBf6qp0MEwdonlwznlcrw==} + engines: {node: '>=10.15.0'} + peerDependencies: + vue: ^3.5.24 + + vue3-print-nb@0.1.4: + resolution: {integrity: sha512-LExI7viEzplR6ZKQ2b+V4U0cwGYbVD4fut/XHvk3UPGlT5CcvIGs6VlwGp107aKgk6P8Pgx4rco3Rehv2lti3A==} + + vue3-signature@0.2.4: + resolution: {integrity: sha512-XFwwFVK9OG3F085pKIq2SlNVqx32WdFH+TXbGEWc5FfEKpx8oMmZuAwZZ50K/pH2FgmJSE8IRwU9DDhrLpd6iA==} + peerDependencies: + vue: ^3.5.24 + + vue@3.5.25: + resolution: {integrity: sha512-YLVdgv2K13WJ6n+kD5owehKtEXwdwXuj2TTyJMsO7pSeKw2bfRNZGjhB7YzrpbMYj5b5QsUebHpOqR3R3ziy/g==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + vuedraggable@4.1.0: + resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} + peerDependencies: + vue: ^3.5.24 + + vueuc@0.4.65: + resolution: {integrity: sha512-lXuMl+8gsBmruudfxnMF9HW4be8rFziylXFu1VHVNbLVhRTXXV4njvpRuJapD/8q+oFEMSfQMH16E/85VoWRyQ==} + peerDependencies: + vue: ^3.5.24 + + vxe-pc-ui@4.10.31: + resolution: {integrity: sha512-I55GN1BhewtUsL6NddNgHx5GBat5lfLxtBUXnPQsk2wxTvAEP80vo2psn7LJB8DJWkf0MiF+2V6BrpnZUdPcKA==} + + vxe-table@4.17.20: + resolution: {integrity: sha512-7aKi162ILgl2rUug31mJZ26m0bEYe3YMH24Fg9oc81IgU9YglvRo9QFn561WRX9ihX8rD0DSW+3BAlujGdPtWw==} + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + + wangeditor@4.7.15: + resolution: {integrity: sha512-aPTdREd8BxXVyJ5MI+LU83FQ7u1EPd341iXIorRNYSOvoimNoZ4nPg+yn3FGbB93/owEa6buLw8wdhYnMCJQLg==} + + warning@4.0.3: + resolution: {integrity: sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==} + + watermark-js-plus@1.6.3: + resolution: {integrity: sha512-iCLOGf70KacIwjGF9MDViYxQcRiVwOH7l42qDHLeE2HeUsQD1EQuUC9cKRG/4SErTUmdqV3yf5WnKk2dRARHPQ==} + + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + when-exit@2.1.5: + resolution: {integrity: sha512-VGkKJ564kzt6Ms1dbgPP/yuIoQCrsFAnRbptpC5wOEsDaNsbCB2bnfnaA8i/vRs5tjUSEOtIuvl9/MyVsvQZCg==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workbox-background-sync@7.4.0: + resolution: {integrity: sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w==} + + workbox-broadcast-update@7.4.0: + resolution: {integrity: sha512-+eZQwoktlvo62cI0b+QBr40v5XjighxPq3Fzo9AWMiAosmpG5gxRHgTbGGhaJv/q/MFVxwFNGh/UwHZ/8K88lA==} + + workbox-build@7.4.0: + resolution: {integrity: sha512-Ntk1pWb0caOFIvwz/hfgrov/OJ45wPEhI5PbTywQcYjyZiVhT3UrwwUPl6TRYbTm4moaFYithYnl1lvZ8UjxcA==} + engines: {node: '>=20.0.0'} + + workbox-cacheable-response@7.4.0: + resolution: {integrity: sha512-0Fb8795zg/x23ISFkAc7lbWes6vbw34DGFIMw31cwuHPgDEC/5EYm6m/ZkylLX0EnEbbOyOCLjKgFS/Z5g0HeQ==} + + workbox-core@7.4.0: + resolution: {integrity: sha512-6BMfd8tYEnN4baG4emG9U0hdXM4gGuDU3ectXuVHnj71vwxTFI7WOpQJC4siTOlVtGqCUtj0ZQNsrvi6kZZTAQ==} + + workbox-expiration@7.4.0: + resolution: {integrity: sha512-V50p4BxYhtA80eOvulu8xVfPBgZbkxJ1Jr8UUn0rvqjGhLDqKNtfrDfjJKnLz2U8fO2xGQJTx/SKXNTzHOjnHw==} + + workbox-google-analytics@7.4.0: + resolution: {integrity: sha512-MVPXQslRF6YHkzGoFw1A4GIB8GrKym/A5+jYDUSL+AeJw4ytQGrozYdiZqUW1TPQHW8isBCBtyFJergUXyNoWQ==} + + workbox-navigation-preload@7.4.0: + resolution: {integrity: sha512-etzftSgdQfjMcfPgbfaZCfM2QuR1P+4o8uCA2s4rf3chtKTq/Om7g/qvEOcZkG6v7JZOSOxVYQiOu6PbAZgU6w==} + + workbox-precaching@7.4.0: + resolution: {integrity: sha512-VQs37T6jDqf1rTxUJZXRl3yjZMf5JX/vDPhmx2CPgDDKXATzEoqyRqhYnRoxl6Kr0rqaQlp32i9rtG5zTzIlNg==} + + workbox-range-requests@7.4.0: + resolution: {integrity: sha512-3Vq854ZNuP6Y0KZOQWLaLC9FfM7ZaE+iuQl4VhADXybwzr4z/sMmnLgTeUZLq5PaDlcJBxYXQ3U91V7dwAIfvw==} + + workbox-recipes@7.4.0: + resolution: {integrity: sha512-kOkWvsAn4H8GvAkwfJTbwINdv4voFoiE9hbezgB1sb/0NLyTG4rE7l6LvS8lLk5QIRIto+DjXLuAuG3Vmt3cxQ==} + + workbox-routing@7.4.0: + resolution: {integrity: sha512-C/ooj5uBWYAhAqwmU8HYQJdOjjDKBp9MzTQ+otpMmd+q0eF59K+NuXUek34wbL0RFrIXe/KKT+tUWcZcBqxbHQ==} + + workbox-strategies@7.4.0: + resolution: {integrity: sha512-T4hVqIi5A4mHi92+5EppMX3cLaVywDp8nsyUgJhOZxcfSV/eQofcOA6/EMo5rnTNmNTpw0rUgjAI6LaVullPpg==} + + workbox-streams@7.4.0: + resolution: {integrity: sha512-QHPBQrey7hQbnTs5GrEVoWz7RhHJXnPT+12qqWM378orDMo5VMJLCkCM1cnCk+8Eq92lccx/VgRZ7WAzZWbSLg==} + + workbox-sw@7.4.0: + resolution: {integrity: sha512-ltU+Kr3qWR6BtbdlMnCjobZKzeV1hN+S6UvDywBrwM19TTyqA03X66dzw1tEIdJvQ4lYKkBFox6IAEhoSEZ8Xw==} + + workbox-window@7.4.0: + resolution: {integrity: sha512-/bIYdBLAVsNR3v7gYGaV4pQW3M3kEPx5E8vDxGvxo6khTrGtSSCS7QiFKv9ogzBgZiy0OXLP9zO28U/1nF1mfw==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + wsl-utils@0.1.0: + resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==} + engines: {node: '>=18'} + + xdg-basedir@5.1.0: + resolution: {integrity: sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==} + engines: {node: '>=12'} + + xe-utils@3.7.9: + resolution: {integrity: sha512-LWH6M7g+TKX8P2fqGxDPgJQygiULhbMtpmfxTMxJTUjuUv9y2+I45UCcVLh5AnmJSxDV1xTxsq7G5P9Eid06JQ==} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yaml-eslint-parser@1.3.1: + resolution: {integrity: sha512-MdSgP9YA9QjtAO2+lt4O7V2bnH22LPnfeVLiQqjY3cOyn8dy/Ief8otjIe6SPPTK03nM7O3Yl0LTfWuF7l+9yw==} + engines: {node: ^14.17.0 || >=16.0.0} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} + engines: {node: '>=12.20'} + + yoctocolors@2.1.2: + resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} + engines: {node: '>=18'} + + youch-core@0.3.3: + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + + youch@4.1.0-beta.13: + resolution: {integrity: sha512-3+AG1Xvt+R7M7PSDudhbfbwiyveW6B8PLBIwTyEC598biEYIjHhC89i6DBEvR0EZUjGY3uGSnC429HpIa2Z09g==} + + zeebe-bpmn-moddle@1.11.0: + resolution: {integrity: sha512-v2PkIAjyZEnzuFHrm9ZhpbEGMgNjYZkUw+H17JxkA7Da+dcbPHbD7fWuBWSbPzNSCOyYYmrH+PL6wp9407ptMg==} + + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + + zip-stream@6.0.1: + resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} + engines: {node: '>= 14'} + + zod-defaults@0.1.3: + resolution: {integrity: sha512-Cp4UjuVfXqwaOx8A5LlM0IRBZs8B7AXgF+XOZWF8CjMDhsY/Jki+y7VS4adVtQj9NNSsAPwylUlJp9INuxPEnA==} + peerDependencies: + zod: ^3.23.8 + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + + zrender@6.0.0: + resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + + zx@8.8.5: + resolution: {integrity: sha512-SNgDF5L0gfN7FwVOdEFguY3orU5AkfFZm9B5YSHog/UDHv+lvmd82ZAsOenOkQixigwH2+yyH198AwNdKhj+RA==} + engines: {node: '>= 12.17.0'} + hasBin: true + +snapshots: + + '@algolia/abtesting@1.11.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/autocomplete-core@1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-plugin-algolia-insights': 1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0)(search-insights@2.17.3) + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0) + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + - search-insights + + '@algolia/autocomplete-plugin-algolia-insights@1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0) + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + - algoliasearch + + '@algolia/autocomplete-preset-algolia@1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0)': + dependencies: + '@algolia/autocomplete-shared': 1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0) + '@algolia/client-search': 5.45.0 + algoliasearch: 5.45.0 + + '@algolia/autocomplete-shared@1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0)': + dependencies: + '@algolia/client-search': 5.45.0 + algoliasearch: 5.45.0 + + '@algolia/client-abtesting@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/client-analytics@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/client-common@5.45.0': {} + + '@algolia/client-insights@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/client-personalization@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/client-query-suggestions@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/client-search@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/ingestion@1.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/monitoring@1.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/recommend@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + '@algolia/requester-browser-xhr@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + + '@algolia/requester-fetch@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + + '@algolia/requester-node-http@5.45.0': + dependencies: + '@algolia/client-common': 5.45.0 + + '@alloc/quick-lru@5.2.0': {} + + '@ant-design/colors@6.0.0': + dependencies: + '@ctrl/tinycolor': 4.2.0 + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons-vue@7.0.1(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@ant-design/colors': 6.0.0 + '@ant-design/icons-svg': 4.4.2 + vue: 3.5.25(typescript@5.9.3) + + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.5.0 + tinyexec: 1.0.2 + + '@antfu/utils@9.3.0': {} + + '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': + dependencies: + ajv: 8.17.1 + json-schema: 0.4.0 + jsonpointer: 5.0.1 + leven: 3.1.0 + + '@ast-grep/napi-darwin-arm64@0.39.9': + optional: true + + '@ast-grep/napi-darwin-x64@0.39.9': + optional: true + + '@ast-grep/napi-linux-arm64-gnu@0.39.9': + optional: true + + '@ast-grep/napi-linux-arm64-musl@0.39.9': + optional: true + + '@ast-grep/napi-linux-x64-gnu@0.39.9': + optional: true + + '@ast-grep/napi-linux-x64-musl@0.39.9': + optional: true + + '@ast-grep/napi-win32-arm64-msvc@0.39.9': + optional: true + + '@ast-grep/napi-win32-ia32-msvc@0.39.9': + optional: true + + '@ast-grep/napi-win32-x64-msvc@0.39.9': + optional: true + + '@ast-grep/napi@0.39.9': + optionalDependencies: + '@ast-grep/napi-darwin-arm64': 0.39.9 + '@ast-grep/napi-darwin-x64': 0.39.9 + '@ast-grep/napi-linux-arm64-gnu': 0.39.9 + '@ast-grep/napi-linux-arm64-musl': 0.39.9 + '@ast-grep/napi-linux-x64-gnu': 0.39.9 + '@ast-grep/napi-linux-x64-musl': 0.39.9 + '@ast-grep/napi-win32-arm64-msvc': 0.39.9 + '@ast-grep/napi-win32-ia32-msvc': 0.39.9 + '@ast-grep/napi-win32-x64-msvc': 0.39.9 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.28.5': {} + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.27.3': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.0 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/traverse': 7.28.5 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + regexpu-core: 6.4.0 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-member-expression-to-functions@7.28.5': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.27.1': + dependencies: + '@babel/types': 7.28.5 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-wrap-function': 7.28.3 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-member-expression-to-functions': 7.28.5 + '@babel/helper-optimise-call-expression': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + dependencies: + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.28.3': + dependencies: + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-decorators@7.28.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-decorators': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + + '@babel/plugin-syntax-decorators@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-block-scoping@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-globals': 7.28.0 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 + + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-exponentiation-operator@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-logical-assignment-operators@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-optional-chaining@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typescript@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.5) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.5) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-block-scoping': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.5) + '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-transform-exponentiation-operator': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-logical-assignment-operators': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-systemjs': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.5) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.5) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.5) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.5) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.5) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.5) + core-js-compat: 3.47.0 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.28.5 + esutils: 2.0.3 + + '@babel/preset-typescript@7.28.5(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + '@babel/runtime-corejs3@7.28.4': + dependencies: + core-js-pure: 3.47.0 + + '@babel/runtime@7.28.4': {} + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + + '@babel/traverse@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bpmn-io/cm-theme@0.1.0-alpha.2': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/view': 6.38.8 + '@lezer/highlight': 1.2.3 + + '@bpmn-io/diagram-js-ui@0.2.3': + dependencies: + htm: 3.1.1 + preact: 10.27.2 + + '@bpmn-io/extract-process-variables@0.8.0': + dependencies: + min-dash: 4.2.3 + + '@bpmn-io/feel-editor@1.12.1': + dependencies: + '@bpmn-io/feel-lint': 2.1.0 + '@bpmn-io/lang-feel': 2.4.0 + '@camunda/feel-builtins': 0.2.0 + '@codemirror/autocomplete': 6.20.0 + '@codemirror/commands': 6.10.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.2 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.8 + '@lezer/highlight': 1.2.3 + min-dom: 4.2.1 + + '@bpmn-io/feel-lint@1.4.0': + dependencies: + '@codemirror/language': 6.11.3 + lezer-feel: 1.9.0 + + '@bpmn-io/feel-lint@2.1.0': + dependencies: + '@bpmn-io/lezer-feel': 1.9.0 + '@codemirror/language': 6.11.3 + + '@bpmn-io/lang-feel@2.4.0': + dependencies: + '@bpmn-io/lezer-feel': 1.9.0 + '@codemirror/autocomplete': 6.20.0 + '@codemirror/language': 6.11.3 + '@lezer/common': 1.3.0 + + '@bpmn-io/lezer-feel@1.9.0': + dependencies: + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.3 + min-dash: 4.2.3 + + '@bpmn-io/properties-panel@3.34.0': + dependencies: + '@bpmn-io/feel-editor': 1.12.1 + '@carbon/icons': 11.70.0 + '@codemirror/view': 6.38.8 + classnames: 2.5.1 + feelers: 1.4.0 + focus-trap: 7.6.6 + min-dash: 4.2.3 + min-dom: 4.2.1 + + '@cacheable/memory@2.0.6': + dependencies: + '@cacheable/utils': 2.3.2 + '@keyv/bigmap': 1.3.0(keyv@5.5.4) + hookified: 1.13.0 + keyv: 5.5.4 + + '@cacheable/utils@2.3.2': + dependencies: + hashery: 1.2.0 + keyv: 5.5.4 + + '@camunda/feel-builtins@0.2.0': {} + + '@carbon/icons@11.70.0': + dependencies: + '@ibm/telemetry-js': 1.10.2 + + '@changesets/apply-release-plan@7.0.13': + dependencies: + '@changesets/config': 3.1.1 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.4 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.7.3 + + '@changesets/assemble-release-plan@6.0.9': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.7.3 + + '@changesets/changelog-git@0.2.1': + dependencies: + '@changesets/types': 6.1.0 + + '@changesets/changelog-github@0.5.1': + dependencies: + '@changesets/get-github-info': 0.6.0 + '@changesets/types': 6.1.0 + dotenv: 8.6.0 + transitivePeerDependencies: + - encoding + + '@changesets/cli@2.29.7(@types/node@24.10.1)': + dependencies: + '@changesets/apply-release-plan': 7.0.13 + '@changesets/assemble-release-plan': 6.0.9 + '@changesets/changelog-git': 0.2.1 + '@changesets/config': 3.1.1 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/get-release-plan': 4.0.13 + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.5 + '@changesets/should-skip-package': 0.1.2 + '@changesets/types': 6.1.0 + '@changesets/write': 0.4.0 + '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) + '@manypkg/get-packages': 1.1.3 + ansi-colors: 4.1.3 + ci-info: 3.9.0 + enquirer: 2.4.1 + fs-extra: 7.0.1 + mri: 1.2.0 + p-limit: 2.3.0 + package-manager-detector: 0.2.11 + picocolors: 1.1.1 + resolve-from: 5.0.0 + semver: 7.7.3 + spawndamnit: 3.0.1 + term-size: 2.2.1 + transitivePeerDependencies: + - '@types/node' + + '@changesets/config@3.1.1': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.3 + '@changesets/logger': 0.1.1 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.3': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.1 + semver: 7.7.3 + + '@changesets/get-github-info@0.6.0': + dependencies: + dataloader: 1.4.0 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@changesets/get-release-plan@4.0.13': + dependencies: + '@changesets/assemble-release-plan': 6.0.9 + '@changesets/config': 3.1.1 + '@changesets/pre': 2.0.2 + '@changesets/read': 0.6.5 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.4': + dependencies: + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 3.0.1 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.1 + + '@changesets/parse@0.4.1': + dependencies: + '@changesets/types': 6.1.0 + js-yaml: 3.14.2 + + '@changesets/pre@2.0.2': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.5': + dependencies: + '@changesets/git': 3.0.4 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.1 + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.1 + + '@changesets/should-skip-package@0.1.2': + dependencies: + '@changesets/types': 6.1.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.1.0': {} + + '@changesets/write@0.4.0': + dependencies: + '@changesets/types': 6.1.0 + fs-extra: 7.0.1 + human-id: 4.1.2 + prettier: 2.8.8 + + '@clack/core@0.5.0': + dependencies: + picocolors: 1.1.1 + sisteransi: 1.0.5 + + '@clack/prompts@0.11.0': + dependencies: + '@clack/core': 0.5.0 + picocolors: 1.1.1 + sisteransi: 1.0.5 + + '@cloudflare/kv-asset-handler@0.4.1': + dependencies: + mime: 3.0.0 + + '@codemirror/autocomplete@6.20.0': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.8 + '@lezer/common': 1.3.0 + + '@codemirror/commands@6.10.0': + dependencies: + '@codemirror/language': 6.11.3 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.8 + '@lezer/common': 1.3.0 + + '@codemirror/language@6.11.3': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.8 + '@lezer/common': 1.3.0 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.3 + style-mod: 4.1.3 + + '@codemirror/lint@6.9.2': + dependencies: + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.8 + crelt: 1.0.6 + + '@codemirror/state@6.5.2': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/view@6.38.8': + dependencies: + '@codemirror/state': 6.5.2 + crelt: 1.0.6 + style-mod: 4.1.3 + w3c-keyname: 2.2.8 + + '@commitlint/cli@19.8.1(@types/node@24.10.1)(typescript@5.9.3)': + dependencies: + '@commitlint/format': 19.8.1 + '@commitlint/lint': 19.8.1 + '@commitlint/load': 19.8.1(@types/node@24.10.1)(typescript@5.9.3) + '@commitlint/read': 19.8.1 + '@commitlint/types': 19.8.1 + tinyexec: 1.0.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + conventional-changelog-conventionalcommits: 7.0.2 + + '@commitlint/config-validator@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + ajv: 8.17.1 + + '@commitlint/ensure@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + + '@commitlint/execute-rule@19.8.1': {} + + '@commitlint/format@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + chalk: 5.6.2 + + '@commitlint/is-ignored@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + semver: 7.7.3 + + '@commitlint/lint@19.8.1': + dependencies: + '@commitlint/is-ignored': 19.8.1 + '@commitlint/parse': 19.8.1 + '@commitlint/rules': 19.8.1 + '@commitlint/types': 19.8.1 + + '@commitlint/load@19.8.1(@types/node@24.10.1)(typescript@5.9.3)': + dependencies: + '@commitlint/config-validator': 19.8.1 + '@commitlint/execute-rule': 19.8.1 + '@commitlint/resolve-extends': 19.8.1 + '@commitlint/types': 19.8.1 + chalk: 5.6.2 + cosmiconfig: 9.0.0(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.2.0(@types/node@24.10.1)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@19.8.1': {} + + '@commitlint/parse@19.8.1': + dependencies: + '@commitlint/types': 19.8.1 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@commitlint/read@19.8.1': + dependencies: + '@commitlint/top-level': 19.8.1 + '@commitlint/types': 19.8.1 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + tinyexec: 1.0.2 + + '@commitlint/resolve-extends@19.8.1': + dependencies: + '@commitlint/config-validator': 19.8.1 + '@commitlint/types': 19.8.1 + global-directory: 4.0.1 + import-meta-resolve: 4.2.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@19.8.1': + dependencies: + '@commitlint/ensure': 19.8.1 + '@commitlint/message': 19.8.1 + '@commitlint/to-lines': 19.8.1 + '@commitlint/types': 19.8.1 + + '@commitlint/to-lines@19.8.1': {} + + '@commitlint/top-level@19.8.1': + dependencies: + find-up: 7.0.0 + + '@commitlint/types@19.8.1': + dependencies: + '@types/conventional-commits-parser': 5.0.2 + chalk: 5.6.2 + + '@cspell/cspell-bundled-dicts@8.19.4': + dependencies: + '@cspell/dict-ada': 4.1.1 + '@cspell/dict-al': 1.1.1 + '@cspell/dict-aws': 4.0.16 + '@cspell/dict-bash': 4.2.2 + '@cspell/dict-companies': 3.2.7 + '@cspell/dict-cpp': 6.0.14 + '@cspell/dict-cryptocurrencies': 5.0.5 + '@cspell/dict-csharp': 4.0.7 + '@cspell/dict-css': 4.0.18 + '@cspell/dict-dart': 2.3.1 + '@cspell/dict-data-science': 2.0.12 + '@cspell/dict-django': 4.1.5 + '@cspell/dict-docker': 1.1.16 + '@cspell/dict-dotnet': 5.0.10 + '@cspell/dict-elixir': 4.0.8 + '@cspell/dict-en-common-misspellings': 2.1.8 + '@cspell/dict-en-gb': 1.1.33 + '@cspell/dict-en_us': 4.4.24 + '@cspell/dict-filetypes': 3.0.14 + '@cspell/dict-flutter': 1.1.1 + '@cspell/dict-fonts': 4.0.5 + '@cspell/dict-fsharp': 1.1.1 + '@cspell/dict-fullstack': 3.2.7 + '@cspell/dict-gaming-terms': 1.1.2 + '@cspell/dict-git': 3.0.7 + '@cspell/dict-golang': 6.0.24 + '@cspell/dict-google': 1.0.9 + '@cspell/dict-haskell': 4.0.6 + '@cspell/dict-html': 4.0.12 + '@cspell/dict-html-symbol-entities': 4.0.4 + '@cspell/dict-java': 5.0.12 + '@cspell/dict-julia': 1.1.1 + '@cspell/dict-k8s': 1.0.12 + '@cspell/dict-kotlin': 1.1.1 + '@cspell/dict-latex': 4.0.4 + '@cspell/dict-lorem-ipsum': 4.0.5 + '@cspell/dict-lua': 4.0.8 + '@cspell/dict-makefile': 1.0.5 + '@cspell/dict-markdown': 2.0.12(@cspell/dict-css@4.0.18)(@cspell/dict-html-symbol-entities@4.0.4)(@cspell/dict-html@4.0.12)(@cspell/dict-typescript@3.2.3) + '@cspell/dict-monkeyc': 1.0.11 + '@cspell/dict-node': 5.0.8 + '@cspell/dict-npm': 5.2.24 + '@cspell/dict-php': 4.1.0 + '@cspell/dict-powershell': 5.0.15 + '@cspell/dict-public-licenses': 2.0.15 + '@cspell/dict-python': 4.2.22 + '@cspell/dict-r': 2.1.1 + '@cspell/dict-ruby': 5.0.9 + '@cspell/dict-rust': 4.0.12 + '@cspell/dict-scala': 5.0.8 + '@cspell/dict-shell': 1.1.2 + '@cspell/dict-software-terms': 5.1.14 + '@cspell/dict-sql': 2.2.1 + '@cspell/dict-svelte': 1.0.7 + '@cspell/dict-swift': 2.0.6 + '@cspell/dict-terraform': 1.1.3 + '@cspell/dict-typescript': 3.2.3 + '@cspell/dict-vue': 3.0.5 + + '@cspell/cspell-json-reporter@8.19.4': + dependencies: + '@cspell/cspell-types': 8.19.4 + + '@cspell/cspell-pipe@8.19.4': {} + + '@cspell/cspell-resolver@8.19.4': + dependencies: + global-directory: 4.0.1 + + '@cspell/cspell-service-bus@8.19.4': {} + + '@cspell/cspell-types@8.19.4': {} + + '@cspell/dict-ada@4.1.1': {} + + '@cspell/dict-al@1.1.1': {} + + '@cspell/dict-aws@4.0.16': {} + + '@cspell/dict-bash@4.2.2': + dependencies: + '@cspell/dict-shell': 1.1.2 + + '@cspell/dict-companies@3.2.7': {} + + '@cspell/dict-cpp@6.0.14': {} + + '@cspell/dict-cryptocurrencies@5.0.5': {} + + '@cspell/dict-csharp@4.0.7': {} + + '@cspell/dict-css@4.0.18': {} + + '@cspell/dict-dart@2.3.1': {} + + '@cspell/dict-data-science@2.0.12': {} + + '@cspell/dict-django@4.1.5': {} + + '@cspell/dict-docker@1.1.16': {} + + '@cspell/dict-dotnet@5.0.10': {} + + '@cspell/dict-elixir@4.0.8': {} + + '@cspell/dict-en-common-misspellings@2.1.8': {} + + '@cspell/dict-en-gb@1.1.33': {} + + '@cspell/dict-en_us@4.4.24': {} + + '@cspell/dict-filetypes@3.0.14': {} + + '@cspell/dict-flutter@1.1.1': {} + + '@cspell/dict-fonts@4.0.5': {} + + '@cspell/dict-fsharp@1.1.1': {} + + '@cspell/dict-fullstack@3.2.7': {} + + '@cspell/dict-gaming-terms@1.1.2': {} + + '@cspell/dict-git@3.0.7': {} + + '@cspell/dict-golang@6.0.24': {} + + '@cspell/dict-google@1.0.9': {} + + '@cspell/dict-haskell@4.0.6': {} + + '@cspell/dict-html-symbol-entities@4.0.4': {} + + '@cspell/dict-html@4.0.12': {} + + '@cspell/dict-java@5.0.12': {} + + '@cspell/dict-julia@1.1.1': {} + + '@cspell/dict-k8s@1.0.12': {} + + '@cspell/dict-kotlin@1.1.1': {} + + '@cspell/dict-latex@4.0.4': {} + + '@cspell/dict-lorem-ipsum@4.0.5': {} + + '@cspell/dict-lua@4.0.8': {} + + '@cspell/dict-makefile@1.0.5': {} + + '@cspell/dict-markdown@2.0.12(@cspell/dict-css@4.0.18)(@cspell/dict-html-symbol-entities@4.0.4)(@cspell/dict-html@4.0.12)(@cspell/dict-typescript@3.2.3)': + dependencies: + '@cspell/dict-css': 4.0.18 + '@cspell/dict-html': 4.0.12 + '@cspell/dict-html-symbol-entities': 4.0.4 + '@cspell/dict-typescript': 3.2.3 + + '@cspell/dict-monkeyc@1.0.11': {} + + '@cspell/dict-node@5.0.8': {} + + '@cspell/dict-npm@5.2.24': {} + + '@cspell/dict-php@4.1.0': {} + + '@cspell/dict-powershell@5.0.15': {} + + '@cspell/dict-public-licenses@2.0.15': {} + + '@cspell/dict-python@4.2.22': + dependencies: + '@cspell/dict-data-science': 2.0.12 + + '@cspell/dict-r@2.1.1': {} + + '@cspell/dict-ruby@5.0.9': {} + + '@cspell/dict-rust@4.0.12': {} + + '@cspell/dict-scala@5.0.8': {} + + '@cspell/dict-shell@1.1.2': {} + + '@cspell/dict-software-terms@5.1.14': {} + + '@cspell/dict-sql@2.2.1': {} + + '@cspell/dict-svelte@1.0.7': {} + + '@cspell/dict-swift@2.0.6': {} + + '@cspell/dict-terraform@1.1.3': {} + + '@cspell/dict-typescript@3.2.3': {} + + '@cspell/dict-vue@3.0.5': {} + + '@cspell/dynamic-import@8.19.4': + dependencies: + '@cspell/url': 8.19.4 + import-meta-resolve: 4.2.0 + + '@cspell/filetypes@8.19.4': {} + + '@cspell/strong-weak-map@8.19.4': {} + + '@cspell/url@8.19.4': {} + + '@css-render/plugin-bem@0.15.14(css-render@0.15.14)': + dependencies: + css-render: 0.15.14 + + '@css-render/vue3-ssr@0.15.14(vue@3.5.25(typescript@5.9.3))': + dependencies: + vue: 3.5.25(typescript@5.9.3) + + '@csstools/cascade-layer-name-parser@2.0.5(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@3.0.4': {} + + '@csstools/media-query-list-parser@3.0.1(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/media-query-list-parser@4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/postcss-alpha-function@1.0.1(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-cascade-layers@5.0.2(postcss@8.5.6)': + dependencies: + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + '@csstools/postcss-color-function-display-p3-linear@1.0.1(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-color-function@4.0.12(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-color-mix-function@3.0.12(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-color-mix-variadic-function-arguments@1.0.2(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-content-alt-text@2.0.8(postcss@8.5.6)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-contrast-color-function@2.0.12(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-exponential-functions@2.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-font-format-keywords@4.0.0(postcss@8.5.6)': + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-gamut-mapping@2.0.11(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-gradients-interpolation-method@5.0.12(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-hwb-function@4.0.12(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-ic-unit@4.0.4(postcss@8.5.6)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-initial@2.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-is-pseudo-class@5.0.3(postcss@8.5.6)': + dependencies: + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + '@csstools/postcss-light-dark-function@2.0.11(postcss@8.5.6)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-logical-float-and-clear@3.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-overflow@2.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-overscroll-behavior@2.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-resize@3.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-logical-viewport-units@3.0.4(postcss@8.5.6)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-media-minmax@2.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + postcss: 8.5.6 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@3.0.5(postcss@8.5.6)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + postcss: 8.5.6 + + '@csstools/postcss-nested-calc@4.0.0(postcss@8.5.6)': + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-normalize-display-values@4.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-oklab-function@4.0.12(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-progressive-custom-properties@4.2.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-random-function@2.0.1(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-relative-color-syntax@3.0.12(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-scope-pseudo-class@4.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + '@csstools/postcss-sign-functions@1.1.4(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-stepped-value-functions@4.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-text-decoration-shorthand@4.0.3(postcss@8.5.6)': + dependencies: + '@csstools/color-helpers': 5.1.0 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-trigonometric-functions@4.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + + '@csstools/postcss-unset-value@4.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/selector-resolve-nested@3.1.0(postcss-selector-parser@7.1.0)': + dependencies: + postcss-selector-parser: 7.1.0 + + '@csstools/selector-specificity@5.0.0(postcss-selector-parser@7.1.0)': + dependencies: + postcss-selector-parser: 7.1.0 + + '@csstools/utilities@2.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@ctrl/tinycolor@4.2.0': {} + + '@docsearch/css@3.8.2': {} + + '@docsearch/js@3.8.2(@algolia/client-search@5.45.0)(search-insights@2.17.3)': + dependencies: + '@docsearch/react': 3.8.2(@algolia/client-search@5.45.0)(search-insights@2.17.3) + preact: 10.27.2 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/react' + - react + - react-dom + - search-insights + + '@docsearch/react@3.8.2(@algolia/client-search@5.45.0)(search-insights@2.17.3)': + dependencies: + '@algolia/autocomplete-core': 1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0)(search-insights@2.17.3) + '@algolia/autocomplete-preset-algolia': 1.17.7(@algolia/client-search@5.45.0)(algoliasearch@5.45.0) + '@docsearch/css': 3.8.2 + algoliasearch: 5.45.0 + optionalDependencies: + search-insights: 2.17.3 + transitivePeerDependencies: + - '@algolia/client-search' + + '@dual-bundle/import-meta-resolve@4.2.1': {} + + '@element-plus/icons-vue@2.3.2(vue@3.5.25(typescript@5.9.3))': + dependencies: + vue: 3.5.25(typescript@5.9.3) + + '@emnapi/core@1.7.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.7.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/hash@0.8.0': {} + + '@emotion/hash@0.9.2': {} + + '@emotion/unitless@0.8.1': {} + + '@es-joy/jsdoccomment@0.50.2': + dependencies: + '@types/estree': 1.0.8 + '@typescript-eslint/types': 8.48.0 + comment-parser: 1.4.1 + esquery: 1.6.0 + jsdoc-type-pratt-parser: 4.1.0 + + '@es-joy/jsdoccomment@0.76.0': + dependencies: + '@types/estree': 1.0.8 + '@typescript-eslint/types': 8.48.0 + comment-parser: 1.4.1 + esquery: 1.6.0 + jsdoc-type-pratt-parser: 6.10.0 + + '@es-joy/resolve.exports@1.2.0': {} + + '@esbuild/aix-ppc64@0.25.3': + optional: true + + '@esbuild/android-arm64@0.25.3': + optional: true + + '@esbuild/android-arm@0.25.3': + optional: true + + '@esbuild/android-x64@0.25.3': + optional: true + + '@esbuild/darwin-arm64@0.25.3': + optional: true + + '@esbuild/darwin-x64@0.25.3': + optional: true + + '@esbuild/freebsd-arm64@0.25.3': + optional: true + + '@esbuild/freebsd-x64@0.25.3': + optional: true + + '@esbuild/linux-arm64@0.25.3': + optional: true + + '@esbuild/linux-arm@0.25.3': + optional: true + + '@esbuild/linux-ia32@0.25.3': + optional: true + + '@esbuild/linux-loong64@0.25.3': + optional: true + + '@esbuild/linux-mips64el@0.25.3': + optional: true + + '@esbuild/linux-ppc64@0.25.3': + optional: true + + '@esbuild/linux-riscv64@0.25.3': + optional: true + + '@esbuild/linux-s390x@0.25.3': + optional: true + + '@esbuild/linux-x64@0.25.3': + optional: true + + '@esbuild/netbsd-arm64@0.25.3': + optional: true + + '@esbuild/netbsd-x64@0.25.3': + optional: true + + '@esbuild/openbsd-arm64@0.25.3': + optional: true + + '@esbuild/openbsd-x64@0.25.3': + optional: true + + '@esbuild/sunos-x64@0.25.3': + optional: true + + '@esbuild/win32-arm64@0.25.3': + optional: true + + '@esbuild/win32-ia32@0.25.3': + optional: true + + '@esbuild/win32-x64@0.25.3': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1(jiti@2.6.1))': + dependencies: + eslint: 9.39.1(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.1': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/utils@0.2.10': {} + + '@floating-ui/vue@1.1.9(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@floating-ui/dom': 1.7.4 + '@floating-ui/utils': 0.2.10 + vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@form-create/ant-design-vue@3.2.33(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@form-create/component-antdv-frame': 3.2.31 + '@form-create/component-antdv-group': 3.2.31 + '@form-create/component-antdv-upload': 3.2.31 + '@form-create/component-subform': 3.1.34 + '@form-create/core': 3.2.33(vue@3.5.25(typescript@5.9.3)) + '@form-create/utils': 3.2.31 + vue: 3.5.25(typescript@5.9.3) + + '@form-create/antd-designer@3.4.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@form-create/ant-design-vue': 3.2.33(vue@3.5.25(typescript@5.9.3)) + '@form-create/component-wangeditor': 3.2.14 + '@form-create/utils': 3.2.31 + ant-design-vue: 4.2.6(vue@3.5.25(typescript@5.9.3)) + codemirror: 6.65.7 + js-beautify: 1.15.4 + marked: 17.0.1 + signature_pad: 5.1.2 + vue: 3.5.25(typescript@5.9.3) + vuedraggable: 4.1.0(vue@3.5.25(typescript@5.9.3)) + + '@form-create/component-antdv-frame@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-antdv-group@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-antdv-upload@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-elm-checkbox@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-elm-frame@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-elm-group@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-elm-radio@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-elm-select@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-elm-tree@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-elm-upload@3.2.31': + dependencies: + '@form-create/utils': 3.2.31 + + '@form-create/component-subform@3.1.34': {} + + '@form-create/component-wangeditor@3.2.14': + dependencies: + wangeditor: 4.7.15 + + '@form-create/core@3.2.33(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@form-create/utils': 3.2.31 + vue: 3.5.25(typescript@5.9.3) + + '@form-create/designer@3.4.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@form-create/component-wangeditor': 3.2.14 + '@form-create/element-ui': 3.2.33(vue@3.5.25(typescript@5.9.3)) + '@form-create/utils': 3.2.31 + codemirror: 6.65.7 + element-plus: 2.11.8(vue@3.5.25(typescript@5.9.3)) + js-beautify: 1.15.4 + marked: 17.0.1 + signature_pad: 5.1.2 + vue: 3.5.25(typescript@5.9.3) + vuedraggable: 4.1.0(vue@3.5.25(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/composition-api' + + '@form-create/element-ui@3.2.33(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@form-create/component-elm-checkbox': 3.2.31 + '@form-create/component-elm-frame': 3.2.31 + '@form-create/component-elm-group': 3.2.31 + '@form-create/component-elm-radio': 3.2.31 + '@form-create/component-elm-select': 3.2.31 + '@form-create/component-elm-tree': 3.2.31 + '@form-create/component-elm-upload': 3.2.31 + '@form-create/component-subform': 3.1.34 + '@form-create/core': 3.2.33(vue@3.5.25(typescript@5.9.3)) + '@form-create/utils': 3.2.31 + vue: 3.5.25(typescript@5.9.3) + + '@form-create/utils@3.2.31': {} + + '@gera2ld/jsx-dom@2.2.2': + dependencies: + '@babel/runtime': 7.28.4 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@ibm/telemetry-js@1.10.2': {} + + '@iconify-json/logos@1.2.10': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/octicon@1.2.19': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/simple-icons@1.2.60': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify-json/vscode-icons@1.2.36': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/json@2.2.410': + dependencies: + '@iconify/types': 2.0.0 + pathe: 2.0.3 + + '@iconify/tailwind@1.2.0': + dependencies: + '@iconify/types': 2.0.0 + + '@iconify/types@2.0.0': {} + + '@iconify/utils@3.0.2': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@antfu/utils': 9.3.0 + '@iconify/types': 2.0.0 + debug: 4.4.3 + globals: 15.15.0 + kolorist: 1.8.0 + local-pkg: 1.1.2 + mlly: 1.8.0 + transitivePeerDependencies: + - supports-color + + '@iconify/vue@5.0.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@iconify/types': 2.0.0 + vue: 3.5.25(typescript@5.9.3) + + '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.0 + optionalDependencies: + '@types/node': 24.10.1 + + '@internationalized/date@3.10.0': + dependencies: + '@swc/helpers': 0.5.17 + + '@internationalized/number@3.6.5': + dependencies: + '@swc/helpers': 0.5.17 + + '@intlify/bundle-utils@10.0.1(vue-i18n@11.2.2(vue@3.5.25(typescript@5.9.3)))': + dependencies: + '@intlify/message-compiler': 11.2.2 + '@intlify/shared': 11.2.2 + acorn: 8.15.0 + escodegen: 2.1.0 + estree-walker: 2.0.2 + jsonc-eslint-parser: 2.4.1 + mlly: 1.8.0 + source-map-js: 1.2.1 + yaml-eslint-parser: 1.3.1 + optionalDependencies: + vue-i18n: 11.2.2(vue@3.5.25(typescript@5.9.3)) + + '@intlify/core-base@11.2.2': + dependencies: + '@intlify/message-compiler': 11.2.2 + '@intlify/shared': 11.2.2 + + '@intlify/message-compiler@11.2.2': + dependencies: + '@intlify/shared': 11.2.2 + source-map-js: 1.2.1 + + '@intlify/shared@11.2.2': {} + + '@intlify/unplugin-vue-i18n@6.0.8(@vue/compiler-dom@3.5.25)(eslint@9.39.1(jiti@2.6.1))(rollup@4.53.3)(typescript@5.9.3)(vue-i18n@11.2.2(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@intlify/bundle-utils': 10.0.1(vue-i18n@11.2.2(vue@3.5.25(typescript@5.9.3))) + '@intlify/shared': 11.2.2 + '@intlify/vue-i18n-extensions': 8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.25)(vue-i18n@11.2.2(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3)) + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + debug: 4.4.3 + fast-glob: 3.3.3 + js-yaml: 4.1.1 + json5: 2.2.3 + pathe: 1.1.2 + picocolors: 1.1.1 + source-map-js: 1.2.1 + unplugin: 1.16.1 + vue: 3.5.25(typescript@5.9.3) + optionalDependencies: + vue-i18n: 11.2.2(vue@3.5.25(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/compiler-dom' + - eslint + - rollup + - supports-color + - typescript + + '@intlify/vue-i18n-extensions@8.0.0(@intlify/shared@11.2.2)(@vue/compiler-dom@3.5.25)(vue-i18n@11.2.2(vue@3.5.25(typescript@5.9.3)))(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@babel/parser': 7.28.5 + optionalDependencies: + '@intlify/shared': 11.2.2 + '@vue/compiler-dom': 3.5.25 + vue: 3.5.25(typescript@5.9.3) + vue-i18n: 11.2.2(vue@3.5.25(typescript@5.9.3)) + + '@ioredis/commands@1.4.0': {} + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jspm/generator@2.7.6': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) + '@babel/preset-typescript': 7.28.5(@babel/core@7.28.5) + '@jspm/import-map': 1.2.2 + es-module-lexer: 1.7.0 + minimatch: 10.1.1 + node-fetch-cache: 5.1.0 + pako: 2.1.0 + sver: 1.8.4 + tar-stream: 3.1.7 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + - supports-color + + '@jspm/import-map@1.2.2': {} + + '@juggle/resize-observer@3.4.0': {} + + '@keyv/bigmap@1.3.0(keyv@5.5.4)': + dependencies: + hashery: 1.2.0 + hookified: 1.13.0 + keyv: 5.5.4 + + '@keyv/serialize@1.1.1': {} + + '@lezer/common@1.3.0': {} + + '@lezer/highlight@1.2.3': + dependencies: + '@lezer/common': 1.3.0 + + '@lezer/lr@1.4.3': + dependencies: + '@lezer/common': 1.3.0 + + '@lezer/markdown@1.6.0': + dependencies: + '@lezer/common': 1.3.0 + '@lezer/highlight': 1.2.3 + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.28.4 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/find-root@3.1.0': + dependencies: + '@manypkg/tools': 2.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.28.4 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@manypkg/get-packages@3.1.0': + dependencies: + '@manypkg/find-root': 3.1.0 + '@manypkg/tools': 2.1.0 + + '@manypkg/tools@2.1.0': + dependencies: + jju: 1.4.0 + js-yaml: 4.1.1 + tinyglobby: 0.2.15 + + '@mapbox/node-pre-gyp@2.0.3': + dependencies: + consola: 3.4.2 + detect-libc: 2.1.2 + https-proxy-agent: 7.0.6 + node-fetch: 2.7.0 + nopt: 8.1.0 + semver: 7.7.3 + tar: 7.5.2 + transitivePeerDependencies: + - encoding + - supports-color + + '@marijn/find-cluster-break@1.0.2': {} + + '@microsoft/api-extractor-model@7.32.1(@types/node@24.10.1)': + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.0 + '@rushstack/node-core-library': 5.19.0(@types/node@24.10.1) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.55.1(@types/node@24.10.1)': + dependencies: + '@microsoft/api-extractor-model': 7.32.1(@types/node@24.10.1) + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.0 + '@rushstack/node-core-library': 5.19.0(@types/node@24.10.1) + '@rushstack/rig-package': 0.6.0 + '@rushstack/terminal': 0.19.4(@types/node@24.10.1) + '@rushstack/ts-command-line': 5.1.4(@types/node@24.10.1) + diff: 8.0.2 + lodash: 4.17.21 + minimatch: 10.0.3 + resolve: 1.22.11 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/fetch-event-source@2.0.1': {} + + '@microsoft/tsdoc-config@0.18.0': + dependencies: + '@microsoft/tsdoc': 0.16.0 + ajv: 8.12.0 + jju: 1.4.0 + resolve: 1.22.11 + + '@microsoft/tsdoc@0.16.0': {} + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@nolebase/ui@2.18.2(vitepress@1.6.4(@algolia/client-search@5.45.0)(@types/node@24.10.1)(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(less@4.4.2)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.94.2)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.44.1)(typescript@5.9.3))(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@iconify-json/octicon': 1.2.19 + less: 4.4.2 + vitepress: 1.6.4(@algolia/client-search@5.45.0)(@types/node@24.10.1)(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(less@4.4.2)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.94.2)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.44.1)(typescript@5.9.3) + vue: 3.5.25(typescript@5.9.3) + + '@nolebase/vitepress-plugin-git-changelog@2.18.2(vitepress@1.6.4(@algolia/client-search@5.45.0)(@types/node@24.10.1)(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(less@4.4.2)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.94.2)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.44.1)(typescript@5.9.3))(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@iconify-json/octicon': 1.2.19 + '@nolebase/ui': 2.18.2(vitepress@1.6.4(@algolia/client-search@5.45.0)(@types/node@24.10.1)(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(less@4.4.2)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.94.2)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.44.1)(typescript@5.9.3))(vue@3.5.25(typescript@5.9.3)) + colorette: 2.0.20 + date-fns: 4.1.0 + defu: 6.1.4 + es-toolkit: 1.42.0 + execa: 9.6.0 + globby: 14.1.0 + gray-matter: 4.0.3 + less: 4.4.2 + uncrypto: 0.1.3 + vitepress: 1.6.4(@algolia/client-search@5.45.0)(@types/node@24.10.1)(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(less@4.4.2)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.94.2)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.44.1)(typescript@5.9.3) + transitivePeerDependencies: + - vue + + '@npmcli/fs@5.0.0': + dependencies: + semver: 7.7.3 + + '@nuxt/kit@3.20.1(magicast@0.5.1)': + dependencies: + c12: 3.3.2(magicast@0.5.1) + consola: 3.4.2 + defu: 6.1.4 + destr: 2.0.5 + errx: 0.1.0 + exsolve: 1.0.8 + ignore: 7.0.5 + jiti: 2.6.1 + klona: 2.0.6 + knitwork: 1.3.0 + mlly: 1.8.0 + ohash: 2.0.11 + pathe: 2.0.3 + pkg-types: 2.3.0 + rc9: 2.1.2 + scule: 1.3.0 + semver: 7.7.3 + tinyglobby: 0.2.15 + ufo: 1.6.1 + unctx: 2.4.1 + untyped: 2.0.0 + transitivePeerDependencies: + - magicast + optional: true + + '@nuxt/kit@4.2.1(magicast@0.5.1)': + dependencies: + c12: 3.3.2(magicast@0.5.1) + consola: 3.4.2 + defu: 6.1.4 + destr: 2.0.5 + errx: 0.1.0 + exsolve: 1.0.8 + ignore: 7.0.5 + jiti: 2.6.1 + klona: 2.0.6 + mlly: 1.8.0 + ohash: 2.0.11 + pathe: 2.0.3 + pkg-types: 2.3.0 + rc9: 2.1.2 + scule: 1.3.0 + semver: 7.7.3 + tinyglobby: 0.2.15 + ufo: 1.6.1 + unctx: 2.4.1 + untyped: 2.0.0 + transitivePeerDependencies: + - magicast + + '@one-ini/wasm@0.1.1': {} + + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-wasm@2.5.1': + dependencies: + is-glob: 4.0.3 + micromatch: 4.0.8 + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.2.9': {} + + '@playwright/test@1.57.0': + dependencies: + playwright: 1.57.0 + + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/constants@1001.3.1': {} + + '@pnpm/error@1000.0.5': + dependencies: + '@pnpm/constants': 1001.3.1 + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@2.3.1': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + + '@pnpm/types@1001.0.1': {} + + '@pnpm/workspace.read-manifest@1000.2.7': + dependencies: + '@pnpm/constants': 1001.3.1 + '@pnpm/error': 1000.0.5 + '@pnpm/types': 1001.0.1 + read-yaml-file: 2.1.0 + + '@polka/url@1.0.0-next.29': {} + + '@popperjs/core@2.11.8': {} + + '@poppinss/colors@4.1.5': + dependencies: + kleur: 4.1.5 + + '@poppinss/dumper@0.6.5': + dependencies: + '@poppinss/colors': 4.1.5 + '@sindresorhus/is': 7.1.1 + supports-color: 10.2.2 + + '@poppinss/exception@1.2.2': {} + + '@publint/pack@0.1.2': {} + + '@rolldown/pluginutils@1.0.0-beta.50': {} + + '@rolldown/pluginutils@1.0.0-beta.52': {} + + '@rollup/plugin-alias@5.1.1(rollup@4.53.3)': + optionalDependencies: + rollup: 4.53.3 + + '@rollup/plugin-babel@5.3.1(@babel/core@7.28.5)(rollup@2.79.2)': + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + rollup: 2.79.2 + transitivePeerDependencies: + - supports-color + + '@rollup/plugin-commonjs@28.0.9(rollup@4.53.3)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.5.0(picomatch@4.0.3) + is-reference: 1.2.1 + magic-string: 0.30.21 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.53.3 + + '@rollup/plugin-inject@5.0.5(rollup@4.53.3)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + estree-walker: 2.0.2 + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.53.3 + + '@rollup/plugin-json@6.1.0(rollup@4.53.3)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + optionalDependencies: + rollup: 4.53.3 + + '@rollup/plugin-node-resolve@15.3.1(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@2.79.2) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.11 + optionalDependencies: + rollup: 2.79.2 + + '@rollup/plugin-node-resolve@16.0.3(rollup@4.53.3)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-module: 1.0.0 + resolve: 1.22.11 + optionalDependencies: + rollup: 4.53.3 + + '@rollup/plugin-replace@2.4.2(rollup@2.79.2)': + dependencies: + '@rollup/pluginutils': 3.1.0(rollup@2.79.2) + magic-string: 0.25.9 + rollup: 2.79.2 + + '@rollup/plugin-replace@6.0.3(rollup@4.53.3)': + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + magic-string: 0.30.21 + optionalDependencies: + rollup: 4.53.3 + + '@rollup/plugin-terser@0.4.4(rollup@2.79.2)': + dependencies: + serialize-javascript: 6.0.2 + smob: 1.5.0 + terser: 5.44.1 + optionalDependencies: + rollup: 2.79.2 + + '@rollup/plugin-terser@0.4.4(rollup@4.53.3)': + dependencies: + serialize-javascript: 6.0.2 + smob: 1.5.0 + terser: 5.44.1 + optionalDependencies: + rollup: 4.53.3 + + '@rollup/pluginutils@3.1.0(rollup@2.79.2)': + dependencies: + '@types/estree': 0.0.39 + estree-walker: 1.0.1 + picomatch: 2.3.1 + rollup: 2.79.2 + + '@rollup/pluginutils@4.2.1': + dependencies: + estree-walker: 2.0.2 + picomatch: 2.3.1 + + '@rollup/pluginutils@5.3.0(rollup@2.79.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 2.79.2 + + '@rollup/pluginutils@5.3.0(rollup@4.53.3)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.53.3 + + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + + '@rollup/rollup-android-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + + '@rushstack/node-core-library@5.19.0(@types/node@24.10.1)': + dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1(ajv@8.13.0) + fs-extra: 11.3.2 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.5.4 + optionalDependencies: + '@types/node': 24.10.1 + + '@rushstack/problem-matcher@0.1.1(@types/node@24.10.1)': + optionalDependencies: + '@types/node': 24.10.1 + + '@rushstack/rig-package@0.6.0': + dependencies: + resolve: 1.22.11 + strip-json-comments: 3.1.1 + + '@rushstack/terminal@0.19.4(@types/node@24.10.1)': + dependencies: + '@rushstack/node-core-library': 5.19.0(@types/node@24.10.1) + '@rushstack/problem-matcher': 0.1.1(@types/node@24.10.1) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 24.10.1 + + '@rushstack/ts-command-line@5.1.4(@types/node@24.10.1)': + dependencies: + '@rushstack/terminal': 0.19.4(@types/node@24.10.1) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + + '@sec-ant/readable-stream@0.4.1': {} + + '@shikijs/core@2.5.0': + dependencies: + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + + '@shikijs/engine-javascript@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + oniguruma-to-es: 3.1.1 + + '@shikijs/engine-oniguruma@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + + '@shikijs/langs@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/themes@2.5.0': + dependencies: + '@shikijs/types': 2.5.0 + + '@shikijs/transformers@2.5.0': + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/types': 2.5.0 + + '@shikijs/types@2.5.0': + dependencies: + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + '@shikijs/vscode-textmate@10.0.2': {} + + '@simonwep/pickr@1.8.2': + dependencies: + core-js: 3.47.0 + nanopop: 2.4.2 + + '@sindresorhus/base62@1.0.0': {} + + '@sindresorhus/is@7.1.1': {} + + '@sindresorhus/merge-streams@2.3.0': {} + + '@sindresorhus/merge-streams@4.0.0': {} + + '@speed-highlight/core@1.2.12': {} + + '@stylistic/stylelint-plugin@3.1.3(stylelint@16.26.0(typescript@5.9.3))': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 3.0.1(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + is-plain-object: 5.0.0 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + postcss-value-parser: 4.2.0 + style-search: 0.1.0 + stylelint: 16.26.0(typescript@5.9.3) + + '@surma/rollup-plugin-off-main-thread@2.2.3': + dependencies: + ejs: 3.1.10 + json5: 2.2.3 + magic-string: 0.25.9 + string.prototype.matchall: 4.0.12 + + '@svelte-put/shortcut@4.1.0(svelte@5.45.2)': + dependencies: + svelte: 5.45.2 + + '@sveltejs/acorn-typescript@1.0.7(acorn@8.15.0)': + dependencies: + acorn: 8.15.0 + + '@swc/helpers@0.5.17': + dependencies: + tslib: 2.8.1 + + '@sxzz/popperjs-es@2.11.7': {} + + '@tailwindcss/nesting@0.0.0-insiders.565cd3e(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-nested: 5.0.6(postcss@8.5.6) + + '@tailwindcss/typography@0.5.19(tailwindcss@3.4.18(yaml@2.8.1))': + dependencies: + postcss-selector-parser: 6.0.10 + tailwindcss: 3.4.18(yaml@2.8.1) + + '@tanstack/store@0.8.0': {} + + '@tanstack/virtual-core@3.13.12': {} + + '@tanstack/vue-store@0.8.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@tanstack/store': 0.8.0 + vue: 3.5.25(typescript@5.9.3) + vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3)) + + '@tanstack/vue-virtual@3.13.12(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@tanstack/virtual-core': 3.13.12 + vue: 3.5.25(typescript@5.9.3) + + '@tinyflow-ai/ui@1.1.9(svelte@5.45.2)': + dependencies: + '@floating-ui/dom': 1.7.4 + '@xyflow/svelte': 1.4.2(svelte@5.45.2) + transitivePeerDependencies: + - svelte + + '@tinyflow-ai/vue@1.1.9(svelte@5.45.2)(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@tinyflow-ai/ui': 1.1.9(svelte@5.45.2) + vue: 3.5.25(typescript@5.9.3) + transitivePeerDependencies: + - svelte + + '@tinymce/tinymce-vue@6.3.0(tinymce@7.9.1)(vue@3.5.25(typescript@5.9.3))': + dependencies: + vue: 3.5.25(typescript@5.9.3) + optionalDependencies: + tinymce: 7.9.1 + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/archiver@6.0.4': + dependencies: + '@types/readdir-glob': 1.1.5 + + '@types/argparse@1.0.38': {} + + '@types/bintrees@1.0.6': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/codemirror@5.60.17': + dependencies: + '@types/tern': 0.23.9 + + '@types/conventional-commits-parser@5.0.2': + dependencies: + '@types/node': 24.10.1 + + '@types/crypto-js@4.2.2': {} + + '@types/d3-array@3.2.2': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + + '@types/deep-eql@4.0.2': {} + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@0.0.39': {} + + '@types/estree@1.0.8': {} + + '@types/geojson@7946.0.16': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/html-minifier-terser@7.0.2': {} + + '@types/json-schema@7.0.15': {} + + '@types/katex@0.16.7': {} + + '@types/linkify-it@5.0.0': {} + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.21 + + '@types/lodash.clonedeep@4.5.9': + dependencies: + '@types/lodash': 4.17.21 + + '@types/lodash.get@4.4.9': + dependencies: + '@types/lodash': 4.17.21 + + '@types/lodash.isequal@4.5.8': + dependencies: + '@types/lodash': 4.17.21 + + '@types/lodash.set@4.3.9': + dependencies: + '@types/lodash': 4.17.21 + + '@types/lodash@4.17.21': {} + + '@types/markdown-it@14.1.2': + dependencies: + '@types/linkify-it': 5.0.0 + '@types/mdurl': 2.0.0 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdurl@2.0.0': {} + + '@types/minimatch@3.0.5': {} + + '@types/node@12.20.55': {} + + '@types/node@24.10.1': + dependencies: + undici-types: 7.16.0 + + '@types/nprogress@0.2.3': {} + + '@types/parse-json@4.0.2': {} + + '@types/postcss-import@14.0.3': + dependencies: + postcss: 8.5.6 + + '@types/qrcode@1.5.6': + dependencies: + '@types/node': 24.10.1 + + '@types/qs@6.14.0': {} + + '@types/readdir-glob@1.1.5': + dependencies: + '@types/node': 24.10.1 + + '@types/resolve@1.20.2': {} + + '@types/sortablejs@1.15.9': {} + + '@types/tern@0.23.9': + dependencies: + '@types/estree': 1.0.8 + + '@types/tinycolor2@1.4.6': {} + + '@types/trusted-types@2.0.7': {} + + '@types/unist@3.0.3': {} + + '@types/validator@13.15.10': {} + + '@types/video.js@7.3.58': {} + + '@types/web-bluetooth@0.0.16': {} + + '@types/web-bluetooth@0.0.21': {} + + '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.0 + eslint: 9.39.1(jiti@2.6.1) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.48.0 + debug: 4.4.3 + eslint: 9.39.1(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.48.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) + '@typescript-eslint/types': 8.48.0 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + + '@typescript-eslint/scope-manager@8.48.0': + dependencies: + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/visitor-keys': 8.48.0 + + '@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.1(jiti@2.6.1) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@7.18.0': {} + + '@typescript-eslint/types@8.48.0': {} + + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.3 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.3 + ts-api-utils: 1.4.3(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.48.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/visitor-keys': 8.48.0 + debug: 4.4.3 + minimatch: 9.0.5 + semver: 7.7.3 + tinyglobby: 0.2.15 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.9.3) + eslint: 9.39.1(jiti@2.6.1) + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.48.0 + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) + eslint: 9.39.1(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + eslint-visitor-keys: 3.4.3 + + '@typescript-eslint/visitor-keys@8.48.0': + dependencies: + '@typescript-eslint/types': 8.48.0 + eslint-visitor-keys: 4.2.1 + + '@ungap/structured-clone@1.3.0': {} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@vee-validate/zod@4.15.1(vue@3.5.25(typescript@5.9.3))(zod@3.25.76)': + dependencies: + type-fest: 4.41.0 + vee-validate: 4.15.1(vue@3.5.25(typescript@5.9.3)) + zod: 3.25.76 + transitivePeerDependencies: + - vue + + '@vercel/nft@0.30.4(rollup@4.53.3)': + dependencies: + '@mapbox/node-pre-gyp': 2.0.3 + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + acorn: 8.15.0 + acorn-import-attributes: 1.9.5(acorn@8.15.0) + async-sema: 3.1.1 + bindings: 1.5.0 + estree-walker: 2.0.2 + glob: 10.5.0 + graceful-fs: 4.2.11 + node-gyp-build: 4.8.4 + picomatch: 4.0.3 + resolve-from: 5.0.0 + transitivePeerDependencies: + - encoding + - rollup + - supports-color + + '@videojs-player/vue@1.0.0(@types/video.js@7.3.58)(video.js@7.21.7)(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@types/video.js': 7.3.58 + video.js: 7.21.7 + vue: 3.5.25(typescript@5.9.3) + + '@videojs/http-streaming@2.16.3(video.js@7.21.7)': + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/vhs-utils': 3.0.5 + aes-decrypter: 3.1.3 + global: 4.4.0 + m3u8-parser: 4.8.0 + mpd-parser: 0.22.1 + mux.js: 6.0.1 + video.js: 7.21.7 + + '@videojs/vhs-utils@3.0.5': + dependencies: + '@babel/runtime': 7.28.4 + global: 4.4.0 + url-toolkit: 2.2.5 + + '@videojs/xhr@2.6.0': + dependencies: + '@babel/runtime': 7.28.4 + global: 4.4.0 + is-function: 1.0.2 + + '@vite-pwa/vitepress@1.0.1(vite-plugin-pwa@1.1.0(vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1))(workbox-build@7.4.0)(workbox-window@7.4.0))': + dependencies: + vite-plugin-pwa: 1.1.0(vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1))(workbox-build@7.4.0)(workbox-window@7.4.0) + + '@vitejs/plugin-vue-jsx@5.1.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + '@rolldown/pluginutils': 1.0.0-beta.52 + '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.28.5) + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vue: 3.5.25(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@5.2.4(vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1))(vue@3.5.25(typescript@5.9.3))': + dependencies: + vite: 5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1) + vue: 3.5.25(typescript@5.9.3) + + '@vitejs/plugin-vue@6.0.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.50 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vue: 3.5.25(typescript@5.9.3) + + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + '@volar/language-core@2.4.23': + dependencies: + '@volar/source-map': 2.4.23 + + '@volar/language-core@2.4.26': + dependencies: + '@volar/source-map': 2.4.26 + + '@volar/source-map@2.4.23': {} + + '@volar/source-map@2.4.26': {} + + '@volar/typescript@2.4.23': + dependencies: + '@volar/language-core': 2.4.23 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@volar/typescript@2.4.26': + dependencies: + '@volar/language-core': 2.4.26 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/babel-helper-vue-transform-on@1.5.0': {} + + '@vue/babel-helper-vue-transform-on@2.0.1': {} + + '@vue/babel-plugin-jsx@1.5.0(@babel/core@7.28.5)': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@vue/babel-helper-vue-transform-on': 1.5.0 + '@vue/babel-plugin-resolve-type': 1.5.0(@babel/core@7.28.5) + '@vue/shared': 3.5.25 + optionalDependencies: + '@babel/core': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.28.5)': + dependencies: + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@vue/babel-helper-vue-transform-on': 2.0.1 + '@vue/babel-plugin-resolve-type': 2.0.1(@babel/core@7.28.5) + '@vue/shared': 3.5.25 + optionalDependencies: + '@babel/core': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@1.5.0(@babel/core@7.28.5)': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/parser': 7.28.5 + '@vue/compiler-sfc': 3.5.25 + transitivePeerDependencies: + - supports-color + + '@vue/babel-plugin-resolve-type@2.0.1(@babel/core@7.28.5)': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/parser': 7.28.5 + '@vue/compiler-sfc': 3.5.25 + transitivePeerDependencies: + - supports-color + + '@vue/compiler-core@3.5.25': + dependencies: + '@babel/parser': 7.28.5 + '@vue/shared': 3.5.25 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.25': + dependencies: + '@vue/compiler-core': 3.5.25 + '@vue/shared': 3.5.25 + + '@vue/compiler-sfc@3.5.25': + dependencies: + '@babel/parser': 7.28.5 + '@vue/compiler-core': 3.5.25 + '@vue/compiler-dom': 3.5.25 + '@vue/compiler-ssr': 3.5.25 + '@vue/shared': 3.5.25 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.5.25': + dependencies: + '@vue/compiler-dom': 3.5.25 + '@vue/shared': 3.5.25 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/devtools-api@6.6.4': {} + + '@vue/devtools-api@7.7.9': + dependencies: + '@vue/devtools-kit': 7.7.9 + + '@vue/devtools-core@8.0.5(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@vue/devtools-kit': 8.0.5 + '@vue/devtools-shared': 8.0.5 + mitt: 3.0.1 + nanoid: 5.1.6 + pathe: 2.0.3 + vite-hot-client: 2.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + vue: 3.5.25(typescript@5.9.3) + transitivePeerDependencies: + - vite + + '@vue/devtools-kit@7.7.9': + dependencies: + '@vue/devtools-shared': 7.7.9 + birpc: 2.8.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 1.0.0 + speakingurl: 14.0.1 + superjson: 2.2.5 + + '@vue/devtools-kit@8.0.5': + dependencies: + '@vue/devtools-shared': 8.0.5 + birpc: 2.8.0 + hookable: 5.5.3 + mitt: 3.0.1 + perfect-debounce: 2.0.0 + speakingurl: 14.0.1 + superjson: 2.2.5 + + '@vue/devtools-shared@7.7.9': + dependencies: + rfdc: 1.4.1 + + '@vue/devtools-shared@8.0.5': + dependencies: + rfdc: 1.4.1 + + '@vue/language-core@2.2.0(typescript@5.9.3)': + dependencies: + '@volar/language-core': 2.4.26 + '@vue/compiler-dom': 3.5.25 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.25 + alien-signals: 0.4.14 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.9.3 + + '@vue/language-core@3.1.5(typescript@5.9.3)': + dependencies: + '@volar/language-core': 2.4.23 + '@vue/compiler-dom': 3.5.25 + '@vue/shared': 3.5.25 + alien-signals: 3.1.1 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.3 + optionalDependencies: + typescript: 5.9.3 + + '@vue/reactivity@3.5.25': + dependencies: + '@vue/shared': 3.5.25 + + '@vue/runtime-core@3.5.25': + dependencies: + '@vue/reactivity': 3.5.25 + '@vue/shared': 3.5.25 + + '@vue/runtime-dom@3.5.25': + dependencies: + '@vue/reactivity': 3.5.25 + '@vue/runtime-core': 3.5.25 + '@vue/shared': 3.5.25 + csstype: 3.2.3 + + '@vue/server-renderer@3.5.25(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.5.25 + '@vue/shared': 3.5.25 + vue: 3.5.25(typescript@5.9.3) + + '@vue/shared@3.5.25': {} + + '@vue/test-utils@2.4.6': + dependencies: + js-beautify: 1.15.4 + vue-component-type-helpers: 2.2.12 + + '@vueuse/core@12.8.2(typescript@5.9.3)': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 12.8.2 + '@vueuse/shared': 12.8.2(typescript@5.9.3) + vue: 3.5.25(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + '@vueuse/core@13.9.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 13.9.0 + '@vueuse/shared': 13.9.0(vue@3.5.25(typescript@5.9.3)) + vue: 3.5.25(typescript@5.9.3) + + '@vueuse/core@14.1.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@types/web-bluetooth': 0.0.21 + '@vueuse/metadata': 14.1.0 + '@vueuse/shared': 14.1.0(vue@3.5.25(typescript@5.9.3)) + vue: 3.5.25(typescript@5.9.3) + + '@vueuse/core@9.13.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@types/web-bluetooth': 0.0.16 + '@vueuse/metadata': 9.13.0 + '@vueuse/shared': 9.13.0(vue@3.5.25(typescript@5.9.3)) + vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vueuse/integrations@12.8.2(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(focus-trap@7.6.6)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.6)(typescript@5.9.3)': + dependencies: + '@vueuse/core': 12.8.2(typescript@5.9.3) + '@vueuse/shared': 12.8.2(typescript@5.9.3) + vue: 3.5.25(typescript@5.9.3) + optionalDependencies: + async-validator: 4.2.5 + axios: 1.13.2 + change-case: 5.4.4 + focus-trap: 7.6.6 + nprogress: 0.2.0 + qrcode: 1.5.4 + sortablejs: 1.15.6 + transitivePeerDependencies: + - typescript + + '@vueuse/integrations@14.1.0(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(focus-trap@7.6.6)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.6)(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@vueuse/core': 14.1.0(vue@3.5.25(typescript@5.9.3)) + '@vueuse/shared': 14.1.0(vue@3.5.25(typescript@5.9.3)) + vue: 3.5.25(typescript@5.9.3) + optionalDependencies: + async-validator: 4.2.5 + axios: 1.13.2 + change-case: 5.4.4 + focus-trap: 7.6.6 + nprogress: 0.2.0 + qrcode: 1.5.4 + sortablejs: 1.15.6 + + '@vueuse/metadata@12.8.2': {} + + '@vueuse/metadata@13.9.0': {} + + '@vueuse/metadata@14.1.0': {} + + '@vueuse/metadata@9.13.0': {} + + '@vueuse/motion@3.0.3(magicast@0.5.1)(vue@3.5.25(typescript@5.9.3))': + dependencies: + '@vueuse/core': 13.9.0(vue@3.5.25(typescript@5.9.3)) + '@vueuse/shared': 13.9.0(vue@3.5.25(typescript@5.9.3)) + defu: 6.1.4 + framesync: 6.1.2 + popmotion: 11.0.5 + style-value-types: 5.1.2 + vue: 3.5.25(typescript@5.9.3) + optionalDependencies: + '@nuxt/kit': 3.20.1(magicast@0.5.1) + transitivePeerDependencies: + - magicast + + '@vueuse/shared@12.8.2(typescript@5.9.3)': + dependencies: + vue: 3.5.25(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + '@vueuse/shared@13.9.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + vue: 3.5.25(typescript@5.9.3) + + '@vueuse/shared@14.1.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + vue: 3.5.25(typescript@5.9.3) + + '@vueuse/shared@9.13.0(vue@3.5.25(typescript@5.9.3))': + dependencies: + vue-demi: 0.14.10(vue@3.5.25(typescript@5.9.3)) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + + '@vxe-ui/core@4.2.15(vue@3.5.25(typescript@5.9.3))': + dependencies: + dom-zindex: 1.0.6 + vue: 3.5.25(typescript@5.9.3) + xe-utils: 3.7.9 + + '@xmldom/xmldom@0.8.11': {} + + '@xyflow/svelte@1.4.2(svelte@5.45.2)': + dependencies: + '@svelte-put/shortcut': 4.1.0(svelte@5.45.2) + '@xyflow/system': 0.0.73 + svelte: 5.45.2 + + '@xyflow/system@0.0.73': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + abbrev@2.0.0: {} + + abbrev@3.0.1: {} + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-import-attributes@1.9.5(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + aes-decrypter@3.1.3: + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/vhs-utils': 3.0.5 + global: 4.4.0 + pkcs7: 1.0.4 + + agent-base@7.1.4: {} + + ajv-draft-04@1.0.0(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + + ajv-formats@3.0.1(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.12.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + ajv@8.13.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + algoliasearch@5.45.0: + dependencies: + '@algolia/abtesting': 1.11.0 + '@algolia/client-abtesting': 5.45.0 + '@algolia/client-analytics': 5.45.0 + '@algolia/client-common': 5.45.0 + '@algolia/client-insights': 5.45.0 + '@algolia/client-personalization': 5.45.0 + '@algolia/client-query-suggestions': 5.45.0 + '@algolia/client-search': 5.45.0 + '@algolia/ingestion': 1.45.0 + '@algolia/monitoring': 1.45.0 + '@algolia/recommend': 5.45.0 + '@algolia/requester-browser-xhr': 5.45.0 + '@algolia/requester-fetch': 5.45.0 + '@algolia/requester-node-http': 5.45.0 + + alien-signals@0.4.14: {} + + alien-signals@3.1.1: {} + + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + + ansi-colors@4.1.3: {} + + ansi-escapes@7.2.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + + ansis@4.2.0: {} + + ant-design-vue@4.2.6(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@ant-design/colors': 6.0.0 + '@ant-design/icons-vue': 7.0.1(vue@3.5.25(typescript@5.9.3)) + '@babel/runtime': 7.28.4 + '@ctrl/tinycolor': 4.2.0 + '@emotion/hash': 0.9.2 + '@emotion/unitless': 0.8.1 + '@simonwep/pickr': 1.8.2 + array-tree-filter: 2.1.0 + async-validator: 4.2.5 + csstype: 3.2.3 + dayjs: 1.11.19 + dom-align: 1.12.4 + dom-scroll-into-view: 2.0.1 + lodash: 4.17.21 + lodash-es: 4.17.21 + resize-observer-polyfill: 1.5.1 + scroll-into-view-if-needed: 2.2.31 + shallow-equal: 1.2.1 + stylis: 4.3.6 + throttle-debounce: 5.0.2 + vue: 3.5.25(typescript@5.9.3) + vue-types: 3.0.2(vue@3.5.25(typescript@5.9.3)) + warning: 4.0.3 + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + archiver-utils@5.0.2: + dependencies: + glob: 10.5.0 + graceful-fs: 4.2.11 + is-stream: 2.0.1 + lazystream: 1.0.1 + lodash: 4.17.21 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + archiver@7.0.1: + dependencies: + archiver-utils: 5.0.2 + async: 3.2.6 + buffer-crc32: 1.0.0 + readable-stream: 4.7.0 + readdir-glob: 1.1.3 + tar-stream: 3.1.7 + zip-stream: 6.0.1 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + are-docs-informative@0.0.2: {} + + arg@5.0.2: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-differ@3.0.0: {} + + array-ify@1.0.0: {} + + array-move@4.0.0: {} + + array-timsort@1.0.3: {} + + array-tree-filter@2.1.0: {} + + array-union@2.1.0: {} + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + arrify@2.0.1: {} + + assertion-error@2.0.1: {} + + astral-regex@2.0.0: {} + + async-function@1.0.0: {} + + async-sema@3.1.1: {} + + async-validator@4.2.5: {} + + async@3.2.6: {} + + asynckit@0.4.0: {} + + at-least-node@1.0.0: {} + + atomically@2.1.0: + dependencies: + stubborn-fs: 2.0.0 + when-exit: 2.1.5 + + autolinker@3.16.2: + dependencies: + tslib: 2.8.1 + + autoprefixer@10.4.22(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + caniuse-lite: 1.0.30001757 + fraction.js: 5.3.4 + normalize-range: 0.1.2 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axios-mock-adapter@2.1.0(axios@1.13.2): + dependencies: + axios: 1.13.2 + fast-deep-equal: 3.1.3 + is-buffer: 2.0.5 + + axios@1.13.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axobject-query@4.1.0: {} + + b4a@1.7.3: {} + + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.5): + dependencies: + '@babel/compat-data': 7.28.5 + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + core-js-compat: 3.47.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.5): + dependencies: + '@babel/core': 7.28.5 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.5) + transitivePeerDependencies: + - supports-color + + balanced-match@1.0.2: {} + + balanced-match@2.0.0: {} + + bare-events@2.8.2: {} + + base64-js@1.5.1: {} + + baseline-browser-mapping@2.8.31: {} + + benz-amr-recorder@1.1.5: + dependencies: + benz-recorderjs: 1.0.5 + + benz-recorderjs@1.0.5: {} + + better-path-resolve@1.0.0: + dependencies: + is-windows: 1.0.2 + + bignumber.js@9.3.1: {} + + binary-extensions@2.3.0: {} + + bindings@1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + + bintrees@1.0.2: {} + + birpc@2.8.0: {} + + boolbase@1.0.0: {} + + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.6.2 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.41.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.2 + + bpmn-js-properties-panel@5.23.0(@bpmn-io/properties-panel@3.34.0)(bpmn-js@17.11.1)(camunda-bpmn-js-behaviors@1.11.3(bpmn-js@17.11.1)(camunda-bpmn-moddle@7.0.1)(zeebe-bpmn-moddle@1.11.0))(diagram-js@12.8.1): + dependencies: + '@bpmn-io/extract-process-variables': 0.8.0 + '@bpmn-io/properties-panel': 3.34.0 + array-move: 4.0.0 + bpmn-js: 17.11.1 + camunda-bpmn-js-behaviors: 1.11.3(bpmn-js@17.11.1)(camunda-bpmn-moddle@7.0.1)(zeebe-bpmn-moddle@1.11.0) + diagram-js: 12.8.1 + ids: 1.0.5 + min-dash: 4.2.3 + min-dom: 4.2.1 + + bpmn-js-token-simulation@0.36.3: + dependencies: + inherits-browser: 0.1.0 + min-dash: 4.2.3 + min-dom: 4.2.1 + randomcolor: 0.6.2 + + bpmn-js@17.11.1: + dependencies: + bpmn-moddle: 8.1.0 + diagram-js: 14.11.3 + diagram-js-direct-editing: 3.2.0(diagram-js@14.11.3) + ids: 1.0.5 + inherits-browser: 0.1.0 + min-dash: 4.2.3 + min-dom: 4.2.1 + tiny-svg: 3.1.3 + + bpmn-moddle@8.1.0: + dependencies: + min-dash: 4.2.3 + moddle: 6.2.3 + moddle-xml: 10.1.0 + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.0: + dependencies: + baseline-browser-mapping: 2.8.31 + caniuse-lite: 1.0.30001757 + electron-to-chromium: 1.5.262 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.28.0) + + buffer-crc32@1.0.0: {} + + buffer-from@1.1.2: {} + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + builtin-modules@5.0.0: {} + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + c12@3.3.2(magicast@0.5.1): + dependencies: + chokidar: 4.0.3 + confbox: 0.2.2 + defu: 6.1.4 + dotenv: 17.2.3 + exsolve: 1.0.8 + giget: 2.0.0 + jiti: 2.6.1 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.0.0 + pkg-types: 2.3.0 + rc9: 2.1.2 + optionalDependencies: + magicast: 0.5.1 + + cac@6.7.14: {} + + cacache@20.0.3: + dependencies: + '@npmcli/fs': 5.0.0 + fs-minipass: 3.0.3 + glob: 13.0.0 + lru-cache: 11.2.2 + minipass: 7.1.2 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 7.0.4 + ssri: 13.0.0 + unique-filename: 5.0.0 + + cacheable@2.3.0: + dependencies: + '@cacheable/memory': 2.0.6 + '@cacheable/utils': 2.3.2 + hookified: 1.13.0 + keyv: 5.5.4 + qified: 0.5.2 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsite@1.0.0: {} + + callsites@3.1.0: {} + + camel-case@4.1.2: + dependencies: + pascal-case: 3.1.2 + tslib: 2.8.1 + + camelcase-css@2.0.1: {} + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + camelcase@8.0.0: {} + + camunda-bpmn-js-behaviors@1.11.3(bpmn-js@17.11.1)(camunda-bpmn-moddle@7.0.1)(zeebe-bpmn-moddle@1.11.0): + dependencies: + bpmn-js: 17.11.1 + camunda-bpmn-moddle: 7.0.1 + ids: 1.0.5 + min-dash: 4.2.3 + zeebe-bpmn-moddle: 1.11.0 + + camunda-bpmn-moddle@7.0.1: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.28.0 + caniuse-lite: 1.0.30001757 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + + caniuse-lite@1.0.30001757: {} + + ccount@2.0.1: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + chalk-template@1.1.2: + dependencies: + chalk: 5.6.2 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.6.2: {} + + change-case@5.4.4: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + chardet@2.1.1: {} + + check-error@2.1.1: {} + + cheerio-select@2.1.0: + dependencies: + boolbase: 1.0.0 + css-select: 5.2.2 + css-what: 6.2.2 + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + + cheerio@1.0.0-rc.12: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + htmlparser2: 8.0.2 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + + cheerio@1.1.2: + dependencies: + cheerio-select: 2.1.0 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + domutils: 3.2.2 + encoding-sniffer: 0.2.1 + htmlparser2: 10.0.0 + parse5: 7.3.0 + parse5-htmlparser2-tree-adapter: 7.1.0 + parse5-parser-stream: 7.1.2 + undici: 7.16.0 + whatwg-mimetype: 4.0.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + chownr@3.0.0: {} + + ci-info@3.9.0: {} + + ci-info@4.3.1: {} + + circular-dependency-scanner@2.3.0: + dependencies: + '@ast-grep/napi': 0.39.9 + '@vue/compiler-sfc': 3.5.25 + commander: 12.1.0 + get-tsconfig: 4.13.0 + graph-cycles: 3.0.0 + listr2: 8.3.3 + minimatch: 9.0.5 + node-cleanup: 2.1.2 + typescript: 5.9.3 + update-notifier: 7.3.1 + zx: 8.8.5 + + citty@0.1.6: + dependencies: + consola: 3.4.2 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + classnames@2.5.1: {} + + clean-css@5.3.3: + dependencies: + source-map: 0.6.1 + + clean-regexp@1.0.0: + dependencies: + escape-string-regexp: 1.0.5 + + clear-module@4.1.2: + dependencies: + parent-module: 2.0.0 + resolve-from: 5.0.0 + + cli-boxes@3.0.0: {} + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + clipboard@2.0.11: + dependencies: + good-listener: 1.2.2 + select: 1.1.2 + tiny-emitter: 2.1.0 + + clipboardy@4.0.0: + dependencies: + execa: 8.0.1 + is-wsl: 3.1.0 + is64bit: 2.0.0 + + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clsx@2.1.1: {} + + cluster-key-slot@1.1.2: {} + + codemirror@5.65.20: {} + + codemirror@6.65.7: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colord@2.9.3: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comma-separated-tokens@2.0.3: {} + + commander@10.0.1: {} + + commander@11.1.0: {} + + commander@12.1.0: {} + + commander@13.1.0: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + commander@7.2.0: {} + + commander@8.3.0: {} + + comment-json@4.4.1: + dependencies: + array-timsort: 1.0.3 + core-util-is: 1.0.3 + esprima: 4.0.1 + + comment-parser@1.4.1: {} + + commitlint-plugin-function-rules@4.1.1(@commitlint/lint@19.8.1): + dependencies: + '@commitlint/lint': 19.8.1 + + common-tags@1.8.2: {} + + commondir@1.0.1: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + compare-versions@6.1.1: {} + + compatx@0.2.0: {} + + component-event@0.2.1: {} + + compress-commons@6.0.2: + dependencies: + crc-32: 1.2.2 + crc32-stream: 6.0.0 + is-stream: 2.0.1 + normalize-path: 3.0.0 + readable-stream: 4.7.0 + + compute-scroll-into-view@1.0.20: {} + + concat-map@0.0.1: {} + + confbox@0.1.8: {} + + confbox@0.2.2: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + configstore@7.1.0: + dependencies: + atomically: 2.1.0 + dot-prop: 9.0.0 + graceful-fs: 4.2.11 + xdg-basedir: 5.1.0 + + connect-history-api-fallback@1.6.0: {} + + consola@2.15.3: {} + + consola@3.4.2: {} + + conventional-changelog-angular@7.0.0: + dependencies: + compare-func: 2.0.0 + + conventional-changelog-conventionalcommits@7.0.2: + dependencies: + compare-func: 2.0.0 + + conventional-commits-parser@5.0.0: + dependencies: + JSONStream: 1.3.5 + is-text-path: 2.0.0 + meow: 12.1.1 + split2: 4.2.0 + + convert-source-map@2.0.0: {} + + cookie-es@1.2.2: {} + + cookie-es@2.0.0: {} + + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + + copy-anything@4.0.5: + dependencies: + is-what: 5.5.0 + + core-js-compat@3.47.0: + dependencies: + browserslist: 4.28.0 + + core-js-pure@3.47.0: {} + + core-js@3.47.0: {} + + core-util-is@1.0.3: {} + + cosmiconfig-typescript-loader@6.2.0(@types/node@24.10.1)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3): + dependencies: + '@types/node': 24.10.1 + cosmiconfig: 9.0.0(typescript@5.9.3) + jiti: 2.6.1 + typescript: 5.9.3 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@9.0.0(typescript@5.9.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.9.3 + + crc-32@1.2.2: {} + + crc32-stream@6.0.0: + dependencies: + crc-32: 1.2.2 + readable-stream: 4.7.0 + + crelt@1.0.6: {} + + croner@9.1.0: {} + + cropperjs@1.6.2: {} + + cross-env@7.0.3: + dependencies: + cross-spawn: 7.0.6 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crossws@0.3.5: + dependencies: + uncrypto: 0.1.3 + + crypto-js@4.2.0: {} + + crypto-random-string@2.0.0: {} + + cspell-config-lib@8.19.4: + dependencies: + '@cspell/cspell-types': 8.19.4 + comment-json: 4.4.1 + yaml: 2.8.1 + + cspell-dictionary@8.19.4: + dependencies: + '@cspell/cspell-pipe': 8.19.4 + '@cspell/cspell-types': 8.19.4 + cspell-trie-lib: 8.19.4 + fast-equals: 5.3.3 + + cspell-gitignore@8.19.4: + dependencies: + '@cspell/url': 8.19.4 + cspell-glob: 8.19.4 + cspell-io: 8.19.4 + + cspell-glob@8.19.4: + dependencies: + '@cspell/url': 8.19.4 + picomatch: 4.0.3 + + cspell-grammar@8.19.4: + dependencies: + '@cspell/cspell-pipe': 8.19.4 + '@cspell/cspell-types': 8.19.4 + + cspell-io@8.19.4: + dependencies: + '@cspell/cspell-service-bus': 8.19.4 + '@cspell/url': 8.19.4 + + cspell-lib@8.19.4: + dependencies: + '@cspell/cspell-bundled-dicts': 8.19.4 + '@cspell/cspell-pipe': 8.19.4 + '@cspell/cspell-resolver': 8.19.4 + '@cspell/cspell-types': 8.19.4 + '@cspell/dynamic-import': 8.19.4 + '@cspell/filetypes': 8.19.4 + '@cspell/strong-weak-map': 8.19.4 + '@cspell/url': 8.19.4 + clear-module: 4.1.2 + comment-json: 4.4.1 + cspell-config-lib: 8.19.4 + cspell-dictionary: 8.19.4 + cspell-glob: 8.19.4 + cspell-grammar: 8.19.4 + cspell-io: 8.19.4 + cspell-trie-lib: 8.19.4 + env-paths: 3.0.0 + fast-equals: 5.3.3 + gensequence: 7.0.0 + import-fresh: 3.3.1 + resolve-from: 5.0.0 + vscode-languageserver-textdocument: 1.0.12 + vscode-uri: 3.1.0 + xdg-basedir: 5.1.0 + + cspell-trie-lib@8.19.4: + dependencies: + '@cspell/cspell-pipe': 8.19.4 + '@cspell/cspell-types': 8.19.4 + gensequence: 7.0.0 + + cspell@8.19.4: + dependencies: + '@cspell/cspell-json-reporter': 8.19.4 + '@cspell/cspell-pipe': 8.19.4 + '@cspell/cspell-types': 8.19.4 + '@cspell/dynamic-import': 8.19.4 + '@cspell/url': 8.19.4 + chalk: 5.6.2 + chalk-template: 1.1.2 + commander: 13.1.0 + cspell-dictionary: 8.19.4 + cspell-gitignore: 8.19.4 + cspell-glob: 8.19.4 + cspell-io: 8.19.4 + cspell-lib: 8.19.4 + fast-json-stable-stringify: 2.1.0 + file-entry-cache: 9.1.0 + semver: 7.7.3 + tinyglobby: 0.2.15 + + css-blank-pseudo@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + css-declaration-sorter@7.3.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + css-functions-list@3.2.3: {} + + css-has-pseudo@7.0.3(postcss@8.5.6): + dependencies: + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + + css-prefers-color-scheme@10.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + css-render@0.15.14: + dependencies: + '@emotion/hash': 0.8.0 + csstype: 3.0.11 + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-select@5.2.2: + dependencies: + boolbase: 1.0.0 + css-what: 6.2.2 + 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.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + css-what@6.2.2: {} + + cssdb@8.4.2: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@7.0.10(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + css-declaration-sorter: 7.3.0(postcss@8.5.6) + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-calc: 10.1.1(postcss@8.5.6) + postcss-colormin: 7.0.5(postcss@8.5.6) + postcss-convert-values: 7.0.8(postcss@8.5.6) + postcss-discard-comments: 7.0.5(postcss@8.5.6) + postcss-discard-duplicates: 7.0.2(postcss@8.5.6) + postcss-discard-empty: 7.0.1(postcss@8.5.6) + postcss-discard-overridden: 7.0.1(postcss@8.5.6) + postcss-merge-longhand: 7.0.5(postcss@8.5.6) + postcss-merge-rules: 7.0.7(postcss@8.5.6) + postcss-minify-font-values: 7.0.1(postcss@8.5.6) + postcss-minify-gradients: 7.0.1(postcss@8.5.6) + postcss-minify-params: 7.0.5(postcss@8.5.6) + postcss-minify-selectors: 7.0.5(postcss@8.5.6) + postcss-normalize-charset: 7.0.1(postcss@8.5.6) + postcss-normalize-display-values: 7.0.1(postcss@8.5.6) + postcss-normalize-positions: 7.0.1(postcss@8.5.6) + postcss-normalize-repeat-style: 7.0.1(postcss@8.5.6) + postcss-normalize-string: 7.0.1(postcss@8.5.6) + postcss-normalize-timing-functions: 7.0.1(postcss@8.5.6) + postcss-normalize-unicode: 7.0.5(postcss@8.5.6) + postcss-normalize-url: 7.0.1(postcss@8.5.6) + postcss-normalize-whitespace: 7.0.1(postcss@8.5.6) + postcss-ordered-values: 7.0.2(postcss@8.5.6) + postcss-reduce-initial: 7.0.5(postcss@8.5.6) + postcss-reduce-transforms: 7.0.1(postcss@8.5.6) + postcss-svgo: 7.1.0(postcss@8.5.6) + postcss-unique-selectors: 7.0.4(postcss@8.5.6) + + cssnano-utils@5.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + cssnano@7.1.2(postcss@8.5.6): + dependencies: + cssnano-preset-default: 7.0.10(postcss@8.5.6) + lilconfig: 3.1.3 + postcss: 8.5.6 + + csso@5.0.5: + dependencies: + css-tree: 2.2.1 + + csstype@3.0.11: {} + + csstype@3.2.3: {} + + cz-git@1.12.0: {} + + czg@1.12.0: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-flextree@2.1.2: + dependencies: + d3-hierarchy: 1.1.9 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@1.1.9: {} + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.0 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + + dargs@8.1.0: {} + + data-uri-to-buffer@4.0.1: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + dataloader@1.4.0: {} + + date-fns-tz@3.2.0(date-fns@4.1.0): + dependencies: + date-fns: 4.1.0 + + date-fns@4.1.0: {} + + dayjs@1.11.19: {} + + db0@0.3.4: {} + + de-indent@1.0.2: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decamelize@1.2.0: {} + + deep-eql@5.0.2: {} + + deep-extend@0.6.0: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-browser-id@5.0.1: {} + + default-browser@5.4.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + default-passive-events@2.0.0: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@2.0.0: {} + + define-lazy-prop@3.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + defu@6.1.4: {} + + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + + delayed-stream@1.0.0: {} + + delegate@3.2.0: {} + + denque@2.1.0: {} + + depcheck@1.4.7: + dependencies: + '@babel/parser': 7.28.5 + '@babel/traverse': 7.28.5 + '@vue/compiler-sfc': 3.5.25 + callsite: 1.0.0 + camelcase: 6.3.0 + cosmiconfig: 7.1.0 + debug: 4.4.3 + deps-regex: 0.2.0 + findup-sync: 5.0.0 + ignore: 5.3.2 + is-core-module: 2.16.1 + js-yaml: 3.14.2 + json5: 2.2.3 + lodash: 4.17.21 + minimatch: 7.4.6 + multimatch: 5.0.0 + please-upgrade-node: 3.2.0 + readdirp: 3.6.0 + require-package-name: 2.0.1 + resolve: 1.22.11 + resolve-from: 5.0.0 + semver: 7.7.3 + yargs: 16.2.0 + transitivePeerDependencies: + - supports-color + + depd@2.0.0: {} + + deps-regex@0.2.0: {} + + dequal@2.0.3: {} + + destr@2.0.5: {} + + detect-file@1.0.0: {} + + detect-indent@6.1.0: {} + + detect-libc@1.0.3: {} + + detect-libc@2.1.2: {} + + devalue@5.5.0: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diagram-js-direct-editing@3.2.0(diagram-js@14.11.3): + dependencies: + diagram-js: 14.11.3 + min-dash: 4.2.3 + min-dom: 4.2.1 + + diagram-js@12.8.1: + dependencies: + '@bpmn-io/diagram-js-ui': 0.2.3 + clsx: 2.1.1 + didi: 9.0.2 + hammerjs: 2.0.8 + inherits-browser: 0.1.0 + min-dash: 4.2.3 + min-dom: 4.2.1 + object-refs: 0.3.0 + path-intersection: 2.2.1 + tiny-svg: 3.1.3 + + diagram-js@14.11.3: + dependencies: + '@bpmn-io/diagram-js-ui': 0.2.3 + clsx: 2.1.1 + didi: 10.2.2 + inherits-browser: 0.1.0 + min-dash: 4.2.3 + min-dom: 4.2.1 + object-refs: 0.4.0 + path-intersection: 3.1.0 + tiny-svg: 3.1.3 + + didi@10.2.2: {} + + didi@9.0.2: {} + + didyoumean@1.2.2: {} + + diff-sequences@27.5.1: {} + + diff@8.0.2: {} + + dijkstrajs@1.0.3: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dom-align@1.12.4: {} + + dom-scroll-into-view@2.0.1: {} + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom-serializer@2.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + dom-walk@0.1.2: {} + + dom-zindex@1.0.6: {} + + domelementtype@2.3.0: {} + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domhandler@5.0.3: + dependencies: + domelementtype: 2.3.0 + + domify@1.4.2: {} + + domify@2.0.0: {} + + dompurify@3.3.0: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + domutils@3.2.2: + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + dot-prop@10.1.0: + dependencies: + type-fest: 5.2.0 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dot-prop@9.0.0: + dependencies: + type-fest: 4.41.0 + + dotenv-expand@8.0.3: {} + + dotenv@16.0.3: {} + + dotenv@16.6.1: {} + + dotenv@17.2.3: {} + + dotenv@8.6.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} + + eastasianwidth@0.2.0: {} + + echarts@6.0.0: + dependencies: + tslib: 2.3.0 + zrender: 6.0.0 + + editorconfig@1.0.4: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.7.3 + + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + + electron-to-chromium@1.5.262: {} + + element-plus@2.11.8(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@ctrl/tinycolor': 4.2.0 + '@element-plus/icons-vue': 2.3.2(vue@3.5.25(typescript@5.9.3)) + '@floating-ui/dom': 1.7.4 + '@popperjs/core': '@sxzz/popperjs-es@2.11.7' + '@types/lodash': 4.17.21 + '@types/lodash-es': 4.17.12 + '@vueuse/core': 9.13.0(vue@3.5.25(typescript@5.9.3)) + async-validator: 4.2.5 + dayjs: 1.11.19 + lodash: 4.17.21 + lodash-es: 4.17.21 + lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21) + memoize-one: 6.0.0 + normalize-wheel-es: 1.2.0 + vue: 3.5.25(typescript@5.9.3) + transitivePeerDependencies: + - '@vue/composition-api' + + emoji-regex-xs@1.0.0: {} + + emoji-regex@10.6.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + encodeurl@2.0.0: {} + + encoding-sniffer@0.2.1: + dependencies: + iconv-lite: 0.6.3 + whatwg-encoding: 3.1.1 + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + + entities@2.2.0: {} + + entities@4.5.0: {} + + entities@6.0.1: {} + + env-paths@2.2.1: {} + + env-paths@3.0.0: {} + + environment@1.1.0: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + + error-stack-parser-es@1.0.5: {} + + errx@0.1.0: {} + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + es-toolkit@1.42.0: {} + + esbuild@0.25.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.3 + '@esbuild/android-arm': 0.25.3 + '@esbuild/android-arm64': 0.25.3 + '@esbuild/android-x64': 0.25.3 + '@esbuild/darwin-arm64': 0.25.3 + '@esbuild/darwin-x64': 0.25.3 + '@esbuild/freebsd-arm64': 0.25.3 + '@esbuild/freebsd-x64': 0.25.3 + '@esbuild/linux-arm': 0.25.3 + '@esbuild/linux-arm64': 0.25.3 + '@esbuild/linux-ia32': 0.25.3 + '@esbuild/linux-loong64': 0.25.3 + '@esbuild/linux-mips64el': 0.25.3 + '@esbuild/linux-ppc64': 0.25.3 + '@esbuild/linux-riscv64': 0.25.3 + '@esbuild/linux-s390x': 0.25.3 + '@esbuild/linux-x64': 0.25.3 + '@esbuild/netbsd-arm64': 0.25.3 + '@esbuild/netbsd-x64': 0.25.3 + '@esbuild/openbsd-arm64': 0.25.3 + '@esbuild/openbsd-x64': 0.25.3 + '@esbuild/sunos-x64': 0.25.3 + '@esbuild/win32-arm64': 0.25.3 + '@esbuild/win32-ia32': 0.25.3 + '@esbuild/win32-x64': 0.25.3 + + escalade@3.2.0: {} + + escape-goat@4.0.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + + eslint-compat-utils@0.5.1(eslint@9.39.1(jiti@2.6.1)): + dependencies: + eslint: 9.39.1(jiti@2.6.1) + semver: 7.7.3 + + eslint-compat-utils@0.6.5(eslint@9.39.1(jiti@2.6.1)): + dependencies: + eslint: 9.39.1(jiti@2.6.1) + semver: 7.7.3 + + eslint-config-turbo@2.6.1(eslint@9.39.1(jiti@2.6.1))(turbo@2.6.1): + dependencies: + eslint: 9.39.1(jiti@2.6.1) + eslint-plugin-turbo: 2.6.1(eslint@9.39.1(jiti@2.6.1))(turbo@2.6.1) + turbo: 2.6.1 + + eslint-import-context@0.1.9(unrs-resolver@1.11.1): + dependencies: + get-tsconfig: 4.13.0 + stable-hash-x: 0.2.0 + optionalDependencies: + unrs-resolver: 1.11.1 + + eslint-json-compat-utils@0.2.1(eslint@9.39.1(jiti@2.6.1))(jsonc-eslint-parser@2.4.1): + dependencies: + eslint: 9.39.1(jiti@2.6.1) + esquery: 1.6.0 + jsonc-eslint-parser: 2.4.1 + + eslint-plugin-command@3.3.1(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@es-joy/jsdoccomment': 0.50.2 + eslint: 9.39.1(jiti@2.6.1) + + eslint-plugin-es-x@7.8.0(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + eslint: 9.39.1(jiti@2.6.1) + eslint-compat-utils: 0.5.1(eslint@9.39.1(jiti@2.6.1)) + + eslint-plugin-eslint-comments@3.2.0(eslint@9.39.1(jiti@2.6.1)): + dependencies: + escape-string-regexp: 1.0.5 + eslint: 9.39.1(jiti@2.6.1) + ignore: 5.3.2 + + eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@typescript-eslint/types': 8.48.0 + comment-parser: 1.4.1 + debug: 4.4.3 + eslint: 9.39.1(jiti@2.6.1) + eslint-import-context: 0.1.9(unrs-resolver@1.11.1) + is-glob: 4.0.3 + minimatch: 10.1.1 + semver: 7.7.3 + stable-hash-x: 0.2.0 + unrs-resolver: 1.11.1 + optionalDependencies: + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-jsdoc@61.4.1(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@es-joy/jsdoccomment': 0.76.0 + '@es-joy/resolve.exports': 1.2.0 + are-docs-informative: 0.0.2 + comment-parser: 1.4.1 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint: 9.39.1(jiti@2.6.1) + espree: 10.4.0 + esquery: 1.6.0 + html-entities: 2.6.0 + object-deep-merge: 2.0.0 + parse-imports-exports: 0.2.4 + semver: 7.7.3 + spdx-expression-parse: 4.0.0 + to-valid-identifier: 1.0.0 + transitivePeerDependencies: + - supports-color + + eslint-plugin-jsonc@2.21.0(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + diff-sequences: 27.5.1 + eslint: 9.39.1(jiti@2.6.1) + eslint-compat-utils: 0.6.5(eslint@9.39.1(jiti@2.6.1)) + eslint-json-compat-utils: 0.2.1(eslint@9.39.1(jiti@2.6.1))(jsonc-eslint-parser@2.4.1) + espree: 10.4.0 + graphemer: 1.4.0 + jsonc-eslint-parser: 2.4.1 + natural-compare: 1.4.0 + synckit: 0.11.11 + transitivePeerDependencies: + - '@eslint/json' + + eslint-plugin-n@17.23.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + enhanced-resolve: 5.18.3 + eslint: 9.39.1(jiti@2.6.1) + eslint-plugin-es-x: 7.8.0(eslint@9.39.1(jiti@2.6.1)) + get-tsconfig: 4.13.0 + globals: 15.15.0 + globrex: 0.1.2 + ignore: 5.3.2 + semver: 7.7.3 + ts-declaration-location: 1.0.7(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + eslint-plugin-no-only-tests@3.3.0: {} + + eslint-plugin-perfectionist@4.15.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@typescript-eslint/types': 8.48.0 + '@typescript-eslint/utils': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.1(jiti@2.6.1) + natural-orderby: 5.0.0 + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-prettier@5.5.4(@types/eslint@9.6.1)(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2): + dependencies: + eslint: 9.39.1(jiti@2.6.1) + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + '@types/eslint': 9.6.1 + + eslint-plugin-regexp@2.10.0(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + comment-parser: 1.4.1 + eslint: 9.39.1(jiti@2.6.1) + jsdoc-type-pratt-parser: 4.8.0 + refa: 0.12.1 + regexp-ast-analysis: 0.7.1 + scslre: 0.3.0 + + eslint-plugin-turbo@2.6.1(eslint@9.39.1(jiti@2.6.1))(turbo@2.6.1): + dependencies: + dotenv: 16.0.3 + eslint: 9.39.1(jiti@2.6.1) + turbo: 2.6.1 + + eslint-plugin-unicorn@62.0.0(eslint@9.39.1(jiti@2.6.1)): + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint/plugin-kit': 0.4.1 + change-case: 5.4.4 + ci-info: 4.3.1 + clean-regexp: 1.0.0 + core-js-compat: 3.47.0 + eslint: 9.39.1(jiti@2.6.1) + esquery: 1.6.0 + find-up-simple: 1.0.1 + globals: 16.5.0 + indent-string: 5.0.0 + is-builtin-module: 5.0.0 + jsesc: 3.1.0 + pluralize: 8.0.0 + regexp-tree: 0.1.27 + regjsparser: 0.13.0 + semver: 7.7.3 + strip-indent: 4.1.1 + + eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)): + dependencies: + eslint: 9.39.1(jiti@2.6.1) + optionalDependencies: + '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + + eslint-plugin-vitest@0.5.4(@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)(vitest@3.2.4(@types/node@24.10.1)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)): + dependencies: + '@typescript-eslint/utils': 7.18.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.39.1(jiti@2.6.1) + optionalDependencies: + '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + vitest: 3.2.4(@types/node@24.10.1)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + - typescript + + eslint-plugin-vue@10.6.1(@typescript-eslint/parser@8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1))): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + eslint: 9.39.1(jiti@2.6.1) + natural-compare: 1.4.0 + nth-check: 2.1.1 + postcss-selector-parser: 7.1.0 + semver: 7.7.3 + vue-eslint-parser: 10.2.0(eslint@9.39.1(jiti@2.6.1)) + xml-name-validator: 4.0.0 + optionalDependencies: + '@typescript-eslint/parser': 8.48.0(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.39.1(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.39.1 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + + esm-env@1.2.2: {} + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + espree@9.6.1: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 3.4.3 + + esprima@4.0.1: {} + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrap@2.2.0: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@1.0.1: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-target-shim@5.0.1: {} + + eventemitter3@5.0.1: {} + + events-universal@1.0.1: + dependencies: + bare-events: 2.8.2 + transitivePeerDependencies: + - bare-abort-controller + + events@3.3.0: {} + + evtd@0.2.4: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + execa@9.6.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.6 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 8.0.1 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 6.0.0 + pretty-ms: 9.3.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.2 + + expand-tilde@2.0.2: + dependencies: + homedir-polyfill: 1.0.3 + + expect-type@1.2.2: {} + + exsolve@1.0.8: {} + + extend-shallow@2.0.1: + dependencies: + is-extendable: 0.1.1 + + extendable-error@0.1.7: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-equals@5.3.3: {} + + fast-fifo@1.3.2: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-string-compare@3.0.0: {} + + fast-uri@3.1.0: {} + + fast-xml-parser@4.5.3: + dependencies: + strnum: 1.1.2 + + fastest-levenshtein@1.0.16: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + feelers@1.4.0: + dependencies: + '@bpmn-io/cm-theme': 0.1.0-alpha.2 + '@bpmn-io/feel-lint': 1.4.0 + '@codemirror/autocomplete': 6.20.0 + '@codemirror/commands': 6.10.0 + '@codemirror/language': 6.11.3 + '@codemirror/lint': 6.9.2 + '@codemirror/state': 6.5.2 + '@codemirror/view': 6.38.8 + '@lezer/common': 1.3.0 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.3 + '@lezer/markdown': 1.6.0 + feelin: 3.2.0 + lezer-feel: 1.9.0 + min-dom: 5.1.1 + + feelin@3.2.0: + dependencies: + '@lezer/lr': 1.4.3 + lezer-feel: 1.9.0 + luxon: 3.7.2 + + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-entry-cache@11.1.1: + dependencies: + flat-cache: 6.1.19 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + file-entry-cache@9.1.0: + dependencies: + flat-cache: 5.0.0 + + file-uri-to-path@1.0.0: {} + + filelist@1.0.4: + dependencies: + minimatch: 5.1.6 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up-simple@1.0.1: {} + + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + find-up@7.0.0: + dependencies: + locate-path: 7.2.0 + path-exists: 5.0.0 + unicorn-magic: 0.1.0 + + findup-sync@5.0.0: + dependencies: + detect-file: 1.0.0 + is-glob: 4.0.3 + micromatch: 4.0.8 + resolve-dir: 1.0.1 + + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.0 + rollup: 4.53.3 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flat-cache@5.0.0: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flat-cache@6.1.19: + dependencies: + cacheable: 2.3.0 + flatted: 3.3.3 + hookified: 1.13.0 + + flatted@3.3.3: {} + + focus-trap@7.6.6: + dependencies: + tabbable: 6.3.0 + + follow-redirects@1.15.11: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + formdata-node@6.0.3: {} + + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + + fraction.js@5.3.4: {} + + framesync@6.1.2: + dependencies: + tslib: 2.4.0 + + fresh@2.0.0: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@11.3.2: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-extra@7.0.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + + fs-extra@9.1.0: + dependencies: + at-least-node: 1.0.0 + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.2 + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensequence@7.0.0: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.4.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-own-enumerable-property-symbols@3.0.2: {} + + get-port-please@3.2.0: {} + + get-port@7.1.0: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@8.0.1: {} + + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + giget@2.0.0: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.4 + node-fetch-native: 1.6.7 + nypm: 0.6.2 + pathe: 2.0.3 + + git-raw-commits@4.0.0: + dependencies: + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@11.1.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 4.1.1 + minimatch: 10.1.1 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 2.0.1 + + glob@13.0.0: + dependencies: + minimatch: 10.1.1 + minipass: 7.1.2 + path-scurry: 2.0.1 + + global-directory@4.0.1: + dependencies: + ini: 4.1.1 + + global-modules@1.0.0: + dependencies: + global-prefix: 1.0.2 + is-windows: 1.0.2 + resolve-dir: 1.0.1 + + global-modules@2.0.0: + dependencies: + global-prefix: 3.0.0 + + global-prefix@1.0.2: + dependencies: + expand-tilde: 2.0.2 + homedir-polyfill: 1.0.3 + ini: 1.3.8 + is-windows: 1.0.2 + which: 1.3.1 + + global-prefix@3.0.0: + dependencies: + ini: 1.3.8 + kind-of: 6.0.3 + which: 1.3.1 + + global@4.4.0: + dependencies: + min-document: 2.19.2 + process: 0.11.10 + + globals@14.0.0: {} + + globals@15.15.0: {} + + globals@16.5.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + globby@14.1.0: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + + globby@15.0.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + fast-glob: 3.3.3 + ignore: 7.0.5 + path-type: 6.0.0 + slash: 5.1.0 + unicorn-magic: 0.3.0 + + globjoin@0.1.4: {} + + globrex@0.1.2: {} + + good-listener@1.2.2: + dependencies: + delegate: 3.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.10: {} + + graceful-fs@4.2.11: {} + + graph-cycles@3.0.0: + dependencies: + fast-string-compare: 3.0.0 + rotated-array-set: 3.0.0 + short-tree: 3.0.0 + + graphemer@1.4.0: {} + + gray-matter@4.0.3: + dependencies: + js-yaml: 3.14.2 + kind-of: 6.0.3 + section-matter: 1.0.0 + strip-bom-string: 1.0.0 + + gzip-size@7.0.0: + dependencies: + duplexer: 0.1.2 + + h3@1.15.4: + dependencies: + cookie-es: 1.2.2 + crossws: 0.3.5 + defu: 6.1.4 + destr: 2.0.5 + iron-webcrypto: 1.2.1 + node-mock-http: 1.0.3 + radix3: 1.1.2 + ufo: 1.6.1 + uncrypto: 0.1.3 + + hammerjs@2.0.8: {} + + happy-dom@17.6.3: + dependencies: + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hashery@1.2.0: + dependencies: + hookified: 1.13.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-to-html@9.0.5: + dependencies: + '@types/hast': 3.0.4 + '@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.1.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.4 + + he@1.2.0: {} + + hey-listen@1.0.8: {} + + highlight.js@11.11.1: {} + + homedir-polyfill@1.0.3: + dependencies: + parse-passwd: 1.0.0 + + hookable@5.5.3: {} + + hookified@1.13.0: {} + + htm@3.1.1: {} + + html-entities@2.6.0: {} + + html-minifier-terser@6.1.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 8.3.0 + he: 1.2.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.44.1 + + html-minifier-terser@7.2.0: + dependencies: + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 10.0.1 + entities: 4.5.0 + param-case: 3.0.4 + relateurl: 0.2.7 + terser: 5.44.1 + + html-tags@3.3.1: {} + + html-void-elements@3.0.0: {} + + htmlparser2@10.0.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 6.0.1 + + htmlparser2@8.0.2: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + + http-shutdown@1.2.2: {} + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + httpxy@0.1.7: {} + + human-id@4.1.2: {} + + human-signals@5.0.0: {} + + human-signals@8.0.1: {} + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + + idb@7.1.1: {} + + ids@1.0.5: {} + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + image-size@0.5.5: + optional: true + + immutable@5.1.4: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-lazy@4.0.0: {} + + import-meta-resolve@4.2.0: {} + + imurmurhash@0.1.4: {} + + indent-string@5.0.0: {} + + individual@2.0.0: {} + + inherits-browser@0.1.0: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + internmap@2.0.3: {} + + ioredis@5.8.2: + dependencies: + '@ioredis/commands': 1.4.0 + cluster-key-slot: 1.1.2 + debug: 4.4.3 + denque: 2.1.0 + lodash.defaults: 4.2.0 + lodash.isarguments: 3.1.0 + redis-errors: 1.2.0 + redis-parser: 3.0.0 + standard-as-callback: 2.1.0 + transitivePeerDependencies: + - supports-color + + iron-webcrypto@1.2.1: {} + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-buffer@2.0.5: {} + + is-builtin-module@5.0.0: + dependencies: + builtin-modules: 5.0.0 + + is-callable@1.2.7: {} + + is-ci@4.1.0: + dependencies: + ci-info: 4.3.1 + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-docker@2.2.1: {} + + is-docker@3.0.0: {} + + is-extendable@0.1.1: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.4.0 + + is-function@1.0.2: {} + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-in-ci@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-installed-globally@1.0.0: + dependencies: + global-directory: 4.0.1 + is-path-inside: 4.0.0 + + is-interactive@2.0.0: {} + + is-map@2.0.3: {} + + is-module@1.0.0: {} + + is-negative-zero@2.0.3: {} + + is-npm@6.1.0: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-obj@1.0.1: {} + + is-obj@2.0.0: {} + + is-path-inside@4.0.0: {} + + is-plain-obj@4.1.0: {} + + is-plain-object@3.0.1: {} + + is-plain-object@5.0.0: {} + + is-reference@1.2.1: + dependencies: + '@types/estree': 1.0.8 + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-regexp@1.0.0: {} + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@2.0.1: {} + + is-stream@3.0.0: {} + + is-stream@4.0.1: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-subdir@1.2.0: + dependencies: + better-path-resolve: 1.0.0 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-text-path@2.0.0: + dependencies: + text-extensions: 2.4.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-what@3.14.1: {} + + is-what@5.5.0: {} + + is-windows@1.0.2: {} + + is-wsl@2.2.0: + dependencies: + is-docker: 2.2.1 + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + is64bit@2.0.0: + dependencies: + system-architecture: 0.1.0 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jackspeak@4.1.1: + dependencies: + '@isaacs/cliui': 8.0.2 + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.4 + picocolors: 1.1.1 + + jiti@2.6.1: {} + + jju@1.4.0: {} + + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.5.0 + js-cookie: 3.0.5 + nopt: 7.2.1 + + js-cookie@3.0.5: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@3.14.2: + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdoc-type-pratt-parser@4.1.0: {} + + jsdoc-type-pratt-parser@4.8.0: {} + + jsdoc-type-pratt-parser@6.10.0: {} + + jsencrypt@3.5.4: {} + + jsesc@3.1.0: {} + + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-schema@0.4.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + jsonc-eslint-parser@2.4.1: + dependencies: + acorn: 8.15.0 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + semver: 7.7.3 + + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + jsonparse@1.3.1: {} + + jsonpointer@5.0.1: {} + + katex@0.16.25: + dependencies: + commander: 8.3.0 + + keycode@2.2.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + keyv@5.5.4: + dependencies: + '@keyv/serialize': 1.1.1 + + kind-of@6.0.3: {} + + kleur@4.1.5: {} + + klona@2.0.6: {} + + knitwork@1.3.0: {} + + known-css-properties@0.36.0: {} + + known-css-properties@0.37.0: {} + + kolorist@1.8.0: {} + + ky@1.14.0: {} + + latest-version@9.0.0: + dependencies: + package-json: 10.0.1 + + lazystream@1.0.1: + dependencies: + readable-stream: 2.3.8 + + lefthook-darwin-arm64@1.13.6: + optional: true + + lefthook-darwin-x64@1.13.6: + optional: true + + lefthook-freebsd-arm64@1.13.6: + optional: true + + lefthook-freebsd-x64@1.13.6: + optional: true + + lefthook-linux-arm64@1.13.6: + optional: true + + lefthook-linux-x64@1.13.6: + optional: true + + lefthook-openbsd-arm64@1.13.6: + optional: true + + lefthook-openbsd-x64@1.13.6: + optional: true + + lefthook-windows-arm64@1.13.6: + optional: true + + lefthook-windows-x64@1.13.6: + optional: true + + lefthook@1.13.6: + optionalDependencies: + lefthook-darwin-arm64: 1.13.6 + lefthook-darwin-x64: 1.13.6 + lefthook-freebsd-arm64: 1.13.6 + lefthook-freebsd-x64: 1.13.6 + lefthook-linux-arm64: 1.13.6 + lefthook-linux-x64: 1.13.6 + lefthook-openbsd-arm64: 1.13.6 + lefthook-openbsd-x64: 1.13.6 + lefthook-windows-arm64: 1.13.6 + lefthook-windows-x64: 1.13.6 + + less@4.4.2: + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.8.1 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.3.1 + source-map: 0.6.1 + + leven@3.1.0: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lezer-feel@1.9.0: + dependencies: + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.3 + min-dash: 4.2.3 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + linkify-it@5.0.0: + dependencies: + uc.micro: 2.1.0 + + listhen@1.9.0: + dependencies: + '@parcel/watcher': 2.5.1 + '@parcel/watcher-wasm': 2.5.1 + citty: 0.1.6 + clipboardy: 4.0.0 + consola: 3.4.2 + crossws: 0.3.5 + defu: 6.1.4 + get-port-please: 3.2.0 + h3: 1.15.4 + http-shutdown: 1.2.2 + jiti: 2.6.1 + mlly: 1.8.0 + node-forge: 1.3.2 + pathe: 1.1.2 + std-env: 3.10.0 + ufo: 1.6.1 + untun: 0.1.3 + uqr: 0.1.2 + + listr2@8.3.3: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.2 + + local-pkg@1.1.2: + dependencies: + mlly: 1.8.0 + pkg-types: 2.3.0 + quansync: 0.2.11 + + locate-character@3.0.0: {} + + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + locate-path@7.2.0: + dependencies: + p-locate: 6.0.0 + + locko@1.1.0: {} + + lodash-es@4.17.21: {} + + lodash-unified@1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21): + dependencies: + '@types/lodash-es': 4.17.12 + lodash: 4.17.21 + lodash-es: 4.17.21 + + lodash.camelcase@4.3.0: {} + + lodash.clonedeep@4.5.0: {} + + lodash.debounce@4.0.8: {} + + lodash.defaults@4.2.0: {} + + lodash.isarguments@3.1.0: {} + + lodash.isplainobject@4.0.6: {} + + lodash.kebabcase@4.1.1: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.mergewith@4.6.2: {} + + lodash.snakecase@4.1.1: {} + + lodash.sortby@4.7.0: {} + + lodash.startcase@4.4.0: {} + + lodash.truncate@4.4.2: {} + + lodash.uniq@4.5.0: {} + + lodash.upperfirst@4.3.1: {} + + lodash@4.17.21: {} + + log-symbols@6.0.0: + dependencies: + chalk: 5.6.2 + is-unicode-supported: 1.3.0 + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.2.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.1.2 + wrap-ansi: 9.0.2 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.2.1: {} + + lower-case@2.0.2: + dependencies: + tslib: 2.8.1 + + lru-cache@10.4.3: {} + + lru-cache@11.2.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lucide-vue-next@0.553.0(vue@3.5.25(typescript@5.9.3)): + dependencies: + vue: 3.5.25(typescript@5.9.3) + + luxon@3.7.2: {} + + lz-string@1.5.0: {} + + m3u8-parser@4.8.0: + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/vhs-utils': 3.0.5 + global: 4.4.0 + + magic-string@0.25.9: + dependencies: + sourcemap-codec: 1.4.8 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.1: + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + source-map-js: 1.2.1 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + + mark.js@8.11.1: {} + + markdown-it@14.1.0: + dependencies: + argparse: 2.0.1 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 + + marked@17.0.1: {} + + markmap-common@0.16.0: + dependencies: + '@babel/runtime': 7.28.4 + '@gera2ld/jsx-dom': 2.2.2 + npm2url: 0.2.4 + + markmap-html-parser@0.16.1(markmap-common@0.16.0): + dependencies: + '@babel/runtime': 7.28.4 + cheerio: 1.0.0-rc.12 + markmap-common: 0.16.0 + + markmap-lib@0.16.1(markmap-common@0.16.0): + dependencies: + '@babel/runtime': 7.28.4 + highlight.js: 11.11.1 + js-yaml: 4.1.1 + katex: 0.16.25 + markmap-common: 0.16.0 + markmap-html-parser: 0.16.1(markmap-common@0.16.0) + markmap-view: 0.16.0(markmap-common@0.16.0) + prismjs: 1.30.0 + remarkable: 2.0.1 + remarkable-katex: 1.2.1 + + markmap-toolbar@0.17.2(markmap-common@0.16.0): + dependencies: + '@babel/runtime': 7.28.4 + '@gera2ld/jsx-dom': 2.2.2 + markmap-common: 0.16.0 + + markmap-view@0.16.0(markmap-common@0.16.0): + dependencies: + '@babel/runtime': 7.28.4 + '@gera2ld/jsx-dom': 2.2.2 + '@types/d3': 7.4.3 + d3: 7.9.0 + d3-flextree: 2.1.2 + markmap-common: 0.16.0 + + math-intrinsics@1.1.0: {} + + mathml-tag-names@2.1.3: {} + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.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.0.0 + vfile: 6.0.3 + + mdn-data@2.0.28: {} + + mdn-data@2.12.2: {} + + mdn-data@2.25.0: {} + + mdurl@2.0.0: {} + + medium-zoom@1.1.0: {} + + memoize-one@6.0.0: {} + + meow@12.1.1: {} + + meow@13.2.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + 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: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + + mime@1.6.0: + optional: true + + mime@3.0.0: {} + + mime@4.1.0: {} + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + min-dash@4.2.3: {} + + min-document@2.19.2: + dependencies: + dom-walk: 0.1.2 + + min-dom@4.2.1: + dependencies: + component-event: 0.2.1 + domify: 1.4.2 + min-dash: 4.2.3 + + min-dom@5.1.1: + dependencies: + domify: 2.0.0 + min-dash: 4.2.3 + + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + + minimatch@10.1.1: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@7.4.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.1: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.2 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + + minipass@7.1.2: {} + + minisearch@7.2.0: {} + + minizlib@3.1.0: + dependencies: + minipass: 7.1.2 + + mitt@3.0.1: {} + + mkdist@2.4.1(sass@1.94.2)(typescript@5.9.3)(vue-tsc@3.1.5(typescript@5.9.3))(vue@3.5.25(typescript@5.9.3)): + dependencies: + autoprefixer: 10.4.22(postcss@8.5.6) + citty: 0.1.6 + cssnano: 7.1.2(postcss@8.5.6) + defu: 6.1.4 + esbuild: 0.25.3 + jiti: 2.6.1 + mlly: 1.8.0 + pathe: 2.0.3 + pkg-types: 2.3.0 + postcss: 8.5.6 + postcss-nested: 7.0.2(postcss@8.5.6) + semver: 7.7.3 + tinyglobby: 0.2.15 + optionalDependencies: + sass: 1.94.2 + typescript: 5.9.3 + vue: 3.5.25(typescript@5.9.3) + vue-tsc: 3.1.5(typescript@5.9.3) + + mlly@1.8.0: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + moddle-xml@10.1.0: + dependencies: + min-dash: 4.2.3 + moddle: 6.2.3 + saxen: 8.1.2 + + moddle@6.2.3: + dependencies: + min-dash: 4.2.3 + + mpd-parser@0.22.1: + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/vhs-utils': 3.0.5 + '@xmldom/xmldom': 0.8.11 + global: 4.4.0 + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + multimatch@5.0.0: + dependencies: + '@types/minimatch': 3.0.5 + array-differ: 3.0.0 + array-union: 2.1.0 + arrify: 2.0.1 + minimatch: 3.1.2 + + mux.js@6.0.1: + dependencies: + '@babel/runtime': 7.28.4 + global: 4.4.0 + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + naive-ui@2.43.2(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@css-render/plugin-bem': 0.15.14(css-render@0.15.14) + '@css-render/vue3-ssr': 0.15.14(vue@3.5.25(typescript@5.9.3)) + '@types/katex': 0.16.7 + '@types/lodash': 4.17.21 + '@types/lodash-es': 4.17.12 + async-validator: 4.2.5 + css-render: 0.15.14 + csstype: 3.2.3 + date-fns: 4.1.0 + date-fns-tz: 3.2.0(date-fns@4.1.0) + evtd: 0.2.4 + highlight.js: 11.11.1 + lodash: 4.17.21 + lodash-es: 4.17.21 + seemly: 0.3.10 + treemate: 0.3.11 + vdirs: 0.1.8(vue@3.5.25(typescript@5.9.3)) + vooks: 0.2.12(vue@3.5.25(typescript@5.9.3)) + vue: 3.5.25(typescript@5.9.3) + vueuc: 0.4.65(vue@3.5.25(typescript@5.9.3)) + + nanoid@3.3.11: {} + + nanoid@5.1.6: {} + + nanopop@2.4.2: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + natural-orderby@5.0.0: {} + + needle@3.3.1: + dependencies: + iconv-lite: 0.6.3 + sax: 1.4.3 + optional: true + + nitropack@2.12.9: + dependencies: + '@cloudflare/kv-asset-handler': 0.4.1 + '@rollup/plugin-alias': 5.1.1(rollup@4.53.3) + '@rollup/plugin-commonjs': 28.0.9(rollup@4.53.3) + '@rollup/plugin-inject': 5.0.5(rollup@4.53.3) + '@rollup/plugin-json': 6.1.0(rollup@4.53.3) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.53.3) + '@rollup/plugin-replace': 6.0.3(rollup@4.53.3) + '@rollup/plugin-terser': 0.4.4(rollup@4.53.3) + '@vercel/nft': 0.30.4(rollup@4.53.3) + archiver: 7.0.1 + c12: 3.3.2(magicast@0.5.1) + chokidar: 4.0.3 + citty: 0.1.6 + compatx: 0.2.0 + confbox: 0.2.2 + consola: 3.4.2 + cookie-es: 2.0.0 + croner: 9.1.0 + crossws: 0.3.5 + db0: 0.3.4 + defu: 6.1.4 + destr: 2.0.5 + dot-prop: 10.1.0 + esbuild: 0.25.3 + escape-string-regexp: 5.0.0 + etag: 1.8.1 + exsolve: 1.0.8 + globby: 15.0.0 + gzip-size: 7.0.0 + h3: 1.15.4 + hookable: 5.5.3 + httpxy: 0.1.7 + ioredis: 5.8.2 + jiti: 2.6.1 + klona: 2.0.6 + knitwork: 1.3.0 + listhen: 1.9.0 + magic-string: 0.30.21 + magicast: 0.5.1 + mime: 4.1.0 + mlly: 1.8.0 + node-fetch-native: 1.6.7 + node-mock-http: 1.0.3 + ofetch: 1.5.1 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.0.0 + pkg-types: 2.3.0 + pretty-bytes: 7.1.0 + radix3: 1.1.2 + rollup: 4.53.3 + rollup-plugin-visualizer: 6.0.5(rollup@4.53.3) + scule: 1.3.0 + semver: 7.7.3 + serve-placeholder: 2.0.2 + serve-static: 2.2.0 + source-map: 0.7.6 + std-env: 3.10.0 + ufo: 1.6.1 + ultrahtml: 1.6.0 + uncrypto: 0.1.3 + unctx: 2.4.1 + unenv: 2.0.0-rc.24 + unimport: 5.5.0 + unplugin-utils: 0.3.1 + unstorage: 1.17.3(db0@0.3.4)(ioredis@5.8.2) + untyped: 2.0.0 + unwasm: 0.3.11 + youch: 4.1.0-beta.13 + youch-core: 0.3.3 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@electric-sql/pglite' + - '@libsql/client' + - '@netlify/blobs' + - '@planetscale/database' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - bare-abort-controller + - better-sqlite3 + - drizzle-orm + - encoding + - idb-keyval + - mysql2 + - react-native-b4a + - rolldown + - sqlite3 + - supports-color + - uploadthing + + no-case@3.0.4: + dependencies: + lower-case: 2.0.2 + tslib: 2.8.1 + + node-addon-api@7.1.1: {} + + node-cleanup@2.1.2: {} + + node-domexception@1.0.0: {} + + node-fetch-cache@5.1.0: + dependencies: + cacache: 20.0.3 + formdata-node: 6.0.3 + locko: 1.1.0 + node-fetch: 3.3.2 + + node-fetch-native@1.6.7: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + + node-forge@1.3.2: {} + + node-gyp-build@4.8.4: {} + + node-html-parser@5.4.2: + dependencies: + css-select: 4.3.0 + he: 1.2.0 + + node-mock-http@1.0.3: {} + + node-releases@2.0.27: {} + + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + + nopt@8.1.0: + dependencies: + abbrev: 3.0.1 + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-wheel-es@1.2.0: {} + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + npm-run-path@6.0.0: + dependencies: + path-key: 4.0.0 + unicorn-magic: 0.3.0 + + npm2url@0.2.4: {} + + nprogress@0.2.0: {} + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nypm@0.6.2: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 2.0.3 + pkg-types: 2.3.0 + tinyexec: 1.0.2 + + object-assign@4.1.1: {} + + object-deep-merge@2.0.0: {} + + object-hash@3.0.0: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object-refs@0.3.0: {} + + object-refs@0.4.0: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + ofetch@1.5.1: + dependencies: + destr: 2.0.5 + node-fetch-native: 1.6.7 + ufo: 1.6.1 + + ohash@2.0.11: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + oniguruma-to-es@3.1.1: + dependencies: + emoji-regex-xs: 1.0.0 + regex: 6.0.1 + regex-recursion: 6.0.2 + + open@10.2.0: + dependencies: + default-browser: 5.4.0 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + wsl-utils: 0.1.0 + + open@8.4.2: + dependencies: + define-lazy-prop: 2.0.0 + is-docker: 2.2.1 + is-wsl: 2.2.0 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@8.2.0: + dependencies: + chalk: 5.6.2 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + outdent@0.5.0: {} + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-filter@2.1.0: + dependencies: + p-map: 2.1.0 + + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-limit@4.0.0: + dependencies: + yocto-queue: 1.2.2 + + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + p-locate@6.0.0: + dependencies: + p-limit: 4.0.0 + + p-map@2.1.0: {} + + p-map@7.0.4: {} + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + package-json@10.0.1: + dependencies: + ky: 1.14.0 + registry-auth-token: 5.1.0 + registry-url: 6.0.1 + semver: 7.7.3 + + package-manager-detector@0.2.11: + dependencies: + quansync: 0.2.11 + + package-manager-detector@1.5.0: {} + + pako@2.1.0: {} + + param-case@3.0.4: + dependencies: + dot-case: 3.0.4 + tslib: 2.8.1 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parent-module@2.0.0: + dependencies: + callsites: 3.1.0 + + parse-imports-exports@0.2.4: + dependencies: + parse-statements: 1.0.11 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.27.1 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-ms@4.0.0: {} + + parse-node-version@1.0.1: {} + + parse-passwd@1.0.0: {} + + parse-statements@1.0.11: {} + + parse5-htmlparser2-tree-adapter@7.1.0: + dependencies: + domhandler: 5.0.3 + parse5: 7.3.0 + + parse5-parser-stream@7.1.2: + dependencies: + parse5: 7.3.0 + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + pascal-case@3.1.2: + dependencies: + no-case: 3.0.4 + tslib: 2.8.1 + + path-browserify@1.0.1: {} + + path-exists@4.0.0: {} + + path-exists@5.0.0: {} + + path-intersection@2.2.1: {} + + path-intersection@3.1.0: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-scurry@2.0.1: + dependencies: + lru-cache: 11.2.2 + minipass: 7.1.2 + + path-type@4.0.0: {} + + path-type@6.0.0: {} + + pathe@0.2.0: {} + + pathe@1.1.2: {} + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + perfect-debounce@1.0.0: {} + + perfect-debounce@2.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pify@2.3.0: {} + + pify@4.0.1: {} + + pinia-plugin-persistedstate@4.7.1(@nuxt/kit@4.2.1(magicast@0.5.1))(pinia@3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3))): + dependencies: + defu: 6.1.4 + optionalDependencies: + '@nuxt/kit': 4.2.1(magicast@0.5.1) + pinia: 3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)) + + pinia@3.0.4(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@vue/devtools-api': 7.7.9 + vue: 3.5.25(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + + pirates@4.0.7: {} + + pkcs7@1.0.4: + dependencies: + '@babel/runtime': 7.28.4 + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.0 + pathe: 2.0.3 + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.8 + pathe: 2.0.3 + + playwright-core@1.57.0: {} + + playwright@1.57.0: + dependencies: + playwright-core: 1.57.0 + optionalDependencies: + fsevents: 2.3.2 + + please-upgrade-node@3.2.0: + dependencies: + semver-compare: 1.0.0 + + pluralize@8.0.0: {} + + pngjs@5.0.0: {} + + popmotion@11.0.5: + dependencies: + framesync: 6.1.2 + hey-listen: 1.0.8 + style-value-types: 5.1.2 + tslib: 2.4.0 + + possible-typed-array-names@1.1.0: {} + + postcss-antd-fixes@0.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-attribute-case-insensitive@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-calc@10.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + + postcss-clamp@4.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-color-functional-notation@7.0.12(postcss@8.5.6): + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + postcss-color-hex-alpha@10.0.0(postcss@8.5.6): + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-color-rebeccapurple@10.0.0(postcss@8.5.6): + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-colormin@7.0.5(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + caniuse-api: 3.0.0 + colord: 2.9.3 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-convert-values@7.0.8(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-custom-media@11.0.6(postcss@8.5.6): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.5(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + postcss: 8.5.6 + + postcss-custom-properties@14.0.6(postcss@8.5.6): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.5(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-custom-selectors@8.0.5(postcss@8.5.6): + dependencies: + '@csstools/cascade-layer-name-parser': 2.0.5(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-dir-pseudo-class@9.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-discard-comments@7.0.5(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-discard-duplicates@7.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-empty@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-discard-overridden@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-double-position-gradients@6.0.4(postcss@8.5.6): + dependencies: + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-focus-visible@10.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-focus-within@9.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-font-variant@5.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-gap-properties@6.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-html@1.8.0: + dependencies: + htmlparser2: 8.0.2 + js-tokens: 9.0.1 + postcss: 8.5.6 + postcss-safe-parser: 6.0.0(postcss@8.5.6) + + postcss-image-set-function@7.0.0(postcss@8.5.6): + dependencies: + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-import@15.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.11 + + postcss-import@16.1.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.11 + + postcss-js@4.1.0(postcss@8.5.6): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.5.6 + + postcss-lab-function@7.0.12(postcss@8.5.6): + dependencies: + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/utilities': 2.0.0(postcss@8.5.6) + postcss: 8.5.6 + + postcss-load-config@6.0.1(jiti@2.6.1)(postcss@8.5.6)(yaml@2.8.1): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + jiti: 2.6.1 + postcss: 8.5.6 + yaml: 2.8.1 + + postcss-logical@8.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-media-query-parser@0.2.3: {} + + postcss-merge-longhand@7.0.5(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + stylehacks: 7.0.7(postcss@8.5.6) + + postcss-merge-rules@7.0.7(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + caniuse-api: 3.0.0 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-minify-font-values@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-gradients@7.0.1(postcss@8.5.6): + dependencies: + colord: 2.9.3 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-params@7.0.5(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-minify-selectors@7.0.5(postcss@8.5.6): + dependencies: + cssesc: 3.0.0 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-nested@5.0.6(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-nested@6.2.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-nested@7.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-nesting@13.0.2(postcss@8.5.6): + dependencies: + '@csstools/selector-resolve-nested': 3.1.0(postcss-selector-parser@7.1.0) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-normalize-charset@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-normalize-display-values@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-positions@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-repeat-style@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-string@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-timing-functions@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-unicode@7.0.5(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-url@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-normalize-whitespace@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-opacity-percentage@3.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-ordered-values@7.0.2(postcss@8.5.6): + dependencies: + cssnano-utils: 5.0.1(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-overflow-shorthand@6.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-page-break@3.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-place@10.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-preset-env@10.4.0(postcss@8.5.6): + dependencies: + '@csstools/postcss-alpha-function': 1.0.1(postcss@8.5.6) + '@csstools/postcss-cascade-layers': 5.0.2(postcss@8.5.6) + '@csstools/postcss-color-function': 4.0.12(postcss@8.5.6) + '@csstools/postcss-color-function-display-p3-linear': 1.0.1(postcss@8.5.6) + '@csstools/postcss-color-mix-function': 3.0.12(postcss@8.5.6) + '@csstools/postcss-color-mix-variadic-function-arguments': 1.0.2(postcss@8.5.6) + '@csstools/postcss-content-alt-text': 2.0.8(postcss@8.5.6) + '@csstools/postcss-contrast-color-function': 2.0.12(postcss@8.5.6) + '@csstools/postcss-exponential-functions': 2.0.9(postcss@8.5.6) + '@csstools/postcss-font-format-keywords': 4.0.0(postcss@8.5.6) + '@csstools/postcss-gamut-mapping': 2.0.11(postcss@8.5.6) + '@csstools/postcss-gradients-interpolation-method': 5.0.12(postcss@8.5.6) + '@csstools/postcss-hwb-function': 4.0.12(postcss@8.5.6) + '@csstools/postcss-ic-unit': 4.0.4(postcss@8.5.6) + '@csstools/postcss-initial': 2.0.1(postcss@8.5.6) + '@csstools/postcss-is-pseudo-class': 5.0.3(postcss@8.5.6) + '@csstools/postcss-light-dark-function': 2.0.11(postcss@8.5.6) + '@csstools/postcss-logical-float-and-clear': 3.0.0(postcss@8.5.6) + '@csstools/postcss-logical-overflow': 2.0.0(postcss@8.5.6) + '@csstools/postcss-logical-overscroll-behavior': 2.0.0(postcss@8.5.6) + '@csstools/postcss-logical-resize': 3.0.0(postcss@8.5.6) + '@csstools/postcss-logical-viewport-units': 3.0.4(postcss@8.5.6) + '@csstools/postcss-media-minmax': 2.0.9(postcss@8.5.6) + '@csstools/postcss-media-queries-aspect-ratio-number-values': 3.0.5(postcss@8.5.6) + '@csstools/postcss-nested-calc': 4.0.0(postcss@8.5.6) + '@csstools/postcss-normalize-display-values': 4.0.0(postcss@8.5.6) + '@csstools/postcss-oklab-function': 4.0.12(postcss@8.5.6) + '@csstools/postcss-progressive-custom-properties': 4.2.1(postcss@8.5.6) + '@csstools/postcss-random-function': 2.0.1(postcss@8.5.6) + '@csstools/postcss-relative-color-syntax': 3.0.12(postcss@8.5.6) + '@csstools/postcss-scope-pseudo-class': 4.0.1(postcss@8.5.6) + '@csstools/postcss-sign-functions': 1.1.4(postcss@8.5.6) + '@csstools/postcss-stepped-value-functions': 4.0.9(postcss@8.5.6) + '@csstools/postcss-text-decoration-shorthand': 4.0.3(postcss@8.5.6) + '@csstools/postcss-trigonometric-functions': 4.0.9(postcss@8.5.6) + '@csstools/postcss-unset-value': 4.0.0(postcss@8.5.6) + autoprefixer: 10.4.22(postcss@8.5.6) + browserslist: 4.28.0 + css-blank-pseudo: 7.0.1(postcss@8.5.6) + css-has-pseudo: 7.0.3(postcss@8.5.6) + css-prefers-color-scheme: 10.0.0(postcss@8.5.6) + cssdb: 8.4.2 + postcss: 8.5.6 + postcss-attribute-case-insensitive: 7.0.1(postcss@8.5.6) + postcss-clamp: 4.1.0(postcss@8.5.6) + postcss-color-functional-notation: 7.0.12(postcss@8.5.6) + postcss-color-hex-alpha: 10.0.0(postcss@8.5.6) + postcss-color-rebeccapurple: 10.0.0(postcss@8.5.6) + postcss-custom-media: 11.0.6(postcss@8.5.6) + postcss-custom-properties: 14.0.6(postcss@8.5.6) + postcss-custom-selectors: 8.0.5(postcss@8.5.6) + postcss-dir-pseudo-class: 9.0.1(postcss@8.5.6) + postcss-double-position-gradients: 6.0.4(postcss@8.5.6) + postcss-focus-visible: 10.0.1(postcss@8.5.6) + postcss-focus-within: 9.0.1(postcss@8.5.6) + postcss-font-variant: 5.0.0(postcss@8.5.6) + postcss-gap-properties: 6.0.0(postcss@8.5.6) + postcss-image-set-function: 7.0.0(postcss@8.5.6) + postcss-lab-function: 7.0.12(postcss@8.5.6) + postcss-logical: 8.1.0(postcss@8.5.6) + postcss-nesting: 13.0.2(postcss@8.5.6) + postcss-opacity-percentage: 3.0.0(postcss@8.5.6) + postcss-overflow-shorthand: 6.0.0(postcss@8.5.6) + postcss-page-break: 3.0.4(postcss@8.5.6) + postcss-place: 10.0.0(postcss@8.5.6) + postcss-pseudo-class-any-link: 10.0.1(postcss@8.5.6) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.5.6) + postcss-selector-not: 8.0.1(postcss@8.5.6) + + postcss-pseudo-class-any-link@10.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-reduce-initial@7.0.5(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + caniuse-api: 3.0.0 + postcss: 8.5.6 + + postcss-reduce-transforms@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-replace-overflow-wrap@4.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-resolve-nested-selector@0.1.6: {} + + postcss-safe-parser@6.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-safe-parser@7.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-scss@4.0.9(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-selector-not@8.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-selector-parser@6.0.10: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-selector-parser@7.1.0: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-sorting@8.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-sorting@9.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + postcss-svgo@7.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + svgo: 4.0.0 + + postcss-unique-selectors@7.0.4(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + postcss-value-parser@4.2.0: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.27.2: {} + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier-plugin-tailwindcss@0.7.1(prettier@3.6.2): + dependencies: + prettier: 3.6.2 + + prettier@2.8.8: {} + + prettier@3.6.2: {} + + pretty-bytes@5.6.0: {} + + pretty-bytes@6.1.1: {} + + pretty-bytes@7.1.0: {} + + pretty-ms@9.3.0: + dependencies: + parse-ms: 4.0.0 + + prismjs@1.30.0: {} + + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + property-information@7.1.0: {} + + proto-list@1.2.4: {} + + proxy-from-env@1.1.0: {} + + prr@1.0.1: + optional: true + + publint@0.3.15: + dependencies: + '@publint/pack': 0.1.2 + package-manager-detector: 1.5.0 + picocolors: 1.1.1 + sade: 1.8.1 + + punycode.js@2.3.1: {} + + punycode@2.3.1: {} + + pupa@3.3.0: + dependencies: + escape-goat: 4.0.0 + + qified@0.5.2: + dependencies: + hookified: 1.13.0 + + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + quansync@0.2.11: {} + + queue-microtask@1.2.3: {} + + radix3@1.1.2: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + randomcolor@0.6.2: {} + + range-parser@1.2.1: {} + + rc9@2.1.2: + dependencies: + defu: 6.1.4 + destr: 2.0.5 + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + read-yaml-file@1.1.0: + dependencies: + graceful-fs: 4.2.11 + js-yaml: 3.14.2 + pify: 4.0.1 + strip-bom: 3.0.0 + + read-yaml-file@2.1.0: + dependencies: + js-yaml: 4.1.1 + strip-bom: 4.0.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readdir-glob@1.1.3: + dependencies: + minimatch: 5.1.6 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + readdirp@4.1.2: {} + + redis-errors@1.2.0: {} + + redis-parser@3.0.0: + dependencies: + redis-errors: 1.2.0 + + refa@0.12.1: + dependencies: + '@eslint-community/regexpp': 4.12.2 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regenerate-unicode-properties@10.2.2: + dependencies: + regenerate: 1.4.2 + + regenerate@1.4.2: {} + + regex-recursion@6.0.2: + dependencies: + regex-utilities: 2.3.0 + + regex-utilities@2.3.0: {} + + regex@6.0.1: + dependencies: + regex-utilities: 2.3.0 + + regexp-ast-analysis@0.7.1: + dependencies: + '@eslint-community/regexpp': 4.12.2 + refa: 0.12.1 + + regexp-tree@0.1.27: {} + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + regexpu-core@6.4.0: + dependencies: + regenerate: 1.4.2 + regenerate-unicode-properties: 10.2.2 + regjsgen: 0.8.0 + regjsparser: 0.13.0 + unicode-match-property-ecmascript: 2.0.0 + unicode-match-property-value-ecmascript: 2.2.1 + + registry-auth-token@5.1.0: + dependencies: + '@pnpm/npm-conf': 2.3.1 + + registry-url@6.0.1: + dependencies: + rc: 1.2.8 + + regjsgen@0.8.0: {} + + regjsparser@0.13.0: + dependencies: + jsesc: 3.1.0 + + reka-ui@2.6.0(typescript@5.9.3)(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@floating-ui/dom': 1.7.4 + '@floating-ui/vue': 1.1.9(vue@3.5.25(typescript@5.9.3)) + '@internationalized/date': 3.10.0 + '@internationalized/number': 3.6.5 + '@tanstack/vue-virtual': 3.13.12(vue@3.5.25(typescript@5.9.3)) + '@vueuse/core': 12.8.2(typescript@5.9.3) + '@vueuse/shared': 12.8.2(typescript@5.9.3) + aria-hidden: 1.2.6 + defu: 6.1.4 + ohash: 2.0.11 + vue: 3.5.25(typescript@5.9.3) + transitivePeerDependencies: + - '@vue/composition-api' + - typescript + + relateurl@0.2.7: {} + + remarkable-katex@1.2.1: {} + + remarkable@2.0.1: + dependencies: + argparse: 1.0.10 + autolinker: 3.16.2 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + require-main-filename@2.0.0: {} + + require-package-name@2.0.1: {} + + reserved-identifiers@1.2.0: {} + + resize-observer-polyfill@1.5.1: {} + + resolve-dir@1.0.1: + dependencies: + expand-tilde: 2.0.2 + global-modules: 1.0.0 + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve.exports@2.0.3: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@6.1.2: + dependencies: + glob: 13.0.0 + package-json-from-dist: 1.0.1 + + robust-predicates@3.0.2: {} + + rollup-plugin-dts@6.3.0(rollup@4.53.3)(typescript@5.9.3): + dependencies: + magic-string: 0.30.21 + rollup: 4.53.3 + typescript: 5.9.3 + optionalDependencies: + '@babel/code-frame': 7.27.1 + + rollup-plugin-visualizer@5.14.0(rollup@4.53.3): + dependencies: + open: 8.4.2 + picomatch: 4.0.3 + source-map: 0.7.6 + yargs: 17.7.2 + optionalDependencies: + rollup: 4.53.3 + + rollup-plugin-visualizer@6.0.5(rollup@4.53.3): + dependencies: + open: 8.4.2 + picomatch: 4.0.3 + source-map: 0.7.6 + yargs: 17.7.2 + optionalDependencies: + rollup: 4.53.3 + + rollup@2.79.2: + optionalDependencies: + fsevents: 2.3.3 + + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + + rotated-array-set@3.0.0: {} + + run-applescript@7.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rust-result@1.0.0: + dependencies: + individual: 2.0.0 + + rw@1.3.3: {} + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-json-parse@4.0.0: + dependencies: + rust-result: 1.0.0 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + safer-buffer@2.1.2: {} + + sass@1.94.2: + dependencies: + chokidar: 4.0.3 + immutable: 5.1.4 + source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 + + sax@1.4.3: {} + + saxen@8.1.2: {} + + scroll-into-view-if-needed@2.2.31: + dependencies: + compute-scroll-into-view: 1.0.20 + + scslre@0.3.0: + dependencies: + '@eslint-community/regexpp': 4.12.2 + refa: 0.12.1 + regexp-ast-analysis: 0.7.1 + + scule@1.3.0: {} + + search-insights@2.17.3: {} + + section-matter@1.0.0: + dependencies: + extend-shallow: 2.0.1 + kind-of: 6.0.3 + + secure-ls@2.0.0: + dependencies: + crypto-js: 4.2.0 + lz-string: 1.5.0 + + seemly@0.3.10: {} + + select@1.1.2: {} + + semver-compare@1.0.0: {} + + semver@5.7.2: + optional: true + + semver@6.3.1: {} + + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + + semver@7.7.3: {} + + send@1.2.0: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + serve-placeholder@2.0.2: + dependencies: + defu: 6.1.4 + + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + + set-blocking@2.0.0: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setprototypeof@1.2.0: {} + + shallow-equal@1.2.1: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shiki@2.5.0: + dependencies: + '@shikijs/core': 2.5.0 + '@shikijs/engine-javascript': 2.5.0 + '@shikijs/engine-oniguruma': 2.5.0 + '@shikijs/langs': 2.5.0 + '@shikijs/themes': 2.5.0 + '@shikijs/types': 2.5.0 + '@shikijs/vscode-textmate': 10.0.2 + '@types/hast': 3.0.4 + + short-tree@3.0.0: + dependencies: + '@types/bintrees': 1.0.6 + bintrees: 1.0.2 + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + signature_pad@3.0.0-beta.4: {} + + signature_pad@5.1.2: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + sisteransi@1.0.5: {} + + slash@3.0.0: {} + + slash@5.1.0: {} + + slice-ansi@4.0.0: + dependencies: + ansi-styles: 4.3.0 + astral-regex: 2.0.0 + is-fullwidth-code-point: 3.0.0 + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + smob@1.5.0: {} + + sortablejs@1.14.0: {} + + sortablejs@1.15.6: {} + + source-map-js@1.2.1: {} + + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map@0.6.1: {} + + source-map@0.7.6: {} + + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + sourcemap-codec@1.4.8: {} + + space-separated-tokens@2.0.2: {} + + spawndamnit@3.0.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@4.0.0: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.22 + + spdx-license-ids@3.0.22: {} + + speakingurl@14.0.1: {} + + split2@4.2.0: {} + + sprintf-js@1.0.3: {} + + ssri@13.0.0: + dependencies: + minipass: 7.1.2 + + stable-hash-x@0.2.0: {} + + stackback@0.0.2: {} + + standard-as-callback@2.1.0: {} + + statuses@2.0.2: {} + + std-env@3.10.0: {} + + stdin-discarder@0.2.2: {} + + steady-xml@0.1.0: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + streamx@2.23.0: + dependencies: + events-universal: 1.0.1 + fast-fifo: 1.3.2 + text-decoder: 1.2.3 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.4.0 + strip-ansi: 7.1.2 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + stringify-object@3.3.0: + dependencies: + get-own-enumerable-property-symbols: 3.0.2 + is-obj: 1.0.1 + is-regexp: 1.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + + strip-bom-string@1.0.0: {} + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-comments@2.0.1: {} + + strip-final-newline@3.0.0: {} + + strip-final-newline@4.0.0: {} + + strip-indent@4.1.1: {} + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + strnum@1.1.2: {} + + stubborn-fs@2.0.0: + dependencies: + stubborn-utils: 1.0.2 + + stubborn-utils@1.0.2: {} + + style-mod@4.1.3: {} + + style-search@0.1.0: {} + + style-value-types@5.1.2: + dependencies: + hey-listen: 1.0.8 + tslib: 2.4.0 + + stylehacks@7.0.7(postcss@8.5.6): + dependencies: + browserslist: 4.28.0 + postcss: 8.5.6 + postcss-selector-parser: 7.1.0 + + stylelint-config-html@1.1.0(postcss-html@1.8.0)(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + postcss-html: 1.8.0 + stylelint: 16.26.0(typescript@5.9.3) + + stylelint-config-recess-order@6.1.0(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + stylelint: 16.26.0(typescript@5.9.3) + stylelint-order: 6.0.4(stylelint@16.26.0(typescript@5.9.3)) + + stylelint-config-recommended-scss@14.1.0(postcss@8.5.6)(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + postcss-scss: 4.0.9(postcss@8.5.6) + stylelint: 16.26.0(typescript@5.9.3) + stylelint-config-recommended: 14.0.1(stylelint@16.26.0(typescript@5.9.3)) + stylelint-scss: 6.12.1(stylelint@16.26.0(typescript@5.9.3)) + optionalDependencies: + postcss: 8.5.6 + + stylelint-config-recommended-vue@1.6.1(postcss-html@1.8.0)(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + postcss-html: 1.8.0 + semver: 7.7.3 + stylelint: 16.26.0(typescript@5.9.3) + stylelint-config-html: 1.1.0(postcss-html@1.8.0)(stylelint@16.26.0(typescript@5.9.3)) + stylelint-config-recommended: 17.0.0(stylelint@16.26.0(typescript@5.9.3)) + + stylelint-config-recommended@14.0.1(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + stylelint: 16.26.0(typescript@5.9.3) + + stylelint-config-recommended@16.0.0(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + stylelint: 16.26.0(typescript@5.9.3) + + stylelint-config-recommended@17.0.0(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + stylelint: 16.26.0(typescript@5.9.3) + + stylelint-config-standard@38.0.0(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + stylelint: 16.26.0(typescript@5.9.3) + stylelint-config-recommended: 16.0.0(stylelint@16.26.0(typescript@5.9.3)) + + stylelint-order@6.0.4(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + postcss: 8.5.6 + postcss-sorting: 8.0.2(postcss@8.5.6) + stylelint: 16.26.0(typescript@5.9.3) + + stylelint-order@7.0.0(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + postcss: 8.5.6 + postcss-sorting: 9.1.0(postcss@8.5.6) + stylelint: 16.26.0(typescript@5.9.3) + + stylelint-prettier@5.0.3(prettier@3.6.2)(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + stylelint: 16.26.0(typescript@5.9.3) + + stylelint-scss@6.12.1(stylelint@16.26.0(typescript@5.9.3)): + dependencies: + css-tree: 3.1.0 + is-plain-object: 5.0.0 + known-css-properties: 0.36.0 + mdn-data: 2.25.0 + postcss-media-query-parser: 0.2.3 + postcss-resolve-nested-selector: 0.1.6 + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + stylelint: 16.26.0(typescript@5.9.3) + + stylelint@16.26.0(typescript@5.9.3): + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + '@csstools/media-query-list-parser': 4.0.3(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/selector-specificity': 5.0.0(postcss-selector-parser@7.1.0) + '@dual-bundle/import-meta-resolve': 4.2.1 + balanced-match: 2.0.0 + colord: 2.9.3 + cosmiconfig: 9.0.0(typescript@5.9.3) + css-functions-list: 3.2.3 + css-tree: 3.1.0 + debug: 4.4.3 + fast-glob: 3.3.3 + fastest-levenshtein: 1.0.16 + file-entry-cache: 11.1.1 + global-modules: 2.0.0 + globby: 11.1.0 + globjoin: 0.1.4 + html-tags: 3.3.1 + ignore: 7.0.5 + imurmurhash: 0.1.4 + is-plain-object: 5.0.0 + known-css-properties: 0.37.0 + mathml-tag-names: 2.1.3 + meow: 13.2.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-resolve-nested-selector: 0.1.6 + postcss-safe-parser: 7.0.1(postcss@8.5.6) + postcss-selector-parser: 7.1.0 + postcss-value-parser: 4.2.0 + resolve-from: 5.0.0 + string-width: 4.2.3 + supports-hyperlinks: 3.2.0 + svg-tags: 1.0.0 + table: 6.9.0 + write-file-atomic: 5.0.1 + transitivePeerDependencies: + - supports-color + - typescript + + stylis@4.3.6: {} + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.15 + ts-interface-checker: 0.1.13 + + superjson@2.2.5: + dependencies: + copy-anything: 4.0.5 + + supports-color@10.2.2: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@3.2.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svelte@5.45.2: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.7(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.5.0 + esm-env: 1.2.2 + esrap: 2.2.0 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + + sver@1.8.4: + optionalDependencies: + semver: 6.3.1 + + svg-tags@1.0.0: {} + + svgo@4.0.0: + dependencies: + commander: 11.1.0 + css-select: 5.2.2 + css-tree: 3.1.0 + css-what: 6.2.2 + csso: 5.0.5 + picocolors: 1.1.1 + sax: 1.4.3 + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + system-architecture@0.1.0: {} + + tabbable@6.3.0: {} + + table@6.9.0: + dependencies: + ajv: 8.17.1 + lodash.truncate: 4.4.2 + slice-ansi: 4.0.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + tagged-tag@1.0.0: {} + + tailwind-merge@2.6.0: {} + + tailwindcss-animate@1.0.7(tailwindcss@3.4.18(yaml@2.8.1)): + dependencies: + tailwindcss: 3.4.18(yaml@2.8.1) + + tailwindcss@3.4.18(yaml@2.8.1): + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.3 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 2.6.1 + lilconfig: 3.1.3 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@2.6.1)(postcss@8.5.6)(yaml@2.8.1) + postcss-nested: 6.2.0(postcss@8.5.6) + postcss-selector-parser: 6.1.2 + resolve: 1.22.11 + sucrase: 3.35.1 + transitivePeerDependencies: + - tsx + - yaml + + tapable@2.3.0: {} + + tar-stream@3.1.7: + dependencies: + b4a: 1.7.3 + fast-fifo: 1.3.2 + streamx: 2.23.0 + transitivePeerDependencies: + - bare-abort-controller + - react-native-b4a + + tar@7.5.2: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 + + tdesign-icons-vue-next@0.4.1(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@babel/runtime': 7.28.4 + vue: 3.5.25(typescript@5.9.3) + + tdesign-vue-next@1.17.3(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@babel/runtime': 7.28.4 + '@popperjs/core': 2.11.8 + '@types/lodash-es': 4.17.12 + '@types/sortablejs': 1.15.9 + '@types/tinycolor2': 1.4.6 + '@types/validator': 13.15.10 + dayjs: 1.11.19 + lodash-es: 4.17.21 + mitt: 3.0.1 + sortablejs: 1.15.6 + tdesign-icons-vue-next: 0.4.1(vue@3.5.25(typescript@5.9.3)) + tinycolor2: 1.6.0 + validator: 13.15.23 + vue: 3.5.25(typescript@5.9.3) + + temp-dir@2.0.0: {} + + tempy@0.6.0: + dependencies: + is-stream: 2.0.1 + temp-dir: 2.0.0 + type-fest: 0.16.0 + unique-string: 2.0.0 + + term-size@2.2.1: {} + + terser@5.44.1: + dependencies: + '@jridgewell/source-map': 0.3.11 + acorn: 8.15.0 + commander: 2.20.3 + source-map-support: 0.5.21 + + text-decoder@1.2.3: + dependencies: + b4a: 1.7.3 + transitivePeerDependencies: + - react-native-b4a + + text-extensions@2.4.0: {} + + theme-colors@0.1.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + throttle-debounce@5.0.2: {} + + through@2.3.8: {} + + tiny-emitter@2.1.0: {} + + tiny-svg@3.1.3: {} + + tinybench@2.9.0: {} + + tinycolor2@1.6.0: {} + + tinyexec@0.3.2: {} + + tinyexec@1.0.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinymce@7.9.1: {} + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + tippy.js@6.3.7: + dependencies: + '@popperjs/core': 2.11.8 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + to-valid-identifier@1.0.0: + dependencies: + '@sindresorhus/base62': 1.0.0 + reserved-identifiers: 1.2.0 + + toidentifier@1.0.1: {} + + totalist@3.0.1: {} + + tr46@0.0.3: {} + + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + + treemate@0.3.11: {} + + trim-lines@3.0.1: {} + + ts-api-utils@1.4.3(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + ts-declaration-location@1.0.7(typescript@5.9.3): + dependencies: + picomatch: 4.0.3 + typescript: 5.9.3 + + ts-interface-checker@0.1.13: {} + + tslib@2.3.0: {} + + tslib@2.4.0: {} + + tslib@2.8.1: {} + + turbo-darwin-64@2.6.1: + optional: true + + turbo-darwin-arm64@2.6.1: + optional: true + + turbo-linux-64@2.6.1: + optional: true + + turbo-linux-arm64@2.6.1: + optional: true + + turbo-windows-64@2.6.1: + optional: true + + turbo-windows-arm64@2.6.1: + optional: true + + turbo@2.6.1: + optionalDependencies: + turbo-darwin-64: 2.6.1 + turbo-darwin-arm64: 2.6.1 + turbo-linux-64: 2.6.1 + turbo-linux-arm64: 2.6.1 + turbo-windows-64: 2.6.1 + turbo-windows-arm64: 2.6.1 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.16.0: {} + + type-fest@4.41.0: {} + + type-fest@5.2.0: + dependencies: + tagged-tag: 1.0.0 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.8.2: {} + + typescript@5.9.3: {} + + uc.micro@2.1.0: {} + + ufo@1.6.1: {} + + ultrahtml@1.6.0: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + unbuild@3.6.1(sass@1.94.2)(typescript@5.9.3)(vue-tsc@3.1.5(typescript@5.9.3))(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@rollup/plugin-alias': 5.1.1(rollup@4.53.3) + '@rollup/plugin-commonjs': 28.0.9(rollup@4.53.3) + '@rollup/plugin-json': 6.1.0(rollup@4.53.3) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.53.3) + '@rollup/plugin-replace': 6.0.3(rollup@4.53.3) + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + citty: 0.1.6 + consola: 3.4.2 + defu: 6.1.4 + esbuild: 0.25.3 + fix-dts-default-cjs-exports: 1.0.1 + hookable: 5.5.3 + jiti: 2.6.1 + magic-string: 0.30.21 + mkdist: 2.4.1(sass@1.94.2)(typescript@5.9.3)(vue-tsc@3.1.5(typescript@5.9.3))(vue@3.5.25(typescript@5.9.3)) + mlly: 1.8.0 + pathe: 2.0.3 + pkg-types: 2.3.0 + pretty-bytes: 7.1.0 + rollup: 4.53.3 + rollup-plugin-dts: 6.3.0(rollup@4.53.3)(typescript@5.9.3) + scule: 1.3.0 + tinyglobby: 0.2.15 + untyped: 2.0.0 + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - sass + - vue + - vue-sfc-transformer + - vue-tsc + + uncrypto@0.1.3: {} + + unctx@2.4.1: + dependencies: + acorn: 8.15.0 + estree-walker: 3.0.3 + magic-string: 0.30.21 + unplugin: 2.3.11 + + undici-types@7.16.0: {} + + undici@7.16.0: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + + unicode-canonical-property-names-ecmascript@2.0.1: {} + + unicode-match-property-ecmascript@2.0.0: + dependencies: + unicode-canonical-property-names-ecmascript: 2.0.1 + unicode-property-aliases-ecmascript: 2.2.0 + + unicode-match-property-value-ecmascript@2.2.1: {} + + unicode-property-aliases-ecmascript@2.2.0: {} + + unicorn-magic@0.1.0: {} + + unicorn-magic@0.3.0: {} + + unimport@5.5.0: + dependencies: + acorn: 8.15.0 + escape-string-regexp: 5.0.0 + estree-walker: 3.0.3 + local-pkg: 1.1.2 + magic-string: 0.30.21 + mlly: 1.8.0 + pathe: 2.0.3 + picomatch: 4.0.3 + pkg-types: 2.3.0 + scule: 1.3.0 + strip-literal: 3.1.0 + tinyglobby: 0.2.15 + unplugin: 2.3.11 + unplugin-utils: 0.3.1 + + unique-filename@5.0.0: + dependencies: + unique-slug: 6.0.0 + + unique-slug@6.0.0: + dependencies: + imurmurhash: 0.1.4 + + unique-string@2.0.0: + dependencies: + crypto-random-string: 2.0.0 + + 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.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + universalify@0.1.2: {} + + universalify@2.0.1: {} + + unplugin-element-plus@0.11.1(magicast@0.5.1): + dependencies: + '@nuxt/kit': 4.2.1(magicast@0.5.1) + es-module-lexer: 1.7.0 + escape-string-regexp: 5.0.0 + magic-string: 0.30.21 + unplugin: 2.3.11 + transitivePeerDependencies: + - magicast + + unplugin-utils@0.3.1: + dependencies: + pathe: 2.0.3 + picomatch: 4.0.3 + + unplugin@1.16.1: + dependencies: + acorn: 8.15.0 + webpack-virtual-modules: 0.6.2 + + unplugin@2.3.11: + dependencies: + '@jridgewell/remapping': 2.3.5 + acorn: 8.15.0 + picomatch: 4.0.3 + webpack-virtual-modules: 0.6.2 + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + unstorage@1.17.3(db0@0.3.4)(ioredis@5.8.2): + dependencies: + anymatch: 3.1.3 + chokidar: 4.0.3 + destr: 2.0.5 + h3: 1.15.4 + lru-cache: 10.4.3 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.1 + optionalDependencies: + db0: 0.3.4 + ioredis: 5.8.2 + + untun@0.1.3: + dependencies: + citty: 0.1.6 + consola: 3.4.2 + pathe: 1.1.2 + + untyped@2.0.0: + dependencies: + citty: 0.1.6 + defu: 6.1.4 + jiti: 2.6.1 + knitwork: 1.3.0 + scule: 1.3.0 + + unwasm@0.3.11: + dependencies: + knitwork: 1.3.0 + magic-string: 0.30.21 + mlly: 1.8.0 + pathe: 2.0.3 + pkg-types: 2.3.0 + unplugin: 2.3.11 + + upath@1.2.0: {} + + update-browserslist-db@1.1.4(browserslist@4.28.0): + dependencies: + browserslist: 4.28.0 + escalade: 3.2.0 + picocolors: 1.1.1 + + update-notifier@7.3.1: + dependencies: + boxen: 8.0.1 + chalk: 5.6.2 + configstore: 7.1.0 + is-in-ci: 1.0.0 + is-installed-globally: 1.0.0 + is-npm: 6.1.0 + latest-version: 9.0.0 + pupa: 3.3.0 + semver: 7.7.3 + xdg-basedir: 5.1.0 + + uqr@0.1.2: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + url-toolkit@2.2.5: {} + + util-deprecate@1.0.2: {} + + validator@13.15.23: {} + + vdirs@0.1.8(vue@3.5.25(typescript@5.9.3)): + dependencies: + evtd: 0.2.4 + vue: 3.5.25(typescript@5.9.3) + + vee-validate@4.15.1(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@vue/devtools-api': 7.7.9 + type-fest: 4.41.0 + vue: 3.5.25(typescript@5.9.3) + + 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 + + video.js@7.21.7: + dependencies: + '@babel/runtime': 7.28.4 + '@videojs/http-streaming': 2.16.3(video.js@7.21.7) + '@videojs/vhs-utils': 3.0.5 + '@videojs/xhr': 2.6.0 + aes-decrypter: 3.1.3 + global: 4.4.0 + keycode: 2.2.1 + m3u8-parser: 4.8.0 + mpd-parser: 0.22.1 + mux.js: 6.0.1 + safe-json-parse: 4.0.0 + videojs-font: 3.2.0 + videojs-vtt.js: 0.15.5 + + videojs-font@3.2.0: {} + + videojs-vtt.js@0.15.5: + dependencies: + global: 4.4.0 + + vite-dev-rpc@1.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)): + dependencies: + birpc: 2.8.0 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vite-hot-client: 2.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + + vite-hot-client@2.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)): + dependencies: + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + + vite-node@3.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite-plugin-compression@0.5.1(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)): + dependencies: + chalk: 4.1.2 + debug: 4.4.3 + fs-extra: 10.1.0 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + + vite-plugin-dts@4.5.4(@types/node@24.10.1)(rollup@4.53.3)(typescript@5.9.3)(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)): + dependencies: + '@microsoft/api-extractor': 7.55.1(@types/node@24.10.1) + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + '@volar/typescript': 2.4.26 + '@vue/language-core': 2.2.0(typescript@5.9.3) + compare-versions: 6.1.1 + debug: 4.4.3 + kolorist: 1.8.0 + local-pkg: 1.1.2 + magic-string: 0.30.21 + typescript: 5.9.3 + optionalDependencies: + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + + vite-plugin-html@3.2.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)): + dependencies: + '@rollup/pluginutils': 4.2.1 + colorette: 2.0.20 + connect-history-api-fallback: 1.6.0 + consola: 2.15.3 + dotenv: 16.6.1 + dotenv-expand: 8.0.3 + ejs: 3.1.10 + fast-glob: 3.3.3 + fs-extra: 10.1.0 + html-minifier-terser: 6.1.0 + node-html-parser: 5.4.2 + pathe: 0.2.0 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + + vite-plugin-inspect@11.3.3(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)): + dependencies: + ansis: 4.2.0 + debug: 4.4.3 + error-stack-parser-es: 1.0.5 + ohash: 2.0.11 + open: 10.2.0 + perfect-debounce: 2.0.0 + sirv: 3.0.2 + unplugin-utils: 0.3.1 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vite-dev-rpc: 1.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + transitivePeerDependencies: + - supports-color + + vite-plugin-lazy-import@1.0.7: + dependencies: + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) + es-module-lexer: 1.7.0 + rollup: 4.53.3 + xe-utils: 3.7.9 + + vite-plugin-pwa@1.1.0(vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1))(workbox-build@7.4.0)(workbox-window@7.4.0): + dependencies: + debug: 4.4.3 + pretty-bytes: 6.1.1 + tinyglobby: 0.2.15 + vite: 5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1) + workbox-build: 7.4.0 + workbox-window: 7.4.0 + transitivePeerDependencies: + - supports-color + + vite-plugin-pwa@1.1.0(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(workbox-build@7.4.0)(workbox-window@7.4.0): + dependencies: + debug: 4.4.3 + pretty-bytes: 6.1.1 + tinyglobby: 0.2.15 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + workbox-build: 7.4.0 + workbox-window: 7.4.0 + transitivePeerDependencies: + - supports-color + + vite-plugin-vue-devtools@8.0.5(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@vue/devtools-core': 8.0.5(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1))(vue@3.5.25(typescript@5.9.3)) + '@vue/devtools-kit': 8.0.5 + '@vue/devtools-shared': 8.0.5 + sirv: 3.0.2 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vite-plugin-inspect: 11.3.3(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + vite-plugin-vue-inspector: 5.3.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + transitivePeerDependencies: + - '@nuxt/kit' + - supports-color + - vue + + vite-plugin-vue-inspector@5.3.2(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)): + dependencies: + '@babel/core': 7.28.5 + '@babel/plugin-proposal-decorators': 7.28.0(@babel/core@7.28.5) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5) + '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) + '@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.5) + '@vue/compiler-dom': 3.5.25 + kolorist: 1.8.0 + magic-string: 0.30.21 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + transitivePeerDependencies: + - supports-color + + vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1): + dependencies: + esbuild: 0.25.3 + postcss: 8.5.6 + rollup: 4.53.3 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + less: 4.4.2 + sass: 1.94.2 + terser: 5.44.1 + + vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1): + dependencies: + esbuild: 0.25.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 24.10.1 + fsevents: 2.3.3 + jiti: 2.6.1 + less: 4.4.2 + sass: 1.94.2 + terser: 5.44.1 + yaml: 2.8.1 + + vitepress-plugin-group-icons@1.6.5(vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)): + dependencies: + '@iconify-json/logos': 1.2.10 + '@iconify-json/vscode-icons': 1.2.36 + '@iconify/utils': 3.0.2 + optionalDependencies: + vite: 5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1) + transitivePeerDependencies: + - supports-color + + vitepress@1.6.4(@algolia/client-search@5.45.0)(@types/node@24.10.1)(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(less@4.4.2)(nprogress@0.2.0)(postcss@8.5.6)(qrcode@1.5.4)(sass@1.94.2)(search-insights@2.17.3)(sortablejs@1.15.6)(terser@5.44.1)(typescript@5.9.3): + dependencies: + '@docsearch/css': 3.8.2 + '@docsearch/js': 3.8.2(@algolia/client-search@5.45.0)(search-insights@2.17.3) + '@iconify-json/simple-icons': 1.2.60 + '@shikijs/core': 2.5.0 + '@shikijs/transformers': 2.5.0 + '@shikijs/types': 2.5.0 + '@types/markdown-it': 14.1.2 + '@vitejs/plugin-vue': 5.2.4(vite@5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1))(vue@3.5.25(typescript@5.9.3)) + '@vue/devtools-api': 7.7.9 + '@vue/shared': 3.5.25 + '@vueuse/core': 12.8.2(typescript@5.9.3) + '@vueuse/integrations': 12.8.2(async-validator@4.2.5)(axios@1.13.2)(change-case@5.4.4)(focus-trap@7.6.6)(nprogress@0.2.0)(qrcode@1.5.4)(sortablejs@1.15.6)(typescript@5.9.3) + focus-trap: 7.6.6 + mark.js: 8.11.1 + minisearch: 7.2.0 + shiki: 2.5.0 + vite: 5.4.21(@types/node@24.10.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1) + vue: 3.5.25(typescript@5.9.3) + optionalDependencies: + postcss: 8.5.6 + transitivePeerDependencies: + - '@algolia/client-search' + - '@types/node' + - '@types/react' + - async-validator + - axios + - change-case + - drauu + - fuse.js + - idb-keyval + - jwt-decode + - less + - lightningcss + - nprogress + - qrcode + - react + - react-dom + - sass + - sass-embedded + - search-insights + - sortablejs + - stylus + - sugarss + - terser + - typescript + - universal-cookie + + vitest@3.2.4(@types/node@24.10.1)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.2.2 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.94.2)(terser@5.44.1)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.10.1 + happy-dom: 17.6.3 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vooks@0.2.12(vue@3.5.25(typescript@5.9.3)): + dependencies: + evtd: 0.2.4 + vue: 3.5.25(typescript@5.9.3) + + vscode-languageserver-textdocument@1.0.12: {} + + vscode-uri@3.1.0: {} + + vue-component-type-helpers@2.2.12: {} + + vue-demi@0.14.10(vue@3.5.25(typescript@5.9.3)): + dependencies: + vue: 3.5.25(typescript@5.9.3) + + vue-dompurify-html@5.3.0(vue@3.5.25(typescript@5.9.3)): + dependencies: + dompurify: 3.3.0 + vue: 3.5.25(typescript@5.9.3) + + vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@2.6.1)): + dependencies: + debug: 4.4.3 + eslint: 9.39.1(jiti@2.6.1) + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + + vue-i18n@11.2.2(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@intlify/core-base': 11.2.2 + '@intlify/shared': 11.2.2 + '@vue/devtools-api': 6.6.4 + vue: 3.5.25(typescript@5.9.3) + + vue-json-viewer@3.0.4(vue@3.5.25(typescript@5.9.3)): + dependencies: + clipboard: 2.0.11 + vue: 3.5.25(typescript@5.9.3) + + vue-router@4.6.3(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@vue/devtools-api': 6.6.4 + vue: 3.5.25(typescript@5.9.3) + + vue-tippy@6.7.1(vue@3.5.25(typescript@5.9.3)): + dependencies: + tippy.js: 6.3.7 + vue: 3.5.25(typescript@5.9.3) + + vue-tsc@3.1.5(typescript@5.9.3): + dependencies: + '@volar/typescript': 2.4.23 + '@vue/language-core': 3.1.5(typescript@5.9.3) + typescript: 5.9.3 + + vue-types@3.0.2(vue@3.5.25(typescript@5.9.3)): + dependencies: + is-plain-object: 3.0.1 + vue: 3.5.25(typescript@5.9.3) + + vue3-print-nb@0.1.4(typescript@5.9.3): + dependencies: + vue: 3.5.25(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + vue3-signature@0.2.4(vue@3.5.25(typescript@5.9.3)): + dependencies: + default-passive-events: 2.0.0 + signature_pad: 3.0.0-beta.4 + vue: 3.5.25(typescript@5.9.3) + + vue@3.5.25(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.5.25 + '@vue/compiler-sfc': 3.5.25 + '@vue/runtime-dom': 3.5.25 + '@vue/server-renderer': 3.5.25(vue@3.5.25(typescript@5.9.3)) + '@vue/shared': 3.5.25 + optionalDependencies: + typescript: 5.9.3 + + vuedraggable@4.1.0(vue@3.5.25(typescript@5.9.3)): + dependencies: + sortablejs: 1.14.0 + vue: 3.5.25(typescript@5.9.3) + + vueuc@0.4.65(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@css-render/vue3-ssr': 0.15.14(vue@3.5.25(typescript@5.9.3)) + '@juggle/resize-observer': 3.4.0 + css-render: 0.15.14 + evtd: 0.2.4 + seemly: 0.3.10 + vdirs: 0.1.8(vue@3.5.25(typescript@5.9.3)) + vooks: 0.2.12(vue@3.5.25(typescript@5.9.3)) + vue: 3.5.25(typescript@5.9.3) + + vxe-pc-ui@4.10.31(vue@3.5.25(typescript@5.9.3)): + dependencies: + '@vxe-ui/core': 4.2.15(vue@3.5.25(typescript@5.9.3)) + transitivePeerDependencies: + - vue + + vxe-table@4.17.20(vue@3.5.25(typescript@5.9.3)): + dependencies: + vxe-pc-ui: 4.10.31(vue@3.5.25(typescript@5.9.3)) + transitivePeerDependencies: + - vue + + w3c-keyname@2.2.8: {} + + wangeditor@4.7.15: + dependencies: + '@babel/runtime': 7.28.4 + '@babel/runtime-corejs3': 7.28.4 + tslib: 2.8.1 + + warning@4.0.3: + dependencies: + loose-envify: 1.4.0 + + watermark-js-plus@1.6.3: {} + + web-streams-polyfill@3.3.3: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@4.0.2: {} + + webidl-conversions@7.0.0: {} + + webpack-virtual-modules@0.6.2: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@3.0.0: {} + + whatwg-mimetype@4.0.0: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + when-exit@2.1.5: {} + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-module@2.0.1: {} + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + word-wrap@1.2.5: {} + + workbox-background-sync@7.4.0: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.0 + + workbox-broadcast-update@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-build@7.4.0: + dependencies: + '@apideck/better-ajv-errors': 0.3.6(ajv@8.17.1) + '@babel/core': 7.28.5 + '@babel/preset-env': 7.28.5(@babel/core@7.28.5) + '@babel/runtime': 7.28.4 + '@rollup/plugin-babel': 5.3.1(@babel/core@7.28.5)(rollup@2.79.2) + '@rollup/plugin-node-resolve': 15.3.1(rollup@2.79.2) + '@rollup/plugin-replace': 2.4.2(rollup@2.79.2) + '@rollup/plugin-terser': 0.4.4(rollup@2.79.2) + '@surma/rollup-plugin-off-main-thread': 2.2.3 + ajv: 8.17.1 + common-tags: 1.8.2 + fast-json-stable-stringify: 2.1.0 + fs-extra: 9.1.0 + glob: 11.1.0 + lodash: 4.17.21 + pretty-bytes: 5.6.0 + rollup: 2.79.2 + source-map: 0.8.0-beta.0 + stringify-object: 3.3.0 + strip-comments: 2.0.1 + tempy: 0.6.0 + upath: 1.2.0 + workbox-background-sync: 7.4.0 + workbox-broadcast-update: 7.4.0 + workbox-cacheable-response: 7.4.0 + workbox-core: 7.4.0 + workbox-expiration: 7.4.0 + workbox-google-analytics: 7.4.0 + workbox-navigation-preload: 7.4.0 + workbox-precaching: 7.4.0 + workbox-range-requests: 7.4.0 + workbox-recipes: 7.4.0 + workbox-routing: 7.4.0 + workbox-strategies: 7.4.0 + workbox-streams: 7.4.0 + workbox-sw: 7.4.0 + workbox-window: 7.4.0 + transitivePeerDependencies: + - '@types/babel__core' + - supports-color + + workbox-cacheable-response@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-core@7.4.0: {} + + workbox-expiration@7.4.0: + dependencies: + idb: 7.1.1 + workbox-core: 7.4.0 + + workbox-google-analytics@7.4.0: + dependencies: + workbox-background-sync: 7.4.0 + workbox-core: 7.4.0 + workbox-routing: 7.4.0 + workbox-strategies: 7.4.0 + + workbox-navigation-preload@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-precaching@7.4.0: + dependencies: + workbox-core: 7.4.0 + workbox-routing: 7.4.0 + workbox-strategies: 7.4.0 + + workbox-range-requests@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-recipes@7.4.0: + dependencies: + workbox-cacheable-response: 7.4.0 + workbox-core: 7.4.0 + workbox-expiration: 7.4.0 + workbox-precaching: 7.4.0 + workbox-routing: 7.4.0 + workbox-strategies: 7.4.0 + + workbox-routing@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-strategies@7.4.0: + dependencies: + workbox-core: 7.4.0 + + workbox-streams@7.4.0: + dependencies: + workbox-core: 7.4.0 + workbox-routing: 7.4.0 + + workbox-sw@7.4.0: {} + + workbox-window@7.4.0: + dependencies: + '@types/trusted-types': 2.0.7 + workbox-core: 7.4.0 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.1.2 + + write-file-atomic@5.0.1: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 4.1.0 + + wsl-utils@0.1.0: + dependencies: + is-wsl: 3.1.0 + + xdg-basedir@5.1.0: {} + + xe-utils@3.7.9: {} + + xml-name-validator@4.0.0: {} + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yallist@5.0.0: {} + + yaml-eslint-parser@1.3.1: + dependencies: + eslint-visitor-keys: 3.4.3 + yaml: 2.8.1 + + yaml@1.10.2: {} + + yaml@2.8.1: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + yocto-queue@1.2.2: {} + + yoctocolors@2.1.2: {} + + youch-core@0.3.3: + dependencies: + '@poppinss/exception': 1.2.2 + error-stack-parser-es: 1.0.5 + + youch@4.1.0-beta.13: + dependencies: + '@poppinss/colors': 4.1.5 + '@poppinss/dumper': 0.6.5 + '@speed-highlight/core': 1.2.12 + cookie-es: 2.0.0 + youch-core: 0.3.3 + + zeebe-bpmn-moddle@1.11.0: {} + + zimmerframe@1.1.4: {} + + zip-stream@6.0.1: + dependencies: + archiver-utils: 5.0.2 + compress-commons: 6.0.2 + readable-stream: 4.7.0 + + zod-defaults@0.1.3(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod@3.25.76: {} + + zrender@6.0.0: + dependencies: + tslib: 2.3.0 + + zwitch@2.0.4: {} + + zx@8.8.5: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 000000000..e6a78a5da --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,232 @@ +packages: + - internal/* + - internal/lint-configs/* + - packages/* + - packages/@core/base/* + - packages/@core/ui-kit/* + - packages/@core/forward/* + - packages/@core/* + - packages/effects/* + - packages/business/* + - apps/* + - scripts/* + - docs + +catalog: + '@ast-grep/napi': ^0.39.9 + '@changesets/changelog-github': ^0.5.1 + '@changesets/cli': ^2.29.7 + '@changesets/git': ^3.0.4 + '@clack/prompts': ^0.11.0 + '@commitlint/cli': ^19.8.1 + '@commitlint/config-conventional': ^19.8.1 + '@ctrl/tinycolor': ^4.1.0 + '@eslint/js': ^9.39.1 + '@faker-js/faker': ^9.9.0 + '@form-create/ant-design-vue': ^3.2.27 + '@form-create/antd-designer': ^3.3.0 + '@form-create/designer': ^3.3.0 + '@form-create/element-ui': ^3.2.27 + '@form-create/naive-ui': ^3.2.27 + '@iconify/json': ^2.2.406 + '@iconify/tailwind': ^1.2.0 + '@iconify/vue': ^5.0.0 + '@intlify/core-base': ^11.1.7 + '@intlify/unplugin-vue-i18n': ^6.0.8 + '@jspm/generator': ^2.6.2 + '@manypkg/get-packages': ^3.0.0 + '@microsoft/fetch-event-source': ^2.0.1 + '@nolebase/vitepress-plugin-git-changelog': ^2.18.0 + '@playwright/test': ^1.56.1 + '@pnpm/workspace.read-manifest': ^1000.2.6 + '@stylistic/stylelint-plugin': ^3.1.3 + '@tailwindcss/nesting': 0.0.0-insiders.565cd3e + '@tailwindcss/typography': ^0.5.16 + '@tinyflow-ai/vue': ^1.1.1 + '@tinymce/tinymce-vue': ^6.1.0 + '@tanstack/vue-query': ^5.91.0 + '@tanstack/vue-store': ^0.8.0 + '@types/archiver': ^6.0.3 + '@types/codemirror': ^5.60.13 + '@types/crypto-js': ^4.2.2 + '@types/eslint': ^9.6.1 + '@types/html-minifier-terser': ^7.0.2 + '@types/json-bigint': ^1.0.4 + '@types/jsonwebtoken': ^9.0.10 + '@types/lodash.clonedeep': ^4.5.9 + '@types/lodash.get': ^4.4.9 + '@types/lodash.isequal': ^4.5.8 + '@types/lodash.set': ^4.3.9 + '@types/markdown-it': ^14.1.2 + '@types/node': ^24.10.1 + '@types/nprogress': ^0.2.3 + '@types/postcss-import': ^14.0.3 + '@types/qrcode': ^1.5.5 + '@types/qs': ^6.14.0 + '@types/sortablejs': ^1.15.8 + '@typescript-eslint/eslint-plugin': ^8.46.4 + '@typescript-eslint/parser': ^8.46.4 + '@vee-validate/zod': ^4.15.1 + '@vite-pwa/vitepress': ^1.0.0 + '@vitejs/plugin-vue': ^6.0.1 + '@vitejs/plugin-vue-jsx': ^5.1.1 + '@vue/reactivity': ^3.5.17 + '@vue/shared': ^3.5.24 + '@vue/test-utils': ^2.4.6 + '@vueuse/core': ^13.4.0 + '@vueuse/integrations': ^14.0.0 + '@vueuse/motion': ^3.0.3 + '@videojs-player/vue': ^1.0.0 + ant-design-vue: ^4.2.6 + archiver: ^7.0.1 + autoprefixer: ^10.4.22 + axios: ^1.10.0 + axios-mock-adapter: ^2.1.0 + benz-amr-recorder: ^1.1.5 + bpmn-js: ^17.11.1 + bpmn-js-properties-panel: 5.23.0 + bpmn-js-token-simulation: ^0.36.3 + cac: ^6.7.14 + camunda-bpmn-moddle: ^7.0.1 + chalk: ^5.4.1 + cheerio: ^1.1.0 + circular-dependency-scanner: ^2.3.0 + class-variance-authority: ^0.7.1 + clsx: ^2.1.1 + codemirror: ^5.65.15 + commitlint-plugin-function-rules: ^4.1.1 + consola: ^3.4.2 + cropperjs: ^1.6.2 + cross-env: ^7.0.3 + crypto-js: ^4.2.0 + cspell: ^8.19.4 + cssnano: ^7.0.7 + cz-git: ^1.11.2 + czg: ^1.11.1 + dayjs: ^1.11.13 + defu: ^6.1.4 + depcheck: ^1.4.7 + diagram-js: ^12.8.1 + dotenv: ^16.6.1 + echarts: ^6.0.0 + element-plus: ^2.10.2 + eslint: ^9.39.1 + eslint-config-turbo: ^2.6.1 + eslint-plugin-command: ^3.3.1 + eslint-plugin-eslint-comments: ^3.2.0 + eslint-plugin-import-x: ^4.16.1 + eslint-plugin-jsdoc: ^61.2.1 + eslint-plugin-jsonc: ^2.21.0 + eslint-plugin-n: ^17.23.1 + eslint-plugin-no-only-tests: ^3.3.0 + eslint-plugin-perfectionist: ^4.15.1 + eslint-plugin-prettier: ^5.5.4 + eslint-plugin-regexp: ^2.10.0 + eslint-plugin-unicorn: ^62.0.0 + eslint-plugin-unused-imports: ^4.3.0 + eslint-plugin-vitest: ^0.5.4 + eslint-plugin-vue: ^10.5.1 + execa: ^9.6.0 + fast-xml-parser: ^4.5.3 + find-up: ^7.0.0 + get-port: ^7.1.0 + globals: ^16.3.0 + h3: ^1.15.3 + happy-dom: ^17.6.3 + highlight.js: ^11.11.1 + html-minifier-terser: ^7.2.0 + is-ci: ^4.1.0 + jiti: ^2.6.1 + jsencrypt: ^3.3.2 + json-bigint: ^1.0.0 + jsonc-eslint-parser: ^2.4.1 + jsonwebtoken: ^9.0.2 + lefthook: ^1.13.6 + lodash.clonedeep: ^4.5.0 + lodash.get: ^4.4.2 + lodash.isequal: ^4.5.0 + lodash.set: ^4.3.2 + lucide-vue-next: ^0.553.0 + markdown-it: ^14.1.0 + markmap-common: ^0.16.0 + markmap-lib: ^0.16.1 + markmap-toolbar: ^0.17.0 + markmap-view: ^0.16.0 + medium-zoom: ^1.1.0 + naive-ui: ^2.42.0 + nitropack: ^2.11.13 + nprogress: ^0.2.0 + ora: ^8.2.0 + pinia: ^3.0.3 + pinia-plugin-persistedstate: ^4.4.1 + pkg-types: ^2.2.0 + playwright: ^1.56.1 + postcss: ^8.5.6 + postcss-antd-fixes: ^0.2.0 + postcss-html: ^1.8.0 + postcss-import: ^16.1.1 + postcss-preset-env: ^10.2.4 + postcss-scss: ^4.0.9 + prettier: ^3.6.2 + prettier-plugin-tailwindcss: ^0.7.1 + publint: ^0.3.12 + qrcode: ^1.5.4 + qs: ^6.14.0 + reka-ui: ^2.6.0 + resolve.exports: ^2.0.3 + rimraf: ^6.1.0 + rollup: ^4.44.1 + rollup-plugin-visualizer: ^5.14.0 + sass: ^1.94.0 + secure-ls: ^2.0.0 + sortablejs: ^1.15.6 + steady-xml: ^0.1.0 + stylelint: ^16.21.0 + stylelint-config-recess-order: ^6.1.0 + stylelint-config-recommended: ^17.0.0 + stylelint-config-recommended-scss: ^14.1.0 + stylelint-config-recommended-vue: ^1.6.1 + stylelint-config-standard: ^38.0.0 + stylelint-order: ^7.0.0 + stylelint-prettier: ^5.0.3 + stylelint-scss: ^6.12.1 + tailwind-merge: ^2.6.0 + tailwindcss: ^3.4.18 + tailwindcss-animate: ^1.0.7 + tdesign-vue-next: ^1.17.1 + theme-colors: ^0.1.0 + tinymce: ^7.3.0 + tippy.js: ^6.3.7 + turbo: ^2.6.1 + typescript: ^5.9.3 + unbuild: ^3.6.1 + unplugin-element-plus: ^0.11.1 + vee-validate: ^4.15.1 + vite: ^7.2.2 + vite-plugin-compression: ^0.5.1 + vite-plugin-dts: ^4.5.4 + vite-plugin-html: ^3.2.2 + vite-plugin-lazy-import: ^1.0.7 + vite-plugin-pwa: ^1.0.1 + vite-plugin-vue-devtools: ^8.0.3 + vitepress: ^1.6.3 + vitepress-plugin-group-icons: ^1.6.1 + vitest: ^3.2.4 + vue: ^3.5.24 + vue-dompurify-html: ^5.3.0 + vue-eslint-parser: ^10.2.0 + vue-i18n: ^11.1.7 + vue-json-viewer: ^3.0.4 + vue-router: ^4.5.1 + vue-tippy: ^6.7.1 + vue-tsc: ^3.1.4 + vuedraggable: ^4.1.0 + vue3-print-nb: "^0.1.4" + vue3-signature: ^0.2.4 + video.js: ^7.21.6 + vxe-pc-ui: ^4.10.22 + vxe-table: ^4.17.14 + watermark-js-plus: ^1.6.2 + zod: ^3.25.67 + zod-defaults: 0.1.3 + es-toolkit: ^1.41.0 diff --git a/scripts/clean.mjs b/scripts/clean.mjs new file mode 100644 index 000000000..2049e45d5 --- /dev/null +++ b/scripts/clean.mjs @@ -0,0 +1,141 @@ +import { promises as fs } from 'node:fs'; +import { join, normalize } from 'node:path'; + +const rootDir = process.cwd(); + +// 控制并发数量,避免创建过多的并发任务 +const CONCURRENCY_LIMIT = 10; + +// 需要跳过的目录,避免进入这些目录进行清理 +const SKIP_DIRS = new Set(['.DS_Store', '.git', '.idea', '.vscode']); + +/** + * 处理单个文件/目录项 + * @param {string} currentDir - 当前目录路径 + * @param {string} item - 文件/目录名 + * @param {string[]} targets - 要删除的目标列表 + * @param {number} _depth - 当前递归深度 + * @returns {Promise} - 是否需要进一步递归处理 + */ +async function processItem(currentDir, item, targets, _depth) { + // 跳过特殊目录 + if (SKIP_DIRS.has(item)) { + return false; + } + + try { + const itemPath = normalize(join(currentDir, item)); + + if (targets.includes(item)) { + // 匹配到目标目录或文件时直接删除 + await fs.rm(itemPath, { force: true, recursive: true }); + console.log(`✅ Deleted: ${itemPath}`); + return false; // 已删除,无需递归 + } + + // 使用 readdir 的 withFileTypes 选项,避免额外的 lstat 调用 + return true; // 可能需要递归,由调用方决定 + } catch (error) { + // 更详细的错误信息 + if (error.code === 'ENOENT') { + // 文件不存在,可能已被删除,这是正常情况 + return false; + } else if (error.code === 'EPERM' || error.code === 'EACCES') { + console.error(`❌ Permission denied: ${item} in ${currentDir}`); + } else { + console.error( + `❌ Error handling item ${item} in ${currentDir}: ${error.message}`, + ); + } + return false; + } +} + +/** + * 递归查找并删除目标目录(并发优化版本) + * @param {string} currentDir - 当前遍历的目录路径 + * @param {string[]} targets - 要删除的目标列表 + * @param {number} depth - 当前递归深度,避免过深递归 + */ +async function cleanTargetsRecursively(currentDir, targets, depth = 0) { + // 限制递归深度,避免无限递归 + if (depth > 10) { + console.warn(`Max recursion depth reached at: ${currentDir}`); + return; + } + + let dirents; + try { + // 使用 withFileTypes 选项,一次性获取文件类型信息,避免后续 lstat 调用 + dirents = await fs.readdir(currentDir, { withFileTypes: true }); + } catch (error) { + // 如果无法读取目录,可能已被删除或权限不足 + console.warn(`Cannot read directory ${currentDir}: ${error.message}`); + return; + } + + // 分批处理,控制并发数量 + for (let i = 0; i < dirents.length; i += CONCURRENCY_LIMIT) { + const batch = dirents.slice(i, i + CONCURRENCY_LIMIT); + + const tasks = batch.map(async (dirent) => { + const item = dirent.name; + const shouldRecurse = await processItem(currentDir, item, targets, depth); + + // 如果是目录且没有被删除,则递归处理 + if (shouldRecurse && dirent.isDirectory()) { + const itemPath = normalize(join(currentDir, item)); + return cleanTargetsRecursively(itemPath, targets, depth + 1); + } + + return null; + }); + + // 并发执行当前批次的任务 + const results = await Promise.allSettled(tasks); + + // 检查是否有失败的任务(可选:用于调试) + const failedTasks = results.filter( + (result) => result.status === 'rejected', + ); + if (failedTasks.length > 0) { + console.warn( + `${failedTasks.length} tasks failed in batch starting at index ${i} in directory: ${currentDir}`, + ); + } + } +} + +(async function startCleanup() { + // 要删除的目录及文件名称 + const targets = ['node_modules', 'dist', '.turbo', 'dist.zip']; + const deleteLockFile = process.argv.includes('--del-lock'); + const cleanupTargets = [...targets]; + + if (deleteLockFile) { + cleanupTargets.push('pnpm-lock.yaml'); + } + + console.log( + `🚀 Starting cleanup of targets: ${cleanupTargets.join(', ')} from root: ${rootDir}`, + ); + + const startTime = Date.now(); + + try { + // 先统计要删除的目标数量 + console.log('📊 Scanning for cleanup targets...'); + + await cleanTargetsRecursively(rootDir, cleanupTargets); + + const endTime = Date.now(); + const duration = (endTime - startTime) / 1000; + + console.log( + `✨ Cleanup process completed successfully in ${duration.toFixed(2)}s`, + ); + } catch (error) { + console.error(`💥 Unexpected error during cleanup: ${error.message}`); + process.exit(1); + } +})(); diff --git a/scripts/turbo-run/package.json b/scripts/turbo-run/package.json new file mode 100644 index 000000000..4db231365 --- /dev/null +++ b/scripts/turbo-run/package.json @@ -0,0 +1,29 @@ +{ + "name": "@vben/turbo-run", + "version": "5.5.9", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "stub": "pnpm unbuild --stub" + }, + "files": [ + "dist" + ], + "bin": { + "turbo-run": "./bin/turbo-run.mjs" + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "default": "./dist/index.mjs" + }, + "./package.json": "./package.json" + }, + "dependencies": { + "@clack/prompts": "catalog:", + "@vben/node-utils": "workspace:*", + "cac": "catalog:" + } +} diff --git a/scripts/vsh/package.json b/scripts/vsh/package.json new file mode 100644 index 000000000..89f47ea55 --- /dev/null +++ b/scripts/vsh/package.json @@ -0,0 +1,31 @@ +{ + "name": "@vben/vsh", + "version": "5.5.9", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "stub": "pnpm unbuild --stub" + }, + "files": [ + "dist" + ], + "bin": { + "vsh": "./bin/vsh.mjs" + }, + "main": "./dist/index.mjs", + "module": "./dist/index.mjs", + "exports": { + ".": { + "default": "./dist/index.mjs" + }, + "./package.json": "./package.json" + }, + "dependencies": { + "@vben/node-utils": "workspace:*", + "cac": "catalog:", + "circular-dependency-scanner": "catalog:", + "depcheck": "catalog:", + "publint": "catalog:" + } +} diff --git a/scripts/vsh/src/check-dep/index.ts b/scripts/vsh/src/check-dep/index.ts new file mode 100644 index 000000000..3da10b997 --- /dev/null +++ b/scripts/vsh/src/check-dep/index.ts @@ -0,0 +1,193 @@ +import type { CAC } from 'cac'; + +import { getPackages } from '@vben/node-utils'; + +import depcheck from 'depcheck'; + +// 默认配置 +const DEFAULT_CONFIG = { + // 需要忽略的依赖匹配 + ignoreMatches: [ + 'vite', + 'vitest', + 'unbuild', + '@vben/tsconfig', + '@vben/vite-config', + '@vben/tailwind-config', + '@types/*', + '@vben-core/design', + ], + // 需要忽略的包 + ignorePackages: [ + '@vben/commitlint-config', + '@vben/eslint-config', + '@vben/node-utils', + '@vben/prettier-config', + '@vben/stylelint-config', + '@vben/tailwind-config', + '@vben/tsconfig', + '@vben/vite-config', + '@vben/vsh', + ], + // 需要忽略的文件模式 + ignorePatterns: ['dist', 'node_modules', 'public'], +}; + +interface DepcheckResult { + dependencies: string[]; + devDependencies: string[]; + missing: Record; +} + +interface DepcheckConfig { + ignoreMatches?: string[]; + ignorePackages?: string[]; + ignorePatterns?: string[]; +} + +interface PackageInfo { + dir: string; + packageJson: { + name: string; + }; +} + +/** + * 清理依赖检查结果 + * @param unused - 依赖检查结果 + */ +function cleanDepcheckResult(unused: DepcheckResult): void { + // 删除file:前缀的依赖提示,该依赖是本地依赖 + Reflect.deleteProperty(unused.missing, 'file:'); + + // 清理路径依赖 + Object.keys(unused.missing).forEach((key) => { + unused.missing[key] = (unused.missing[key] || []).filter( + (item: string) => !item.startsWith('/'), + ); + if (unused.missing[key].length === 0) { + Reflect.deleteProperty(unused.missing, key); + } + }); +} + +/** + * 格式化依赖检查结果 + * @param pkgName - 包名 + * @param unused - 依赖检查结果 + */ +function formatDepcheckResult(pkgName: string, unused: DepcheckResult): void { + const hasIssues = + Object.keys(unused.missing).length > 0 || + unused.dependencies.length > 0 || + unused.devDependencies.length > 0; + + if (!hasIssues) { + return; + } + + console.log('\n📦 Package:', pkgName); + + if (Object.keys(unused.missing).length > 0) { + console.log('❌ Missing dependencies:'); + Object.entries(unused.missing).forEach(([dep, files]) => { + console.log(` - ${dep}:`); + files.forEach((file) => console.log(` → ${file}`)); + }); + } + + if (unused.dependencies.length > 0) { + console.log('⚠️ Unused dependencies:'); + unused.dependencies.forEach((dep) => console.log(` - ${dep}`)); + } + + if (unused.devDependencies.length > 0) { + console.log('⚠️ Unused devDependencies:'); + unused.devDependencies.forEach((dep) => console.log(` - ${dep}`)); + } +} + +/** + * 运行依赖检查 + * @param config - 配置选项 + */ +async function runDepcheck(config: DepcheckConfig = {}): Promise { + try { + const finalConfig = { + ...DEFAULT_CONFIG, + ...config, + }; + + const { packages } = await getPackages(); + + let hasIssues = false; + + await Promise.all( + packages.map(async (pkg: PackageInfo) => { + // 跳过需要忽略的包 + if (finalConfig.ignorePackages.includes(pkg.packageJson.name)) { + return; + } + + const unused = await depcheck(pkg.dir, { + ignoreMatches: finalConfig.ignoreMatches, + ignorePatterns: finalConfig.ignorePatterns, + }); + + cleanDepcheckResult(unused); + + const pkgHasIssues = + Object.keys(unused.missing).length > 0 || + unused.dependencies.length > 0 || + unused.devDependencies.length > 0; + + if (pkgHasIssues) { + hasIssues = true; + formatDepcheckResult(pkg.packageJson.name, unused); + } + }), + ); + + if (!hasIssues) { + console.log('\n✅ Dependency check completed, no issues found'); + } + } catch (error) { + console.error( + '❌ Dependency check failed:', + error instanceof Error ? error.message : error, + ); + } +} + +/** + * 定义依赖检查命令 + * @param cac - CAC实例 + */ +function defineDepcheckCommand(cac: CAC): void { + cac + .command('check-dep') + .option( + '--ignore-packages ', + 'Packages to ignore, comma separated', + ) + .option( + '--ignore-matches ', + 'Dependency patterns to ignore, comma separated', + ) + .option( + '--ignore-patterns ', + 'File patterns to ignore, comma separated', + ) + .usage('Analyze project dependencies') + .action(async ({ ignoreMatches, ignorePackages, ignorePatterns }) => { + const config: DepcheckConfig = { + ...(ignorePackages && { ignorePackages: ignorePackages.split(',') }), + ...(ignoreMatches && { ignoreMatches: ignoreMatches.split(',') }), + ...(ignorePatterns && { ignorePatterns: ignorePatterns.split(',') }), + }; + + await runDepcheck(config); + }); +} + +export { defineDepcheckCommand, type DepcheckConfig }; diff --git a/turbo.json b/turbo.json new file mode 100644 index 000000000..7226f13dc --- /dev/null +++ b/turbo.json @@ -0,0 +1,45 @@ +{ + "$schema": "https://turbo.build/schema.json", + "globalDependencies": [ + "pnpm-lock.yaml", + "**/.env.*local", + "**/tsconfig*.json", + "internal/node-utils/*.json", + "internal/node-utils/src/**/*.ts", + "internal/tailwind-config/src/**/*.ts", + "internal/vite-config/*.json", + "internal/vite-config/src/**/*.ts", + "scripts/*/src/**/*.ts", + "scripts/*/src/**/*.json" + ], + "globalEnv": ["NODE_ENV"], + "tasks": { + "build": { + "dependsOn": ["^build"], + "outputs": [ + "dist/**", + "dist.zip", + ".vitepress/dist.zip", + ".vitepress/dist/**" + ] + }, + "preview": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "build:analyze": { + "dependsOn": ["^build"], + "outputs": ["dist/**"] + }, + "test:e2e": {}, + "dev": { + "dependsOn": [], + "outputs": [], + "cache": false, + "persistent": true + }, + "typecheck": { + "outputs": [] + } + } +} diff --git a/vben-admin.code-workspace b/vben-admin.code-workspace new file mode 100644 index 000000000..9b7acc34c --- /dev/null +++ b/vben-admin.code-workspace @@ -0,0 +1,168 @@ +{ + "folders": [ + { + "name": "@vben/web-antd", + "path": "apps/web-antd", + }, + { + "name": "@vben/web-ele", + "path": "apps/web-ele", + }, + { + "name": "@vben/web-naive", + "path": "apps/web-naive", + }, + { + "name": "@vben/web-tdesign", + "path": "apps/web-tdesign", + }, + { + "name": "@vben/docs", + "path": "docs", + }, + { + "name": "@vben/commitlint-config", + "path": "internal/lint-configs/commitlint-config", + }, + { + "name": "@vben/eslint-config", + "path": "internal/lint-configs/eslint-config", + }, + { + "name": "@vben/prettier-config", + "path": "internal/lint-configs/prettier-config", + }, + { + "name": "@vben/stylelint-config", + "path": "internal/lint-configs/stylelint-config", + }, + { + "name": "@vben/node-utils", + "path": "internal/node-utils", + }, + { + "name": "@vben/tailwind-config", + "path": "internal/tailwind-config", + }, + { + "name": "@vben/tsconfig", + "path": "internal/tsconfig", + }, + { + "name": "@vben/vite-config", + "path": "internal/vite-config", + }, + { + "name": "@vben-core/design", + "path": "packages/@core/base/design", + }, + { + "name": "@vben-core/icons", + "path": "packages/@core/base/icons", + }, + { + "name": "@vben-core/shared", + "path": "packages/@core/base/shared", + }, + { + "name": "@vben-core/typings", + "path": "packages/@core/base/typings", + }, + { + "name": "@vben-core/composables", + "path": "packages/@core/composables", + }, + { + "name": "@vben-core/preferences", + "path": "packages/@core/preferences", + }, + { + "name": "@vben-core/form-ui", + "path": "packages/@core/ui-kit/form-ui", + }, + { + "name": "@vben-core/layout-ui", + "path": "packages/@core/ui-kit/layout-ui", + }, + { + "name": "@vben-core/menu-ui", + "path": "packages/@core/ui-kit/menu-ui", + }, + { + "name": "@vben-core/popup-ui", + "path": "packages/@core/ui-kit/popup-ui", + }, + { + "name": "@vben-core/shadcn-ui", + "path": "packages/@core/ui-kit/shadcn-ui", + }, + { + "name": "@vben-core/tabs-ui", + "path": "packages/@core/ui-kit/tabs-ui", + }, + { + "name": "@vben/constants", + "path": "packages/constants", + }, + { + "name": "@vben/access", + "path": "packages/effects/access", + }, + { + "name": "@vben/common-ui", + "path": "packages/effects/common-ui", + }, + { + "name": "@vben/hooks", + "path": "packages/effects/hooks", + }, + { + "name": "@vben/layouts", + "path": "packages/effects/layouts", + }, + { + "name": "@vben/plugins", + "path": "packages/effects/plugins", + }, + { + "name": "@vben/request", + "path": "packages/effects/request", + }, + { + "name": "@vben/icons", + "path": "packages/icons", + }, + { + "name": "@vben/locales", + "path": "packages/locales", + }, + { + "name": "@vben/preferences", + "path": "packages/preferences", + }, + { + "name": "@vben/stores", + "path": "packages/stores", + }, + { + "name": "@vben/styles", + "path": "packages/styles", + }, + { + "name": "@vben/types", + "path": "packages/types", + }, + { + "name": "@vben/utils", + "path": "packages/utils", + }, + { + "name": "@vben/turbo-run", + "path": "scripts/turbo-run", + }, + { + "name": "@vben/vsh", + "path": "scripts/vsh", + }, + ], +}