From d42166d2c0e7fe3b0a4586710edb62933246008d Mon Sep 17 00:00:00 2001 From: dengqichen Date: Mon, 13 Oct 2025 16:25:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + backend/docs/01-架构总览.md | 766 + backend/docs/02-后端技术设计.md | 1787 + backend/docs/03-前端技术设计.md | 1404 + backend/docs/04-数据模型设计.md | 192 + backend/docs/05-API契约.md | 163 + backend/docs/05-开发规范.md | 836 + backend/docs/99-最终修正落地方案.md | 97 + backend/docs/README.md | 386 + backend/logs/flowable-devops.log | 33428 ---------------- .../devops/service/NodeTypeService.java | 175 +- .../devops/workflow/node/WorkflowNode.java | 40 +- .../node/registry/NodeTypeRegistry.java | 66 +- backend/src/main/resources/application.yml | 157 +- .../src/test/resources/application-test.yml | 40 - frontend/.browserslistrc | 4 + frontend/.commitlintrc.js | 1 + frontend/.cursorignore | 2 + frontend/.dockerignore | 7 + frontend/.editorconfig | 18 + frontend/.env.example | 2 - frontend/.eslintrc.json | 14 - frontend/.gitattributes | 11 + frontend/.gitconfig | 2 + frontend/.gitignore | 52 + frontend/.gitpod.yml | 6 + frontend/.idea/.gitignore | 5 - frontend/.idea/frontend.iml | 12 - .../inspectionProfiles/Project_Default.xml | 6 - frontend/.idea/modules.xml | 8 - frontend/.idea/vcs.xml | 6 - frontend/.node-version | 1 + frontend/.npmrc | 13 + frontend/.prettierignore | 18 + frontend/.prettierrc | 5 - frontend/.prettierrc.mjs | 1 + frontend/.stylelintignore | 4 + frontend/LICENSE | 9 + frontend/README.ja-JP.md | 153 + frontend/README.md | 174 +- frontend/README.zh-CN.md | 153 + frontend/apps/web-antd/.env | 8 + frontend/apps/web-antd/.env.analyze | 7 + frontend/apps/web-antd/.env.development | 16 + frontend/apps/web-antd/.env.production | 19 + frontend/apps/web-antd/CLAUDE.md | 813 + frontend/apps/web-antd/index.html | 35 + .../apps/web-antd/node-size-comparison.html | 213 + frontend/apps/web-antd/package.json | 59 + frontend/apps/web-antd/postcss.config.mjs | 1 + frontend/apps/web-antd/public/favicon.ico | Bin 0 -> 5430 bytes .../web-antd/src/adapter/component/index.ts | 211 + frontend/apps/web-antd/src/adapter/form.ts | 49 + .../apps/web-antd/src/adapter/vxe-table.ts | 69 + frontend/apps/web-antd/src/api/core/auth.ts | 72 + frontend/apps/web-antd/src/api/core/index.ts | 3 + frontend/apps/web-antd/src/api/core/menu.ts | 46 + frontend/apps/web-antd/src/api/core/user.ts | 25 + frontend/apps/web-antd/src/api/index.ts | 1 + frontend/apps/web-antd/src/api/request.ts | 113 + frontend/apps/web-antd/src/app.vue | 39 + frontend/apps/web-antd/src/bootstrap.ts | 76 + frontend/apps/web-antd/src/layouts/auth.vue | 23 + frontend/apps/web-antd/src/layouts/basic.vue | 157 + frontend/apps/web-antd/src/layouts/index.ts | 6 + frontend/apps/web-antd/src/locales/README.md | 3 + frontend/apps/web-antd/src/locales/index.ts | 102 + .../src/locales/langs/en-US/demos.json | 12 + .../src/locales/langs/en-US/page.json | 19 + .../src/locales/langs/zh-CN/demos.json | 12 + .../src/locales/langs/zh-CN/page.json | 19 + frontend/apps/web-antd/src/main.ts | 31 + frontend/apps/web-antd/src/preferences.ts | 13 + frontend/apps/web-antd/src/router/access.ts | 42 + frontend/apps/web-antd/src/router/guard.ts | 135 + frontend/apps/web-antd/src/router/index.ts | 37 + .../apps/web-antd/src/router/routes/core.ts | 97 + .../apps/web-antd/src/router/routes/index.ts | 37 + .../src/router/routes/modules/dashboard.ts | 38 + .../src/router/routes/modules/demos.ts | 28 + .../src/router/routes/modules/vben.ts | 81 + .../src/router/routes/modules/workflow.ts | 47 + frontend/apps/web-antd/src/store/auth.ts | 118 + frontend/apps/web-antd/src/store/index.ts | 1 + frontend/apps/web-antd/src/store/workflow.ts | 490 + frontend/apps/web-antd/src/types/bpmn.d.ts | 63 + frontend/apps/web-antd/src/types/workflow.ts | 365 + .../apps/web-antd/src/views/_core/README.md | 3 + .../web-antd/src/views/_core/about/index.vue | 9 + .../views/_core/authentication/code-login.vue | 69 + .../_core/authentication/forget-password.vue | 43 + .../src/views/_core/authentication/login.vue | 98 + .../_core/authentication/qrcode-login.vue | 10 + .../views/_core/authentication/register.vue | 96 + .../src/views/_core/fallback/coming-soon.vue | 7 + .../src/views/_core/fallback/forbidden.vue | 9 + .../views/_core/fallback/internal-error.vue | 9 + .../src/views/_core/fallback/not-found.vue | 9 + .../src/views/_core/fallback/offline.vue | 9 + .../dashboard/analytics/analytics-trends.vue | 98 + .../analytics/analytics-visits-data.vue | 82 + .../analytics/analytics-visits-sales.vue | 46 + .../analytics/analytics-visits-source.vue | 65 + .../dashboard/analytics/analytics-visits.vue | 55 + .../src/views/dashboard/analytics/index.vue | 90 + .../src/views/dashboard/workspace/index.vue | 266 + .../web-antd/src/views/demos/antd/index.vue | 66 + .../web-antd/src/views/workflow/README.md | 202 + .../workflow/process-design/QUICK_TEST.md | 211 + .../views/workflow/process-design/README.md | 165 + .../views/workflow/process-design/SOLUTION.md | 187 + .../process-design/TRANSLATION_DEBUG.md | 208 + .../views/workflow/process-design/UPGRADE.md | 239 + .../views/workflow/process-design/index.vue | 322 + .../process-design/translate/zh-CN.ts | 515 + .../workflow/vue-flow-design/ARCHITECTURE.md | 231 + .../vue-flow-design/DEBUG_FIELD_MAPPING.md | 150 + .../vue-flow-design/FIELD_MAPPING_GUIDE.md | 189 + .../vue-flow-design/IMPLEMENTATION_SUMMARY.md | 376 + .../QUICK_TEST_FIELD_MAPPING.md | 150 + .../views/workflow/vue-flow-design/README.md | 359 + .../workflow/vue-flow-design/TEST_WORKFLOW.md | 82 + .../workflow/vue-flow-design/USAGE_GUIDE.md | 341 + .../vue-flow-design/components/BaseNode.vue | 231 + .../components/FieldMappingSelector.vue | 409 + .../components/NodeConfigPanel.vue | 525 + .../components/NodePalette.vue | 262 + .../components/ParameterEditor.vue | 202 + .../views/workflow/vue-flow-design/index.vue | 655 + .../vue-flow-design/mock/nodeTypes.ts | 442 + .../vue-flow-design/mock/workflows.ts | 87 + .../vue-flow-design/nodes/DecisionNode.vue | 265 + .../nodes/EditableTaskNode.vue | 436 + .../vue-flow-design/nodes/EndNode.vue | 223 + .../vue-flow-design/nodes/StartNode.vue | 221 + .../vue-flow-design/nodes/TaskNode.vue | 224 + .../stores/useWorkflowStore.ts | 464 + .../vue-flow-design/utils/expression.ts | 251 + .../vue-flow-design/utils/fieldTree.ts | 165 + .../views/workflow/vue-flow-test/index.vue | 73 + frontend/apps/web-antd/start_dev.bat | 4 + frontend/apps/web-antd/tailwind.config.mjs | 1 + frontend/apps/web-antd/tsconfig.json | 12 + frontend/apps/web-antd/tsconfig.node.json | 10 + frontend/apps/web-antd/vite.config.mts | 20 + frontend/cspell.json | 68 + frontend/dev.log | 11 - frontend/dist/assets/index-CTkmAz9X.css | 1 - frontend/dist/assets/index-D9R4V7sH.js | 506 - frontend/dist/index.html | 13 - .../.vitepress/components/demo-preview.vue | 45 + frontend/docs/.vitepress/components/index.ts | 1 + .../.vitepress/components/preview-group.vue | 110 + frontend/docs/.vitepress/config/en.mts | 231 + frontend/docs/.vitepress/config/index.mts | 25 + .../.vitepress/config/plugins/demo-preview.ts | 143 + frontend/docs/.vitepress/config/shared.mts | 172 + frontend/docs/.vitepress/config/zh.mts | 358 + .../theme/components/site-layout.vue | 96 + .../theme/components/vben-contributors.vue | 29 + frontend/docs/.vitepress/theme/index.ts | 29 + frontend/docs/.vitepress/theme/plugins/hm.ts | 28 + .../docs/.vitepress/theme/styles/base.css | 22 + .../docs/.vitepress/theme/styles/index.ts | 4 + .../.vitepress/theme/styles/variables.css | 132 + frontend/docs/01-架构总览.md | 766 + frontend/docs/02-后端技术设计.md | 1787 + frontend/docs/03-前端技术设计.md | 1404 + frontend/docs/04-数据模型设计.md | 192 + frontend/docs/05-API契约.md | 163 + frontend/docs/05-开发规范.md | 836 + frontend/docs/99-最终修正落地方案.md | 97 + frontend/docs/README.md | 386 + frontend/docs/package.json | 35 + frontend/docs/src/_env/adapter/component.ts | 128 + frontend/docs/src/_env/adapter/form.ts | 47 + frontend/docs/src/_env/adapter/vxe-table.ts | 70 + frontend/docs/src/_env/node/adapter/form.ts | 4 + .../docs/src/_env/node/adapter/vxe-table.ts | 3 + frontend/docs/src/commercial/community.md | 30 + frontend/docs/src/commercial/customized.md | 12 + .../docs/src/commercial/technical-support.md | 8 + .../src/components/common-ui/vben-alert.md | 166 + .../common-ui/vben-api-component.md | 173 + .../common-ui/vben-count-to-animator.md | 59 + .../src/components/common-ui/vben-drawer.md | 156 + .../common-ui/vben-ellipsis-text.md | 64 + .../src/components/common-ui/vben-form.md | 563 + .../src/components/common-ui/vben-modal.md | 174 + .../components/common-ui/vben-vxe-table.md | 276 + frontend/docs/src/components/introduction.md | 15 + .../docs/src/components/layout-ui/page.md | 44 + .../docs/src/demos/vben-alert/alert/index.vue | 36 + .../src/demos/vben-alert/confirm/index.vue | 75 + .../src/demos/vben-alert/prompt/index.vue | 118 + .../vben-api-component/cascader/index.vue | 100 + .../vben-count-to-animator/basic/index.vue | 6 + .../vben-count-to-animator/custom/index.vue | 12 + .../demos/vben-drawer/auto-height/drawer.vue | 45 + .../demos/vben-drawer/auto-height/index.vue | 21 + .../src/demos/vben-drawer/basic/index.vue | 11 + .../src/demos/vben-drawer/dynamic/drawer.vue | 26 + .../src/demos/vben-drawer/dynamic/index.vue | 29 + .../src/demos/vben-drawer/extra/drawer.vue | 8 + .../src/demos/vben-drawer/extra/index.vue | 21 + .../demos/vben-drawer/shared-data/drawer.vue | 26 + .../demos/vben-drawer/shared-data/index.vue | 27 + .../vben-ellipsis-text/auto-display/index.vue | 16 + .../demos/vben-ellipsis-text/expand/index.vue | 10 + .../demos/vben-ellipsis-text/line/index.vue | 10 + .../vben-ellipsis-text/tooltip/index.vue | 14 + .../docs/src/demos/vben-form/api/index.vue | 236 + .../docs/src/demos/vben-form/basic/index.vue | 231 + .../docs/src/demos/vben-form/custom/index.vue | 68 + .../src/demos/vben-form/dynamic/index.vue | 168 + .../docs/src/demos/vben-form/query/index.vue | 94 + .../docs/src/demos/vben-form/rules/index.vue | 190 + .../demos/vben-modal/animation-type/index.vue | 36 + .../demos/vben-modal/auto-height/index.vue | 21 + .../demos/vben-modal/auto-height/modal.vue | 45 + .../docs/src/demos/vben-modal/basic/index.vue | 11 + .../src/demos/vben-modal/draggable/index.vue | 21 + .../src/demos/vben-modal/draggable/modal.vue | 10 + .../src/demos/vben-modal/dynamic/index.vue | 29 + .../src/demos/vben-modal/dynamic/modal.vue | 38 + .../docs/src/demos/vben-modal/extra/index.vue | 21 + .../docs/src/demos/vben-modal/extra/modal.vue | 8 + .../demos/vben-modal/shared-data/index.vue | 27 + .../demos/vben-modal/shared-data/modal.vue | 26 + .../src/demos/vben-vxe-table/basic/index.vue | 85 + .../vben-vxe-table/custom-cell/index.vue | 105 + .../demos/vben-vxe-table/edit-cell/index.vue | 55 + .../demos/vben-vxe-table/edit-row/index.vue | 92 + .../src/demos/vben-vxe-table/fixed/index.vue | 67 + .../src/demos/vben-vxe-table/form/index.vue | 127 + .../docs/src/demos/vben-vxe-table/mock-api.ts | 36 + .../src/demos/vben-vxe-table/remote/index.vue | 112 + .../src/demos/vben-vxe-table/table-data.ts | 384 + .../src/demos/vben-vxe-table/tree/index.vue | 80 + .../demos/vben-vxe-table/virtual/index.vue | 64 + .../docs/src/en/guide/essentials/build.md | 243 + .../docs/src/en/guide/essentials/concept.md | 70 + .../src/en/guide/essentials/development.md | 255 + .../en/guide/essentials/external-module.md | 58 + .../docs/src/en/guide/essentials/icons.md | 78 + .../docs/src/en/guide/essentials/route.md | 603 + .../docs/src/en/guide/essentials/server.md | 356 + .../docs/src/en/guide/essentials/settings.md | 626 + .../docs/src/en/guide/essentials/styles.md | 106 + frontend/docs/src/en/guide/in-depth/access.md | 356 + .../src/en/guide/in-depth/check-updates.md | 48 + .../docs/src/en/guide/in-depth/features.md | 84 + frontend/docs/src/en/guide/in-depth/layout.md | 1 + .../docs/src/en/guide/in-depth/loading.md | 44 + frontend/docs/src/en/guide/in-depth/locale.md | 227 + frontend/docs/src/en/guide/in-depth/login.md | 119 + frontend/docs/src/en/guide/in-depth/theme.md | 1295 + .../src/en/guide/in-depth/ui-framework.md | 17 + .../src/en/guide/introduction/changelog.md | 3 + .../src/en/guide/introduction/quick-start.md | 95 + .../docs/src/en/guide/introduction/roadmap.md | 3 + .../docs/src/en/guide/introduction/thin.md | 67 + .../docs/src/en/guide/introduction/vben.md | 49 + .../docs/src/en/guide/introduction/why.md | 9 + frontend/docs/src/en/guide/other/faq.md | 159 + .../docs/src/en/guide/other/project-update.md | 54 + .../docs/src/en/guide/other/remove-code.md | 18 + .../docs/src/en/guide/project/changeset.md | 21 + frontend/docs/src/en/guide/project/cli.md | 106 + frontend/docs/src/en/guide/project/dir.md | 68 + .../docs/src/en/guide/project/standard.md | 210 + .../docs/src/en/guide/project/tailwindcss.md | 13 + frontend/docs/src/en/guide/project/test.md | 33 + frontend/docs/src/en/guide/project/vite.md | 33 + frontend/docs/src/en/index.md | 76 + frontend/docs/src/friend-links/index.md | 26 + frontend/docs/src/guide/essentials/build.md | 243 + frontend/docs/src/guide/essentials/concept.md | 70 + .../docs/src/guide/essentials/development.md | 255 + .../src/guide/essentials/external-module.md | 58 + frontend/docs/src/guide/essentials/icons.md | 78 + frontend/docs/src/guide/essentials/route.md | 644 + frontend/docs/src/guide/essentials/server.md | 387 + .../docs/src/guide/essentials/settings.md | 629 + frontend/docs/src/guide/essentials/styles.md | 106 + frontend/docs/src/guide/in-depth/access.md | 357 + .../docs/src/guide/in-depth/check-updates.md | 92 + frontend/docs/src/guide/in-depth/features.md | 84 + frontend/docs/src/guide/in-depth/layout.md | 1 + frontend/docs/src/guide/in-depth/loading.md | 46 + frontend/docs/src/guide/in-depth/locale.md | 227 + frontend/docs/src/guide/in-depth/login.md | 220 + frontend/docs/src/guide/in-depth/theme.md | 1295 + .../docs/src/guide/in-depth/ui-framework.md | 17 + .../docs/src/guide/introduction/changelog.md | 3 + .../src/guide/introduction/quick-start.md | 111 + .../docs/src/guide/introduction/roadmap.md | 3 + frontend/docs/src/guide/introduction/thin.md | 94 + frontend/docs/src/guide/introduction/vben.md | 49 + frontend/docs/src/guide/introduction/why.md | 23 + frontend/docs/src/guide/other/faq.md | 159 + .../docs/src/guide/other/project-update.md | 55 + frontend/docs/src/guide/other/remove-code.md | 18 + frontend/docs/src/guide/project/changeset.md | 21 + frontend/docs/src/guide/project/cli.md | 113 + frontend/docs/src/guide/project/dir.md | 68 + frontend/docs/src/guide/project/standard.md | 210 + .../docs/src/guide/project/tailwindcss.md | 17 + frontend/docs/src/guide/project/test.md | 33 + frontend/docs/src/guide/project/vite.md | 33 + frontend/docs/src/index.md | 111 + frontend/docs/src/public/favicon.ico | Bin 0 -> 5430 bytes frontend/docs/src/public/guide/devtools.png | Bin 0 -> 402172 bytes frontend/docs/src/public/guide/loading.png | Bin 0 -> 89250 bytes frontend/docs/src/public/guide/locale.png | Bin 0 -> 482275 bytes .../docs/src/public/guide/login-expired.png | Bin 0 -> 569303 bytes frontend/docs/src/public/guide/login.png | Bin 0 -> 480224 bytes .../docs/src/public/guide/preferences.png | Bin 0 -> 126147 bytes frontend/docs/src/public/guide/qq.png | Bin 0 -> 457417 bytes frontend/docs/src/public/guide/qq_channel.png | Bin 0 -> 458478 bytes frontend/docs/src/public/guide/report.png | Bin 0 -> 1024246 bytes frontend/docs/src/public/guide/test.png | Bin 0 -> 254971 bytes .../docs/src/public/guide/update-notice.png | Bin 0 -> 410070 bytes frontend/docs/src/public/logos/nitro.svg | 42 + frontend/docs/src/public/logos/shadcn-ui.svg | 1 + frontend/docs/src/public/logos/turborepo.svg | 32 + frontend/docs/src/public/logos/vite.svg | 15 + frontend/docs/src/sponsor/personal.md | 12 + frontend/docs/tailwind.config.mjs | 11 + frontend/docs/tsconfig.json | 19 + frontend/eslint.config.mjs | 5 + frontend/index.html | 12 - .../lint-configs/commitlint-config/index.mjs | 153 + .../commitlint-config/package.json | 33 + .../eslint-config/build.config.ts | 7 + .../lint-configs/eslint-config/package.json | 56 + .../eslint-config/src/configs/command.ts | 10 + .../eslint-config/src/configs/comments.ts | 24 + .../eslint-config/src/configs/disableds.ts | 28 + .../eslint-config/src/configs/ignores.ts | 52 + .../eslint-config/src/configs/import.ts | 25 + .../eslint-config/src/configs/index.ts | 17 + .../eslint-config/src/configs/javascript.ts | 241 + .../eslint-config/src/configs/jsdoc.ts | 34 + .../eslint-config/src/configs/jsonc.ts | 258 + .../eslint-config/src/configs/node.ts | 57 + .../src/configs/perfectionist.ts | 89 + .../eslint-config/src/configs/prettier.ts | 19 + .../eslint-config/src/configs/regexp.ts | 20 + .../eslint-config/src/configs/test.ts | 45 + .../eslint-config/src/configs/turbo.ts | 18 + .../eslint-config/src/configs/typescript.ts | 72 + .../eslint-config/src/configs/unicorn.ts | 45 + .../eslint-config/src/configs/vue.ts | 153 + .../eslint-config/src/custom-config.ts | 168 + .../lint-configs/eslint-config/src/index.ts | 60 + .../lint-configs/eslint-config/src/util.ts | 8 + .../lint-configs/eslint-config/tsconfig.json | 6 + .../lint-configs/prettier-config/index.mjs | 18 + .../lint-configs/prettier-config/package.json | 28 + .../lint-configs/stylelint-config/index.mjs | 141 + .../stylelint-config/package.json | 43 + frontend/internal/node-utils/build.config.ts | 7 + frontend/internal/node-utils/package.json | 43 + .../node-utils/src/__tests__/hash.test.ts | 52 + .../node-utils/src/__tests__/path.test.ts | 67 + frontend/internal/node-utils/src/constants.ts | 6 + frontend/internal/node-utils/src/date.ts | 12 + frontend/internal/node-utils/src/fs.ts | 39 + frontend/internal/node-utils/src/git.ts | 34 + frontend/internal/node-utils/src/hash.ts | 18 + frontend/internal/node-utils/src/index.ts | 19 + frontend/internal/node-utils/src/monorepo.ts | 46 + frontend/internal/node-utils/src/path.ts | 11 + frontend/internal/node-utils/src/prettier.ts | 21 + frontend/internal/node-utils/src/spinner.ts | 26 + frontend/internal/node-utils/tsconfig.json | 6 + .../internal/tailwind-config/build.config.ts | 10 + .../internal/tailwind-config/package.json | 66 + .../internal/tailwind-config/src/index.ts | 266 + .../internal/tailwind-config/src/module.d.ts | 3 + .../tailwind-config/src/plugins/entry.ts | 53 + .../tailwind-config/src/postcss.config.ts | 15 + .../internal/tailwind-config/tsconfig.json | 9 + frontend/internal/tsconfig/base.json | 40 + frontend/internal/tsconfig/library.json | 13 + frontend/internal/tsconfig/node.json | 12 + frontend/internal/tsconfig/package.json | 25 + frontend/internal/tsconfig/web-app.json | 8 + frontend/internal/tsconfig/web.json | 14 + frontend/internal/vite-config/build.config.ts | 7 + frontend/internal/vite-config/package.json | 59 + .../vite-config/src/config/application.ts | 125 + .../internal/vite-config/src/config/common.ts | 13 + .../internal/vite-config/src/config/index.ts | 37 + .../vite-config/src/config/library.ts | 59 + frontend/internal/vite-config/src/index.ts | 4 + frontend/internal/vite-config/src/options.ts | 45 + .../vite-config/src/plugins/archiver.ts | 75 + .../src/plugins/extra-app-config.ts | 92 + .../vite-config/src/plugins/importmap.ts | 245 + .../internal/vite-config/src/plugins/index.ts | 247 + .../src/plugins/inject-app-loading/README.md | 3 + .../default-loading-antd.html | 107 + .../inject-app-loading/default-loading.html | 113 + .../src/plugins/inject-app-loading/index.ts | 66 + .../src/plugins/inject-metadata.ts | 111 + .../vite-config/src/plugins/license.ts | 63 + .../vite-config/src/plugins/nitro-mock.ts | 98 + .../internal/vite-config/src/plugins/print.ts | 28 + .../vite-config/src/plugins/vxe-table.ts | 20 + frontend/internal/vite-config/src/typing.ts | 343 + .../internal/vite-config/src/utils/env.ts | 110 + frontend/internal/vite-config/tsconfig.json | 6 + frontend/lefthook.yml | 76 + frontend/package-lock.json | 6625 --- frontend/package.json | 144 +- frontend/packages/@core/README.md | 3 + frontend/packages/@core/base/README.md | 5 + .../packages/@core/base/design/package.json | 41 + .../@core/base/design/src/css/global.css | 160 + .../@core/base/design/src/css/nprogress.css | 59 + .../@core/base/design/src/css/transition.css | 236 + .../packages/@core/base/design/src/css/ui.css | 87 + .../base/design/src/design-tokens/dark.css | 446 + .../base/design/src/design-tokens/default.css | 382 + .../base/design/src/design-tokens/index.ts | 4 + .../packages/@core/base/design/src/index.ts | 8 + .../@core/base/design/src/scss-bem/bem.scss | 34 + .../base/design/src/scss-bem/constants.scss | 5 + .../packages/@core/base/design/tsconfig.json | 6 + .../@core/base/design/vite.config.mts | 9 + .../packages/@core/base/icons/build.config.ts | 7 + .../packages/@core/base/icons/package.json | 41 + .../@core/base/icons/src/create-icon.ts | 14 + .../packages/@core/base/icons/src/index.ts | 11 + .../packages/@core/base/icons/src/lucide.ts | 68 + .../packages/@core/base/icons/tsconfig.json | 6 + .../@core/base/shared/build.config.ts | 14 + .../packages/@core/base/shared/package.json | 103 + .../cache/__tests__/storage-manager.test.ts | 130 + .../@core/base/shared/src/cache/index.ts | 1 + .../base/shared/src/cache/storage-manager.ts | 118 + .../@core/base/shared/src/cache/types.ts | 17 + .../src/color/__tests__/convert.test.ts | 58 + .../@core/base/shared/src/color/color.ts | 9 + .../@core/base/shared/src/color/convert.ts | 62 + .../@core/base/shared/src/color/generator.ts | 45 + .../@core/base/shared/src/color/index.ts | 3 + .../base/shared/src/constants/globals.ts | 16 + .../@core/base/shared/src/constants/index.ts | 2 + .../@core/base/shared/src/constants/vben.ts | 26 + .../@core/base/shared/src/global-state.ts | 45 + .../packages/@core/base/shared/src/store.ts | 1 + .../shared/src/utils/__tests__/diff.test.ts | 53 + .../shared/src/utils/__tests__/dom.test.ts | 127 + .../src/utils/__tests__/inference.test.ts | 183 + .../shared/src/utils/__tests__/letter.test.ts | 116 + .../src/utils/__tests__/resources.test.ts | 82 + .../src/utils/__tests__/state-handler.test.ts | 60 + .../shared/src/utils/__tests__/tree.test.ts | 196 + .../shared/src/utils/__tests__/unique.test.ts | 60 + .../__tests__/update-css-variables.test.ts | 30 + .../shared/src/utils/__tests__/util.test.ts | 156 + .../shared/src/utils/__tests__/window.test.ts | 33 + .../@core/base/shared/src/utils/cn.ts | 10 + .../@core/base/shared/src/utils/date.ts | 26 + .../@core/base/shared/src/utils/diff.ts | 96 + .../@core/base/shared/src/utils/dom.ts | 95 + .../@core/base/shared/src/utils/download.ts | 157 + .../@core/base/shared/src/utils/index.ts | 21 + .../@core/base/shared/src/utils/inference.ts | 165 + .../@core/base/shared/src/utils/letter.ts | 47 + .../@core/base/shared/src/utils/merge.ts | 10 + .../@core/base/shared/src/utils/nprogress.ts | 43 + .../@core/base/shared/src/utils/resources.ts | 21 + .../base/shared/src/utils/state-handler.ts | 50 + .../@core/base/shared/src/utils/to.ts | 21 + .../@core/base/shared/src/utils/tree.ts | 97 + .../@core/base/shared/src/utils/unique.ts | 15 + .../shared/src/utils/update-css-variables.ts | 35 + .../@core/base/shared/src/utils/util.ts | 44 + .../@core/base/shared/src/utils/window.ts | 37 + .../packages/@core/base/shared/tsconfig.json | 6 + .../@core/base/typings/build.config.ts | 7 + .../packages/@core/base/typings/package.json | 44 + .../packages/@core/base/typings/src/app.d.ts | 111 + .../@core/base/typings/src/basic.d.ts | 35 + .../@core/base/typings/src/helper.d.ts | 132 + .../packages/@core/base/typings/src/index.ts | 6 + .../@core/base/typings/src/menu-record.ts | 76 + .../packages/@core/base/typings/src/tabs.ts | 8 + .../@core/base/typings/src/vue-router.d.ts | 153 + .../packages/@core/base/typings/tsconfig.json | 6 + .../@core/base/typings/vue-router.d.ts | 9 + .../@core/composables/build.config.ts | 7 + .../packages/@core/composables/package.json | 47 + .../src/__tests__/use-sortable.test.ts | 48 + .../packages/@core/composables/src/index.ts | 13 + .../@core/composables/src/use-is-mobile.ts | 7 + .../@core/composables/src/use-layout-style.ts | 87 + .../@core/composables/src/use-namespace.ts | 106 + .../composables/src/use-priority-value.ts | 94 + .../@core/composables/src/use-scroll-lock.ts | 54 + .../src/use-simple-locale/README.md | 3 + .../src/use-simple-locale/index.ts | 27 + .../src/use-simple-locale/messages.ts | 24 + .../@core/composables/src/use-sortable.ts | 29 + .../packages/@core/composables/tsconfig.json | 6 + .../__snapshots__/config.test.ts.snap | 136 + .../preferences/__tests__/config.test.ts | 10 + .../preferences/__tests__/preferences.test.ts | 253 + .../@core/preferences/build.config.ts | 7 + .../packages/@core/preferences/package.json | 37 + .../packages/@core/preferences/src/config.ts | 138 + .../@core/preferences/src/constants.ts | 88 + .../packages/@core/preferences/src/index.ts | 35 + .../@core/preferences/src/preferences.ts | 235 + .../packages/@core/preferences/src/types.ts | 324 + .../preferences/src/update-css-variables.ts | 116 + .../@core/preferences/src/use-preferences.ts | 254 + .../packages/@core/preferences/tsconfig.json | 6 + frontend/packages/@core/ui-kit/README.md | 3 + .../ui-kit/form-ui/__tests__/form-api.test.ts | 189 + .../@core/ui-kit/form-ui/build.config.ts | 21 + .../@core/ui-kit/form-ui/package.json | 52 + .../@core/ui-kit/form-ui/postcss.config.mjs | 1 + .../form-ui/src/components/form-actions.vue | 188 + .../@core/ui-kit/form-ui/src/config.ts | 87 + .../@core/ui-kit/form-ui/src/form-api.ts | 643 + .../ui-kit/form-ui/src/form-render/context.ts | 24 + .../form-ui/src/form-render/dependencies.ts | 124 + .../form-ui/src/form-render/expandable.ts | 105 + .../form-ui/src/form-render/form-field.vue | 394 + .../form-ui/src/form-render/form-label.vue | 31 + .../ui-kit/form-ui/src/form-render/form.vue | 191 + .../ui-kit/form-ui/src/form-render/helper.ts | 60 + .../ui-kit/form-ui/src/form-render/index.ts | 3 + .../@core/ui-kit/form-ui/src/index.ts | 12 + .../@core/ui-kit/form-ui/src/types.ts | 457 + .../ui-kit/form-ui/src/use-form-context.ts | 109 + .../@core/ui-kit/form-ui/src/use-vben-form.ts | 50 + .../@core/ui-kit/form-ui/src/vben-form.vue | 77 + .../ui-kit/form-ui/src/vben-use-form.vue | 148 + .../@core/ui-kit/form-ui/tailwind.config.mjs | 1 + .../@core/ui-kit/form-ui/tsconfig.json | 6 + .../@core/ui-kit/layout-ui/build.config.ts | 21 + .../@core/ui-kit/layout-ui/package.json | 48 + .../@core/ui-kit/layout-ui/postcss.config.mjs | 1 + .../ui-kit/layout-ui/src/components/index.ts | 5 + .../src/components/layout-content.vue | 64 + .../src/components/layout-footer.vue | 44 + .../src/components/layout-header.vue | 77 + .../src/components/layout-sidebar.vue | 322 + .../src/components/layout-tabbar.vue | 30 + .../layout-ui/src/components/widgets/index.ts | 2 + .../widgets/sidebar-collapse-button.vue | 19 + .../widgets/sidebar-fixed-button.vue | 19 + .../ui-kit/layout-ui/src/hooks/use-layout.ts | 53 + .../@core/ui-kit/layout-ui/src/index.ts | 2 + .../@core/ui-kit/layout-ui/src/vben-layout.ts | 175 + .../ui-kit/layout-ui/src/vben-layout.vue | 616 + .../ui-kit/layout-ui/tailwind.config.mjs | 1 + .../@core/ui-kit/layout-ui/tsconfig.json | 6 + .../packages/@core/ui-kit/menu-ui/README.md | 1 + .../@core/ui-kit/menu-ui/build.config.ts | 26 + .../@core/ui-kit/menu-ui/package.json | 48 + .../@core/ui-kit/menu-ui/postcss.config.mjs | 1 + .../src/components/collapse-transition.vue | 96 + .../ui-kit/menu-ui/src/components/index.ts | 4 + .../menu-ui/src/components/menu-badge-dot.vue | 28 + .../menu-ui/src/components/menu-badge.vue | 57 + .../menu-ui/src/components/menu-item.vue | 122 + .../ui-kit/menu-ui/src/components/menu.vue | 872 + .../src/components/normal-menu/index.ts | 2 + .../src/components/normal-menu/normal-menu.ts | 27 + .../components/normal-menu/normal-menu.vue | 161 + .../src/components/sub-menu-content.vue | 105 + .../menu-ui/src/components/sub-menu.vue | 275 + .../@core/ui-kit/menu-ui/src/hooks/index.ts | 2 + .../menu-ui/src/hooks/use-menu-context.ts | 55 + .../menu-ui/src/hooks/use-menu-scroll.ts | 46 + .../ui-kit/menu-ui/src/hooks/use-menu.ts | 48 + .../@core/ui-kit/menu-ui/src/index.ts | 4 + .../@core/ui-kit/menu-ui/src/menu.vue | 32 + .../@core/ui-kit/menu-ui/src/sub-menu.vue | 71 + .../@core/ui-kit/menu-ui/src/types.ts | 145 + .../@core/ui-kit/menu-ui/src/utils/index.ts | 52 + .../@core/ui-kit/menu-ui/tailwind.config.mjs | 1 + .../@core/ui-kit/menu-ui/tsconfig.json | 6 + .../@core/ui-kit/popup-ui/build.config.ts | 21 + .../@core/ui-kit/popup-ui/package.json | 48 + .../@core/ui-kit/popup-ui/postcss.config.mjs | 1 + .../ui-kit/popup-ui/src/alert/AlertBuilder.ts | 244 + .../@core/ui-kit/popup-ui/src/alert/alert.ts | 99 + .../@core/ui-kit/popup-ui/src/alert/alert.vue | 210 + .../@core/ui-kit/popup-ui/src/alert/index.ts | 14 + .../src/drawer/__tests__/drawer-api.test.ts | 116 + .../ui-kit/popup-ui/src/drawer/drawer-api.ts | 183 + .../ui-kit/popup-ui/src/drawer/drawer.ts | 179 + .../ui-kit/popup-ui/src/drawer/drawer.vue | 332 + .../@core/ui-kit/popup-ui/src/drawer/index.ts | 3 + .../ui-kit/popup-ui/src/drawer/use-drawer.ts | 142 + .../@core/ui-kit/popup-ui/src/index.ts | 3 + .../src/modal/__tests__/modal-api.test.ts | 117 + .../@core/ui-kit/popup-ui/src/modal/index.ts | 3 + .../ui-kit/popup-ui/src/modal/modal-api.ts | 193 + .../@core/ui-kit/popup-ui/src/modal/modal.ts | 194 + .../@core/ui-kit/popup-ui/src/modal/modal.vue | 358 + .../popup-ui/src/modal/use-modal-draggable.ts | 134 + .../ui-kit/popup-ui/src/modal/use-modal.ts | 151 + .../@core/ui-kit/popup-ui/tailwind.config.mjs | 1 + .../@core/ui-kit/popup-ui/tsconfig.json | 6 + .../@core/ui-kit/shadcn-ui/build.config.ts | 27 + .../@core/ui-kit/shadcn-ui/components.json | 16 + .../@core/ui-kit/shadcn-ui/package.json | 54 + .../@core/ui-kit/shadcn-ui/postcss.config.mjs | 1 + .../src/components/avatar/avatar.vue | 76 + .../shadcn-ui/src/components/avatar/index.ts | 1 + .../src/components/back-top/back-top.vue | 43 + .../src/components/back-top/backtop.ts | 38 + .../src/components/back-top/index.ts | 1 + .../src/components/back-top/use-backtop.ts | 45 + .../breadcrumb/breadcrumb-background.vue | 109 + .../components/breadcrumb/breadcrumb-view.vue | 39 + .../src/components/breadcrumb/breadcrumb.vue | 98 + .../src/components/breadcrumb/index.ts | 3 + .../src/components/breadcrumb/types.ts | 17 + .../src/components/button/button-group.vue | 98 + .../shadcn-ui/src/components/button/button.ts | 53 + .../src/components/button/button.vue | 42 + .../components/button/check-button-group.vue | 196 + .../src/components/button/icon-button.vue | 68 + .../shadcn-ui/src/components/button/index.ts | 5 + .../src/components/checkbox/checkbox.vue | 26 + .../src/components/checkbox/index.ts | 1 + .../components/context-menu/context-menu.vue | 97 + .../src/components/context-menu/index.ts | 3 + .../src/components/context-menu/interface.ts | 38 + .../count-to-animator/count-to-animator.vue | 128 + .../src/components/count-to-animator/index.ts | 1 + .../dropdown-menu/dropdown-menu.vue | 49 + .../dropdown-menu/dropdown-radio-menu.vue | 52 + .../src/components/dropdown-menu/index.ts | 4 + .../src/components/dropdown-menu/interface.ts | 32 + .../expandable-arrow/expandable-arrow.vue | 31 + .../src/components/expandable-arrow/index.ts | 1 + .../components/full-screen/full-screen.vue | 28 + .../src/components/full-screen/index.ts | 1 + .../src/components/hover-card/hover-card.vue | 55 + .../src/components/hover-card/index.ts | 2 + .../shadcn-ui/src/components/icon/icon.vue | 35 + .../shadcn-ui/src/components/icon/index.ts | 1 + .../ui-kit/shadcn-ui/src/components/index.ts | 23 + .../src/components/input-password/index.ts | 1 + .../input-password/input-password.vue | 57 + .../input-password/password-strength.vue | 66 + .../shadcn-ui/src/components/logo/index.ts | 1 + .../shadcn-ui/src/components/logo/logo.vue | 73 + .../src/components/pin-input/index.ts | 3 + .../src/components/pin-input/input.vue | 120 + .../src/components/pin-input/types.ts | 30 + .../shadcn-ui/src/components/popover/index.ts | 1 + .../src/components/popover/popover.vue | 60 + .../src/components/render-content/index.ts | 1 + .../render-content/render-content.vue | 56 + .../src/components/scrollbar/index.ts | 1 + .../src/components/scrollbar/scrollbar.vue | 165 + .../src/components/segmented/index.ts | 3 + .../src/components/segmented/segmented.vue | 59 + .../components/segmented/tabs-indicator.vue | 37 + .../src/components/segmented/types.ts | 6 + .../shadcn-ui/src/components/select/index.ts | 1 + .../src/components/select/select.vue | 57 + .../src/components/spine-text/index.ts | 1 + .../src/components/spine-text/spine-text.vue | 49 + .../shadcn-ui/src/components/spinner/index.ts | 2 + .../src/components/spinner/loading.vue | 140 + .../src/components/spinner/spinner.vue | 137 + .../src/components/tooltip/help-tooltip.vue | 31 + .../shadcn-ui/src/components/tooltip/index.ts | 2 + .../src/components/tooltip/tooltip.vue | 44 + .../@core/ui-kit/shadcn-ui/src/index.ts | 3 + .../shadcn-ui/src/ui/accordion/Accordion.vue | 16 + .../src/ui/accordion/AccordionContent.vue | 28 + .../src/ui/accordion/AccordionItem.vue | 25 + .../src/ui/accordion/AccordionTrigger.vue | 39 + .../shadcn-ui/src/ui/accordion/index.ts | 4 + .../src/ui/alert-dialog/AlertDialog.vue | 16 + .../src/ui/alert-dialog/AlertDialogAction.vue | 13 + .../src/ui/alert-dialog/AlertDialogCancel.vue | 13 + .../ui/alert-dialog/AlertDialogContent.vue | 101 + .../alert-dialog/AlertDialogDescription.vue | 28 + .../ui/alert-dialog/AlertDialogOverlay.vue | 8 + .../src/ui/alert-dialog/AlertDialogTitle.vue | 30 + .../shadcn-ui/src/ui/alert-dialog/index.ts | 6 + .../ui-kit/shadcn-ui/src/ui/avatar/Avatar.vue | 27 + .../src/ui/avatar/AvatarFallback.vue | 13 + .../shadcn-ui/src/ui/avatar/AvatarImage.vue | 11 + .../ui-kit/shadcn-ui/src/ui/avatar/avatar.ts | 22 + .../ui-kit/shadcn-ui/src/ui/avatar/index.ts | 4 + .../ui-kit/shadcn-ui/src/ui/badge/Badge.vue | 18 + .../ui-kit/shadcn-ui/src/ui/badge/badge.ts | 25 + .../ui-kit/shadcn-ui/src/ui/badge/index.ts | 3 + .../src/ui/breadcrumb/Breadcrumb.vue | 11 + .../src/ui/breadcrumb/BreadcrumbEllipsis.vue | 22 + .../src/ui/breadcrumb/BreadcrumbItem.vue | 17 + .../src/ui/breadcrumb/BreadcrumbLink.vue | 21 + .../src/ui/breadcrumb/BreadcrumbList.vue | 20 + .../src/ui/breadcrumb/BreadcrumbPage.vue | 18 + .../src/ui/breadcrumb/BreadcrumbSeparator.vue | 21 + .../shadcn-ui/src/ui/breadcrumb/index.ts | 7 + .../ui-kit/shadcn-ui/src/ui/button/Button.vue | 32 + .../ui-kit/shadcn-ui/src/ui/button/button.ts | 34 + .../ui-kit/shadcn-ui/src/ui/button/index.ts | 5 + .../ui-kit/shadcn-ui/src/ui/button/types.ts | 20 + .../ui-kit/shadcn-ui/src/ui/card/Card.vue | 20 + .../shadcn-ui/src/ui/card/CardContent.vue | 13 + .../shadcn-ui/src/ui/card/CardDescription.vue | 13 + .../shadcn-ui/src/ui/card/CardFooter.vue | 13 + .../shadcn-ui/src/ui/card/CardHeader.vue | 13 + .../shadcn-ui/src/ui/card/CardTitle.vue | 13 + .../ui-kit/shadcn-ui/src/ui/card/index.ts | 6 + .../shadcn-ui/src/ui/checkbox/Checkbox.vue | 47 + .../ui-kit/shadcn-ui/src/ui/checkbox/index.ts | 1 + .../src/ui/context-menu/ContextMenu.vue | 18 + .../context-menu/ContextMenuCheckboxItem.vue | 47 + .../ui/context-menu/ContextMenuContent.vue | 43 + .../src/ui/context-menu/ContextMenuGroup.vue | 13 + .../src/ui/context-menu/ContextMenuItem.vue | 37 + .../src/ui/context-menu/ContextMenuLabel.vue | 34 + .../src/ui/context-menu/ContextMenuPortal.vue | 13 + .../ui/context-menu/ContextMenuRadioGroup.vue | 19 + .../ui/context-menu/ContextMenuRadioItem.vue | 47 + .../ui/context-menu/ContextMenuSeparator.vue | 24 + .../ui/context-menu/ContextMenuShortcut.vue | 17 + .../src/ui/context-menu/ContextMenuSub.vue | 16 + .../ui/context-menu/ContextMenuSubContent.vue | 37 + .../ui/context-menu/ContextMenuSubTrigger.vue | 41 + .../ui/context-menu/ContextMenuTrigger.vue | 15 + .../shadcn-ui/src/ui/context-menu/index.ts | 14 + .../ui-kit/shadcn-ui/src/ui/dialog/Dialog.vue | 16 + .../shadcn-ui/src/ui/dialog/DialogClose.vue | 13 + .../shadcn-ui/src/ui/dialog/DialogContent.vue | 136 + .../src/ui/dialog/DialogDescription.vue | 28 + .../shadcn-ui/src/ui/dialog/DialogFooter.vue | 15 + .../shadcn-ui/src/ui/dialog/DialogHeader.vue | 15 + .../shadcn-ui/src/ui/dialog/DialogOverlay.vue | 11 + .../src/ui/dialog/DialogScrollContent.vue | 71 + .../shadcn-ui/src/ui/dialog/DialogTitle.vue | 30 + .../shadcn-ui/src/ui/dialog/DialogTrigger.vue | 13 + .../ui-kit/shadcn-ui/src/ui/dialog/index.ts | 9 + .../src/ui/dropdown-menu/DropdownMenu.vue | 18 + .../DropdownMenuCheckboxItem.vue | 47 + .../ui/dropdown-menu/DropdownMenuContent.vue | 48 + .../ui/dropdown-menu/DropdownMenuGroup.vue | 13 + .../src/ui/dropdown-menu/DropdownMenuItem.vue | 36 + .../ui/dropdown-menu/DropdownMenuLabel.vue | 32 + .../dropdown-menu/DropdownMenuRadioGroup.vue | 19 + .../dropdown-menu/DropdownMenuRadioItem.vue | 48 + .../dropdown-menu/DropdownMenuSeparator.vue | 28 + .../ui/dropdown-menu/DropdownMenuShortcut.vue | 13 + .../src/ui/dropdown-menu/DropdownMenuSub.vue | 16 + .../dropdown-menu/DropdownMenuSubContent.vue | 37 + .../dropdown-menu/DropdownMenuSubTrigger.vue | 35 + .../ui/dropdown-menu/DropdownMenuTrigger.vue | 15 + .../shadcn-ui/src/ui/dropdown-menu/index.ts | 16 + .../shadcn-ui/src/ui/form/FormControl.vue | 19 + .../shadcn-ui/src/ui/form/FormDescription.vue | 20 + .../ui-kit/shadcn-ui/src/ui/form/FormItem.vue | 20 + .../shadcn-ui/src/ui/form/FormLabel.vue | 18 + .../shadcn-ui/src/ui/form/FormMessage.vue | 18 + .../ui-kit/shadcn-ui/src/ui/form/index.ts | 11 + .../shadcn-ui/src/ui/form/injectionKeys.ts | 4 + .../shadcn-ui/src/ui/form/useFormField.ts | 38 + .../shadcn-ui/src/ui/hover-card/HoverCard.vue | 16 + .../src/ui/hover-card/HoverCardContent.vue | 40 + .../src/ui/hover-card/HoverCardTrigger.vue | 13 + .../shadcn-ui/src/ui/hover-card/index.ts | 3 + .../@core/ui-kit/shadcn-ui/src/ui/index.ts | 31 + .../ui-kit/shadcn-ui/src/ui/input/Input.vue | 37 + .../ui-kit/shadcn-ui/src/ui/input/index.ts | 1 + .../ui-kit/shadcn-ui/src/ui/label/Label.vue | 31 + .../ui-kit/shadcn-ui/src/ui/label/index.ts | 1 + .../src/ui/number-field/NumberField.vue | 26 + .../ui/number-field/NumberFieldContent.vue | 20 + .../ui/number-field/NumberFieldDecrement.vue | 37 + .../ui/number-field/NumberFieldIncrement.vue | 37 + .../src/ui/number-field/NumberFieldInput.vue | 16 + .../shadcn-ui/src/ui/number-field/index.ts | 5 + .../src/ui/pagination/PaginationEllipsis.vue | 29 + .../src/ui/pagination/PaginationFirst.vue | 35 + .../src/ui/pagination/PaginationLast.vue | 35 + .../src/ui/pagination/PaginationNext.vue | 35 + .../src/ui/pagination/PaginationPrev.vue | 35 + .../shadcn-ui/src/ui/pagination/index.ts | 10 + .../shadcn-ui/src/ui/pin-input/PinInput.vue | 28 + .../src/ui/pin-input/PinInputGroup.vue | 25 + .../src/ui/pin-input/PinInputInput.vue | 30 + .../src/ui/pin-input/PinInputSeparator.vue | 17 + .../shadcn-ui/src/ui/pin-input/index.ts | 4 + .../shadcn-ui/src/ui/popover/Popover.vue | 16 + .../src/ui/popover/PopoverContent.vue | 46 + .../src/ui/popover/PopoverTrigger.vue | 13 + .../ui-kit/shadcn-ui/src/ui/popover/index.ts | 4 + .../src/ui/radio-group/RadioGroup.vue | 26 + .../src/ui/radio-group/RadioGroupItem.vue | 40 + .../shadcn-ui/src/ui/radio-group/index.ts | 2 + .../src/ui/resizable/ResizableHandle.vue | 50 + .../src/ui/resizable/ResizablePanelGroup.vue | 37 + .../shadcn-ui/src/ui/resizable/index.ts | 3 + .../src/ui/scroll-area/ScrollArea.vue | 50 + .../src/ui/scroll-area/ScrollBar.vue | 40 + .../shadcn-ui/src/ui/scroll-area/index.ts | 2 + .../ui-kit/shadcn-ui/src/ui/select/Select.vue | 16 + .../shadcn-ui/src/ui/select/SelectContent.vue | 67 + .../shadcn-ui/src/ui/select/SelectGroup.vue | 23 + .../shadcn-ui/src/ui/select/SelectItem.vue | 47 + .../src/ui/select/SelectItemText.vue | 13 + .../shadcn-ui/src/ui/select/SelectLabel.vue | 15 + .../src/ui/select/SelectScrollDownButton.vue | 33 + .../src/ui/select/SelectScrollUpButton.vue | 33 + .../src/ui/select/SelectSeparator.vue | 24 + .../shadcn-ui/src/ui/select/SelectTrigger.vue | 37 + .../shadcn-ui/src/ui/select/SelectValue.vue | 13 + .../ui-kit/shadcn-ui/src/ui/select/index.ts | 11 + .../shadcn-ui/src/ui/separator/Separator.vue | 44 + .../shadcn-ui/src/ui/separator/index.ts | 1 + .../ui-kit/shadcn-ui/src/ui/sheet/Sheet.vue | 16 + .../shadcn-ui/src/ui/sheet/SheetClose.vue | 13 + .../shadcn-ui/src/ui/sheet/SheetContent.vue | 107 + .../src/ui/sheet/SheetDescription.vue | 26 + .../shadcn-ui/src/ui/sheet/SheetFooter.vue | 15 + .../shadcn-ui/src/ui/sheet/SheetHeader.vue | 11 + .../shadcn-ui/src/ui/sheet/SheetOverlay.vue | 11 + .../shadcn-ui/src/ui/sheet/SheetTitle.vue | 26 + .../shadcn-ui/src/ui/sheet/SheetTrigger.vue | 13 + .../ui-kit/shadcn-ui/src/ui/sheet/index.ts | 10 + .../ui-kit/shadcn-ui/src/ui/sheet/sheet.ts | 24 + .../ui-kit/shadcn-ui/src/ui/switch/Switch.vue | 41 + .../ui-kit/shadcn-ui/src/ui/switch/index.ts | 1 + .../ui-kit/shadcn-ui/src/ui/tabs/Tabs.vue | 16 + .../shadcn-ui/src/ui/tabs/TabsContent.vue | 31 + .../ui-kit/shadcn-ui/src/ui/tabs/TabsList.vue | 31 + .../shadcn-ui/src/ui/tabs/TabsTrigger.vue | 33 + .../ui-kit/shadcn-ui/src/ui/tabs/index.ts | 5 + .../shadcn-ui/src/ui/textarea/Textarea.vue | 32 + .../ui-kit/shadcn-ui/src/ui/textarea/index.ts | 1 + .../src/ui/toggle-group/ToggleGroup.vue | 44 + .../src/ui/toggle-group/ToggleGroupItem.vue | 48 + .../shadcn-ui/src/ui/toggle-group/index.ts | 2 + .../ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue | 47 + .../ui-kit/shadcn-ui/src/ui/toggle/index.ts | 2 + .../ui-kit/shadcn-ui/src/ui/toggle/toggle.ts | 27 + .../shadcn-ui/src/ui/tooltip/Tooltip.vue | 16 + .../src/ui/tooltip/TooltipContent.vue | 48 + .../src/ui/tooltip/TooltipProvider.vue | 13 + .../src/ui/tooltip/TooltipTrigger.vue | 13 + .../ui-kit/shadcn-ui/src/ui/tooltip/index.ts | 4 + .../ui-kit/shadcn-ui/src/ui/tree/index.ts | 2 + .../ui-kit/shadcn-ui/src/ui/tree/tree.vue | 388 + .../ui-kit/shadcn-ui/src/ui/tree/types.ts | 42 + .../ui-kit/shadcn-ui/tailwind.config.mjs | 1 + .../@core/ui-kit/shadcn-ui/tsconfig.json | 12 + .../@core/ui-kit/tabs-ui/build.config.ts | 21 + .../@core/ui-kit/tabs-ui/package.json | 47 + .../@core/ui-kit/tabs-ui/postcss.config.mjs | 1 + .../ui-kit/tabs-ui/src/components/index.ts | 2 + .../src/components/tabs-chrome/tabs.vue | 209 + .../tabs-ui/src/components/tabs/tabs.vue | 148 + .../tabs-ui/src/components/widgets/index.ts | 2 + .../src/components/widgets/tool-more.vue | 18 + .../src/components/widgets/tool-screen.vue | 19 + .../@core/ui-kit/tabs-ui/src/index.ts | 3 + .../@core/ui-kit/tabs-ui/src/tabs-view.vue | 106 + .../@core/ui-kit/tabs-ui/src/types.ts | 73 + .../@core/ui-kit/tabs-ui/src/use-tabs-drag.ts | 124 + .../tabs-ui/src/use-tabs-view-scroll.ts | 202 + .../@core/ui-kit/tabs-ui/tailwind.config.mjs | 1 + .../@core/ui-kit/tabs-ui/tsconfig.json | 6 + frontend/packages/constants/README.md | 19 + frontend/packages/constants/package.json | 25 + frontend/packages/constants/src/core.ts | 23 + frontend/packages/constants/src/index.ts | 2 + frontend/packages/constants/tsconfig.json | 6 + frontend/packages/effects/README.md | 10 + frontend/packages/effects/access/package.json | 29 + .../effects/access/src/access-control.vue | 47 + .../packages/effects/access/src/accessible.ts | 156 + .../packages/effects/access/src/directive.ts | 42 + frontend/packages/effects/access/src/index.ts | 4 + .../packages/effects/access/src/use-access.ts | 53 + .../packages/effects/access/tsconfig.json | 6 + .../packages/effects/common-ui/package.json | 54 + .../api-component/api-component.vue | 288 + .../src/components/api-component/index.ts | 1 + .../captcha/hooks/useCaptchaPoints.ts | 19 + .../common-ui/src/components/captcha/index.ts | 7 + .../captcha/point-selection-captcha/index.vue | 176 + .../point-selection-captcha-card.vue | 84 + .../captcha/slider-captcha/index.vue | 244 + .../slider-captcha/slider-captcha-action.vue | 65 + .../slider-captcha/slider-captcha-bar.vue | 40 + .../slider-captcha/slider-captcha-content.vue | 53 + .../captcha/slider-rotate-captcha/index.vue | 213 + .../slider-translate-captcha/index.vue | 311 + .../common-ui/src/components/captcha/types.ts | 211 + .../src/components/col-page/col-page.vue | 107 + .../src/components/col-page/index.ts | 2 + .../src/components/col-page/types.ts | 26 + .../src/components/count-to/count-to.vue | 123 + .../src/components/count-to/index.ts | 2 + .../src/components/count-to/types.ts | 53 + .../ellipsis-text/ellipsis-text.vue | 232 + .../src/components/ellipsis-text/index.ts | 1 + .../components/icon-picker/icon-picker.vue | 326 + .../src/components/icon-picker/icons.ts | 56 + .../src/components/icon-picker/index.ts | 1 + .../effects/common-ui/src/components/index.ts | 34 + .../src/components/json-viewer/index.ts | 3 + .../src/components/json-viewer/index.vue | 116 + .../src/components/json-viewer/style.scss | 98 + .../src/components/json-viewer/types.ts | 44 + .../src/components/loading/directive.ts | 132 + .../common-ui/src/components/loading/index.ts | 3 + .../src/components/loading/loading.vue | 39 + .../src/components/loading/spinner.vue | 28 + .../components/page/__tests__/page.test.ts | 89 + .../common-ui/src/components/page/index.ts | 2 + .../common-ui/src/components/page/page.vue | 106 + .../common-ui/src/components/page/types.ts | 17 + .../common-ui/src/components/resize/index.ts | 1 + .../src/components/resize/resize.vue | 1122 + .../src/components/tippy/directive.ts | 100 + .../common-ui/src/components/tippy/index.ts | 67 + .../packages/effects/common-ui/src/index.ts | 2 + .../effects/common-ui/src/ui/about/about.ts | 14 + .../effects/common-ui/src/ui/about/about.vue | 183 + .../effects/common-ui/src/ui/about/index.ts | 1 + .../src/ui/authentication/auth-title.vue | 13 + .../src/ui/authentication/code-login.vue | 117 + .../src/ui/authentication/dingding-login.vue | 113 + .../src/ui/authentication/forget-password.vue | 116 + .../common-ui/src/ui/authentication/index.ts | 7 + .../ui/authentication/login-expired-modal.vue | 95 + .../common-ui/src/ui/authentication/login.vue | 186 + .../src/ui/authentication/qrcode-login.vue | 95 + .../src/ui/authentication/register.vue | 121 + .../ui/authentication/third-party-login.vue | 66 + .../common-ui/src/ui/authentication/types.ts | 70 + .../analysis/analysis-chart-card.vue | 24 + .../analysis/analysis-charts-tabs.vue | 40 + .../dashboard/analysis/analysis-overview.vue | 55 + .../src/ui/dashboard/analysis/index.ts | 3 + .../common-ui/src/ui/dashboard/index.ts | 3 + .../common-ui/src/ui/dashboard/typing.ts | 48 + .../src/ui/dashboard/workbench/index.ts | 5 + .../dashboard/workbench/workbench-header.vue | 46 + .../dashboard/workbench/workbench-project.vue | 65 + .../workbench/workbench-quick-nav.vue | 56 + .../ui/dashboard/workbench/workbench-todo.vue | 63 + .../dashboard/workbench/workbench-trends.vue | 64 + .../common-ui/src/ui/fallback/fallback.ts | 25 + .../common-ui/src/ui/fallback/fallback.vue | 164 + .../src/ui/fallback/icons/icon-403.vue | 151 + .../src/ui/fallback/icons/icon-404.vue | 154 + .../src/ui/fallback/icons/icon-500.vue | 215 + .../ui/fallback/icons/icon-coming-soon.vue | 262 + .../src/ui/fallback/icons/icon-offline.vue | 112 + .../src/ui/fallback/icons/warning.svg | 1 + .../common-ui/src/ui/fallback/index.ts | 2 + .../effects/common-ui/src/ui/index.ts | 4 + .../packages/effects/common-ui/tsconfig.json | 6 + frontend/packages/effects/hooks/README.md | 19 + frontend/packages/effects/hooks/package.json | 33 + frontend/packages/effects/hooks/src/index.ts | 9 + .../effects/hooks/src/use-app-config.ts | 36 + .../effects/hooks/src/use-content-maximize.ts | 24 + .../effects/hooks/src/use-design-tokens.ts | 321 + .../effects/hooks/src/use-hover-toggle.ts | 163 + .../effects/hooks/src/use-pagination.ts | 58 + .../packages/effects/hooks/src/use-refresh.ts | 16 + .../packages/effects/hooks/src/use-tabs.ts | 133 + .../effects/hooks/src/use-watermark.ts | 84 + frontend/packages/effects/hooks/tsconfig.json | 9 + .../packages/effects/layouts/package.json | 43 + .../src/authentication/authentication.vue | 164 + .../layouts/src/authentication/form.vue | 33 + .../src/authentication/icons/slogan.vue | 4568 +++ .../layouts/src/authentication/index.ts | 2 + .../layouts/src/authentication/toolbar.vue | 49 + .../layouts/src/authentication/types.ts | 1 + .../effects/layouts/src/basic/README.md | 7 + .../src/basic/content/content-spinner.vue | 12 + .../layouts/src/basic/content/content.vue | 148 + .../layouts/src/basic/content/index.ts | 2 + .../src/basic/content/use-content-spinner.ts | 50 + .../layouts/src/basic/copyright/copyright.vue | 48 + .../layouts/src/basic/copyright/index.ts | 1 + .../layouts/src/basic/footer/footer.vue | 11 + .../effects/layouts/src/basic/footer/index.ts | 1 + .../layouts/src/basic/header/header.vue | 185 + .../effects/layouts/src/basic/header/index.ts | 1 + .../effects/layouts/src/basic/index.ts | 1 + .../effects/layouts/src/basic/layout.vue | 404 + .../layouts/src/basic/menu/extra-menu.vue | 41 + .../effects/layouts/src/basic/menu/index.ts | 5 + .../effects/layouts/src/basic/menu/menu.vue | 45 + .../layouts/src/basic/menu/mixed-menu.vue | 46 + .../layouts/src/basic/menu/use-extra-menu.ts | 133 + .../layouts/src/basic/menu/use-mixed-menu.ts | 172 + .../layouts/src/basic/menu/use-navigation.ts | 67 + .../effects/layouts/src/basic/tabbar/index.ts | 2 + .../layouts/src/basic/tabbar/tabbar.vue | 75 + .../layouts/src/basic/tabbar/use-tabbar.ts | 227 + .../layouts/src/iframe/iframe-router-view.vue | 86 + .../layouts/src/iframe/iframe-view.vue | 3 + .../effects/layouts/src/iframe/index.ts | 2 + .../packages/effects/layouts/src/index.ts | 4 + .../layouts/src/widgets/breadcrumb.vue | 74 + .../widgets/check-updates/check-updates.vue | 136 + .../src/widgets/check-updates/index.ts | 1 + .../layouts/src/widgets/color-toggle.vue | 64 + .../widgets/global-search/global-search.vue | 157 + .../src/widgets/global-search/index.ts | 1 + .../widgets/global-search/search-panel.vue | 288 + .../effects/layouts/src/widgets/index.ts | 11 + .../layouts/src/widgets/language-toggle.vue | 39 + .../layouts/src/widgets/layout-toggle.vue | 64 + .../layouts/src/widgets/lock-screen/index.ts | 2 + .../widgets/lock-screen/lock-screen-modal.vue | 102 + .../src/widgets/lock-screen/lock-screen.vue | 160 + .../layouts/src/widgets/notification/index.ts | 3 + .../src/widgets/notification/notification.vue | 187 + .../layouts/src/widgets/notification/types.ts | 9 + .../src/widgets/preferences/blocks/block.vue | 22 + .../preferences/blocks/checkbox-item.vue | 63 + .../preferences/blocks/general/animation.vue | 51 + .../preferences/blocks/general/general.vue | 31 + .../src/widgets/preferences/blocks/index.ts | 19 + .../widgets/preferences/blocks/input-item.vue | 52 + .../preferences/blocks/layout/breadcrumb.vue | 56 + .../preferences/blocks/layout/content.vue | 53 + .../preferences/blocks/layout/copyright.vue | 44 + .../preferences/blocks/layout/footer.vue | 17 + .../preferences/blocks/layout/header.vue | 74 + .../preferences/blocks/layout/layout.vue | 112 + .../preferences/blocks/layout/navigation.vue | 45 + .../preferences/blocks/layout/sidebar.vue | 100 + .../preferences/blocks/layout/tabbar.vue | 94 + .../preferences/blocks/layout/widget.vue | 71 + .../preferences/blocks/number-field-item.vue | 74 + .../preferences/blocks/select-item.vue | 68 + .../blocks/shortcut-keys/global.vue | 50 + .../preferences/blocks/switch-item.vue | 55 + .../preferences/blocks/theme/builtin.vue | 161 + .../preferences/blocks/theme/color-mode.vue | 26 + .../preferences/blocks/theme/radius.vue | 38 + .../preferences/blocks/theme/theme.vue | 83 + .../preferences/blocks/toggle-item.vue | 46 + .../preferences/icons/content-compact.vue | 119 + .../preferences/icons/full-content.vue | 50 + .../preferences/icons/header-mixed-nav.vue | 202 + .../widgets/preferences/icons/header-nav.vue | 119 + .../preferences/icons/header-sidebar-nav.vue | 177 + .../src/widgets/preferences/icons/index.ts | 12 + .../widgets/preferences/icons/mixed-nav.vue | 161 + .../src/widgets/preferences/icons/setting.vue | 12 + .../preferences/icons/sidebar-mixed-nav.vue | 173 + .../widgets/preferences/icons/sidebar-nav.vue | 153 + .../layouts/src/widgets/preferences/index.ts | 3 + .../preferences/preferences-button.vue | 20 + .../preferences/preferences-drawer.vue | 449 + .../src/widgets/preferences/preferences.vue | 72 + .../preferences/use-open-preferences.ts | 16 + .../layouts/src/widgets/theme-toggle/index.ts | 1 + .../src/widgets/theme-toggle/theme-button.vue | 185 + .../src/widgets/theme-toggle/theme-toggle.vue | 83 + .../src/widgets/user-dropdown/index.ts | 1 + .../widgets/user-dropdown/user-dropdown.vue | 262 + .../packages/effects/layouts/tsconfig.json | 6 + frontend/packages/effects/plugins/README.md | 28 + .../packages/effects/plugins/package.json | 47 + .../plugins/src/echarts/echarts-ui.vue | 15 + .../effects/plugins/src/echarts/echarts.ts | 59 + .../effects/plugins/src/echarts/index.ts | 3 + .../plugins/src/echarts/use-echarts.ts | 122 + .../effects/plugins/src/motion/index.ts | 8 + .../effects/plugins/src/motion/types.ts | 26 + .../effects/plugins/src/vxe-table/api.ts | 128 + .../effects/plugins/src/vxe-table/extends.ts | 81 + .../effects/plugins/src/vxe-table/index.ts | 10 + .../effects/plugins/src/vxe-table/init.ts | 131 + .../effects/plugins/src/vxe-table/style.css | 117 + .../effects/plugins/src/vxe-table/types.ts | 93 + .../plugins/src/vxe-table/use-vxe-grid.ts | 70 + .../plugins/src/vxe-table/use-vxe-grid.vue | 479 + .../packages/effects/plugins/tsconfig.json | 6 + .../packages/effects/request/package.json | 32 + .../packages/effects/request/src/index.ts | 2 + .../request/src/request-client/index.ts | 3 + .../request-client/modules/downloader.test.ts | 86 + .../src/request-client/modules/downloader.ts | 41 + .../src/request-client/modules/interceptor.ts | 40 + .../request-client/modules/uploader.test.ts | 118 + .../src/request-client/modules/uploader.ts | 42 + .../src/request-client/preset-interceptors.ts | 165 + .../src/request-client/request-client.test.ts | 99 + .../src/request-client/request-client.ts | 150 + .../request/src/request-client/types.ts | 81 + .../packages/effects/request/tsconfig.json | 6 + frontend/packages/icons/README.md | 19 + frontend/packages/icons/package.json | 22 + frontend/packages/icons/src/iconify/index.ts | 15 + .../packages/icons/src/icons/empty-icon.vue | 27 + frontend/packages/icons/src/index.ts | 3 + .../icons/src/svg/icons/antdv-logo.svg | 29 + .../packages/icons/src/svg/icons/avatar-1.svg | 1 + .../packages/icons/src/svg/icons/avatar-2.svg | 1 + .../packages/icons/src/svg/icons/avatar-3.svg | 1 + .../packages/icons/src/svg/icons/avatar-4.svg | 1 + .../packages/icons/src/svg/icons/bell.svg | 1 + .../packages/icons/src/svg/icons/cake.svg | 1 + .../packages/icons/src/svg/icons/card.svg | 1 + .../packages/icons/src/svg/icons/download.svg | 1 + frontend/packages/icons/src/svg/index.ts | 25 + frontend/packages/icons/src/svg/load.ts | 61 + frontend/packages/icons/tsconfig.json | 6 + frontend/packages/locales/package.json | 28 + frontend/packages/locales/src/i18n.ts | 147 + frontend/packages/locales/src/index.ts | 30 + .../src/langs/en-US/authentication.json | 61 + .../locales/src/langs/en-US/common.json | 24 + .../locales/src/langs/en-US/preferences.json | 189 + .../packages/locales/src/langs/en-US/ui.json | 107 + .../src/langs/zh-CN/authentication.json | 61 + .../locales/src/langs/zh-CN/common.json | 24 + .../locales/src/langs/zh-CN/preferences.json | 189 + .../packages/locales/src/langs/zh-CN/ui.json | 107 + frontend/packages/locales/src/typing.ts | 25 + frontend/packages/locales/tsconfig.json | 6 + frontend/packages/preferences/package.json | 26 + frontend/packages/preferences/src/index.ts | 17 + frontend/packages/preferences/tsconfig.json | 6 + frontend/packages/stores/package.json | 32 + frontend/packages/stores/shim-pinia.d.ts | 9 + frontend/packages/stores/src/index.ts | 3 + .../stores/src/modules/access.test.ts | 46 + .../packages/stores/src/modules/access.ts | 129 + frontend/packages/stores/src/modules/index.ts | 3 + .../stores/src/modules/tabbar.test.ts | 300 + .../packages/stores/src/modules/tabbar.ts | 658 + .../packages/stores/src/modules/user.test.ts | 37 + frontend/packages/stores/src/modules/user.ts | 64 + frontend/packages/stores/src/setup.ts | 60 + frontend/packages/stores/tsconfig.json | 5 + frontend/packages/styles/README.md | 19 + frontend/packages/styles/package.json | 34 + frontend/packages/styles/src/antd/index.css | 75 + frontend/packages/styles/src/ele/index.css | 44 + .../packages/styles/src/global/index.scss | 1 + frontend/packages/styles/src/index.ts | 1 + frontend/packages/styles/src/naive/index.css | 20 + frontend/packages/styles/tsconfig.json | 6 + frontend/packages/types/README.md | 20 + frontend/packages/types/global.d.ts | 32 + frontend/packages/types/package.json | 27 + frontend/packages/types/src/index.ts | 2 + frontend/packages/types/src/user.ts | 20 + frontend/packages/types/tsconfig.json | 6 + frontend/packages/utils/README.md | 19 + frontend/packages/utils/package.json | 27 + .../__tests__/find-menu-by-path.test.ts | 88 + .../helpers/__tests__/generate-menus.test.ts | 233 + .../generate-routes-frontend.test.ts | 105 + .../__tests__/merge-route-modules.test.ts | 68 + .../utils/src/helpers/find-menu-by-path.ts | 37 + .../utils/src/helpers/generate-menus.ts | 90 + .../src/helpers/generate-routes-backend.ts | 86 + .../src/helpers/generate-routes-frontend.ts | 58 + .../utils/src/helpers/get-popup-container.ts | 10 + frontend/packages/utils/src/helpers/index.ts | 8 + .../utils/src/helpers/merge-route-modules.ts | 28 + .../utils/src/helpers/reset-routes.ts | 31 + .../src/helpers/unmount-global-loading.ts | 31 + frontend/packages/utils/src/index.ts | 4 + frontend/packages/utils/tsconfig.json | 9 + frontend/playground/.env | 8 + frontend/playground/.env.analyze | 7 + frontend/playground/.env.development | 20 + frontend/playground/.env.production | 19 + .../__tests__/e2e/auth-login.spec.ts | 20 + .../playground/__tests__/e2e/common/auth.ts | 46 + frontend/playground/index.html | 35 + frontend/playground/package.json | 59 + frontend/playground/playwright.config.ts | 108 + frontend/playground/postcss.config.mjs | 1 + frontend/playground/public/favicon.ico | Bin 0 -> 5430 bytes .../playground/src/adapter/component/index.ts | 207 + frontend/playground/src/adapter/form.ts | 47 + frontend/playground/src/adapter/vxe-table.ts | 297 + frontend/playground/src/api/core/auth.ts | 57 + frontend/playground/src/api/core/index.ts | 3 + frontend/playground/src/api/core/menu.ts | 10 + frontend/playground/src/api/core/user.ts | 10 + .../playground/src/api/examples/download.ts | 28 + frontend/playground/src/api/examples/index.ts | 2 + .../src/api/examples/json-bigint.ts | 10 + .../playground/src/api/examples/params.ts | 19 + .../playground/src/api/examples/status.ts | 10 + frontend/playground/src/api/examples/table.ts | 18 + .../playground/src/api/examples/upload.ts | 25 + frontend/playground/src/api/index.ts | 3 + frontend/playground/src/api/request.ts | 129 + frontend/playground/src/api/system/dept.ts | 54 + frontend/playground/src/api/system/index.ts | 3 + frontend/playground/src/api/system/menu.ts | 158 + frontend/playground/src/api/system/role.ts | 55 + frontend/playground/src/app.vue | 39 + frontend/playground/src/bootstrap.ts | 80 + frontend/playground/src/layouts/auth.vue | 25 + frontend/playground/src/layouts/basic.vue | 183 + frontend/playground/src/layouts/index.ts | 6 + frontend/playground/src/locales/README.md | 3 + frontend/playground/src/locales/index.ts | 102 + .../src/locales/langs/en-US/demos.json | 70 + .../src/locales/langs/en-US/examples.json | 76 + .../src/locales/langs/en-US/page.json | 16 + .../src/locales/langs/en-US/system.json | 65 + .../src/locales/langs/zh-CN/demos.json | 71 + .../src/locales/langs/zh-CN/examples.json | 76 + .../src/locales/langs/zh-CN/page.json | 16 + .../src/locales/langs/zh-CN/system.json | 67 + frontend/playground/src/main.ts | 31 + frontend/playground/src/preferences.ts | 13 + frontend/playground/src/router/access.ts | 42 + frontend/playground/src/router/guard.ts | 136 + frontend/playground/src/router/index.ts | 37 + frontend/playground/src/router/routes/core.ts | 97 + .../playground/src/router/routes/index.ts | 47 + .../src/router/routes/modules/dashboard.ts | 38 + .../src/router/routes/modules/demos.ts | 594 + .../src/router/routes/modules/examples.ts | 335 + .../src/router/routes/modules/system.ts | 46 + .../src/router/routes/modules/vben.ts | 94 + frontend/playground/src/store/auth.ts | 120 + frontend/playground/src/store/index.ts | 1 + frontend/playground/src/views/_core/README.md | 3 + .../src/views/_core/about/index.vue | 9 + .../views/_core/authentication/code-login.vue | 109 + .../_core/authentication/forget-password.vue | 42 + .../src/views/_core/authentication/login.vue | 132 + .../_core/authentication/qrcode-login.vue | 10 + .../views/_core/authentication/register.vue | 96 + .../src/views/_core/fallback/coming-soon.vue | 7 + .../src/views/_core/fallback/forbidden.vue | 9 + .../views/_core/fallback/internal-error.vue | 9 + .../src/views/_core/fallback/not-found.vue | 9 + .../src/views/_core/fallback/offline.vue | 9 + .../dashboard/analytics/analytics-trends.vue | 98 + .../analytics/analytics-visits-data.vue | 82 + .../analytics/analytics-visits-sales.vue | 46 + .../analytics/analytics-visits-source.vue | 65 + .../dashboard/analytics/analytics-visits.vue | 55 + .../src/views/dashboard/analytics/index.vue | 90 + .../src/views/dashboard/workspace/index.vue | 266 + .../src/views/demos/access/admin-visible.vue | 11 + .../src/views/demos/access/button-control.vue | 157 + .../src/views/demos/access/index.vue | 98 + .../views/demos/access/menu-visible-403.vue | 11 + .../src/views/demos/access/super-visible.vue | 11 + .../src/views/demos/access/user-visible.vue | 11 + .../src/views/demos/active-icon/index.vue | 11 + .../src/views/demos/badge/index.vue | 117 + .../views/demos/breadcrumb/lateral-detail.vue | 21 + .../src/views/demos/breadcrumb/lateral.vue | 25 + .../views/demos/breadcrumb/level-detail.vue | 11 + .../views/demos/features/clipboard/index.vue | 25 + .../demos/features/file-download/base64.ts | 1 + .../demos/features/file-download/index.vue | 100 + .../demos/features/full-screen/index.vue | 47 + .../features/hide-menu-children/children.vue | 23 + .../features/hide-menu-children/parent.vue | 17 + .../src/views/demos/features/icons/index.vue | 115 + .../demos/features/json-bigint/index.vue | 39 + .../demos/features/login-expired/index.vue | 39 + .../views/demos/features/menu-query/index.vue | 11 + .../views/demos/features/new-window/index.vue | 11 + .../request-params-serializer/index.vue | 61 + .../src/views/demos/features/tabs/index.vue | 105 + .../views/demos/features/tabs/tab-detail.vue | 23 + .../vue-query/concurrency-caching.vue | 61 + .../views/demos/features/vue-query/index.vue | 40 + .../features/vue-query/infinite-queries.vue | 58 + .../features/vue-query/paginated-queries.vue | 53 + .../features/vue-query/query-retries.vue | 34 + .../views/demos/features/vue-query/typing.ts | 18 + .../views/demos/features/watermark/index.vue | 86 + .../src/views/demos/nested/menu-1.vue | 7 + .../src/views/demos/nested/menu-2-1.vue | 7 + .../src/views/demos/nested/menu-3-1.vue | 7 + .../src/views/demos/nested/menu-3-2-1.vue | 7 + .../src/views/examples/button-group/index.vue | 229 + .../captcha/point-selection-captcha.vue | 181 + .../views/examples/captcha/slider-captcha.vue | 117 + .../captcha/slider-rotate-captcha.vue | 28 + .../captcha/slider-translate-captcha.vue | 27 + .../src/views/examples/count-to/index.vue | 178 + .../src/views/examples/doc-button.vue | 22 + .../examples/drawer/auto-height-demo.vue | 47 + .../src/views/examples/drawer/base-demo.vue | 35 + .../views/examples/drawer/dynamic-demo.vue | 31 + .../examples/drawer/form-drawer-demo.vue | 56 + .../views/examples/drawer/in-content-demo.vue | 48 + .../src/views/examples/drawer/index.vue | 195 + .../examples/drawer/shared-data-demo.vue | 29 + .../src/views/examples/ellipsis/index.vue | 46 + .../src/views/examples/form/api.vue | 274 + .../src/views/examples/form/basic.vue | 447 + .../src/views/examples/form/custom-layout.vue | 111 + .../src/views/examples/form/custom.vue | 100 + .../src/views/examples/form/dynamic.vue | 262 + .../src/views/examples/form/merge.vue | 116 + .../examples/form/modules/two-fields.vue | 42 + .../src/views/examples/form/query.vue | 216 + .../src/views/examples/form/rules.vue | 245 + .../examples/form/scroll-to-error-test.vue | 183 + .../src/views/examples/json-viewer/data.ts | 66 + .../src/views/examples/json-viewer/index.vue | 51 + .../src/views/examples/layout/col-page.vue | 106 + .../src/views/examples/loading/index.vue | 101 + .../views/examples/modal/auto-height-demo.vue | 49 + .../src/views/examples/modal/base-demo.vue | 34 + .../src/views/examples/modal/blur-demo.vue | 23 + .../src/views/examples/modal/drag-demo.vue | 19 + .../src/views/examples/modal/dynamic-demo.vue | 41 + .../views/examples/modal/form-modal-demo.vue | 91 + .../views/examples/modal/in-content-demo.vue | 30 + .../src/views/examples/modal/index.vue | 278 + .../src/views/examples/modal/nested-demo.vue | 24 + .../views/examples/modal/shared-data-demo.vue | 29 + .../src/views/examples/motion/index.vue | 213 + .../src/views/examples/resize/basic.vue | 58 + .../src/views/examples/tippy/index.vue | 303 + .../src/views/examples/vxe-table/basic.vue | 111 + .../views/examples/vxe-table/custom-cell.vue | 108 + .../views/examples/vxe-table/edit-cell.vue | 57 + .../src/views/examples/vxe-table/edit-row.vue | 94 + .../src/views/examples/vxe-table/fixed.vue | 69 + .../src/views/examples/vxe-table/form.vue | 127 + .../src/views/examples/vxe-table/remote.vue | 81 + .../views/examples/vxe-table/table-data.ts | 172 + .../src/views/examples/vxe-table/tree.vue | 62 + .../src/views/examples/vxe-table/virtual.vue | 66 + .../playground/src/views/system/dept/data.ts | 135 + .../playground/src/views/system/dept/list.vue | 143 + .../src/views/system/dept/modules/form.vue | 78 + .../playground/src/views/system/menu/data.ts | 109 + .../playground/src/views/system/menu/list.vue | 162 + .../src/views/system/menu/modules/form.vue | 505 + .../playground/src/views/system/role/data.ts | 127 + .../playground/src/views/system/role/list.vue | 164 + .../src/views/system/role/modules/form.vue | 139 + frontend/playground/tailwind.config.mjs | 1 + frontend/playground/tsconfig.json | 12 + frontend/playground/tsconfig.node.json | 10 + frontend/playground/vite.config.mts | 20 + frontend/pnpm-lock.yaml | 22538 +++++++++++ frontend/pnpm-workspace.yaml | 194 + frontend/scripts/clean.mjs | 56 + frontend/scripts/deploy/Dockerfile | 37 + .../deploy/build-local-docker-image.sh | 55 + frontend/scripts/deploy/nginx.conf | 75 + frontend/scripts/turbo-run/README.md | 59 + frontend/scripts/turbo-run/bin/turbo-run.mjs | 3 + frontend/scripts/turbo-run/build.config.ts | 7 + frontend/scripts/turbo-run/package.json | 29 + frontend/scripts/turbo-run/src/index.ts | 29 + frontend/scripts/turbo-run/src/run.ts | 67 + frontend/scripts/turbo-run/tsconfig.json | 6 + frontend/scripts/vsh/README.md | 56 + frontend/scripts/vsh/bin/vsh.mjs | 3 + frontend/scripts/vsh/build.config.ts | 7 + frontend/scripts/vsh/package.json | 31 + .../scripts/vsh/src/check-circular/index.ts | 170 + frontend/scripts/vsh/src/check-dep/index.ts | 194 + .../scripts/vsh/src/code-workspace/index.ts | 78 + frontend/scripts/vsh/src/index.ts | 74 + frontend/scripts/vsh/src/lint/index.ts | 48 + frontend/scripts/vsh/src/publint/index.ts | 185 + frontend/scripts/vsh/tsconfig.json | 6 + frontend/src/App.tsx | 27 - frontend/src/api/http.ts | 37 - frontend/src/api/nodeType.ts | 17 - frontend/src/api/task.ts | 48 - frontend/src/api/workflow.ts | 39 - frontend/src/components/common/CodeEditor.tsx | 10 - frontend/src/components/common/Empty.tsx | 10 - frontend/src/components/common/Error.tsx | 6 - .../src/components/common/ExpressionInput.tsx | 33 - .../common/FieldMappingSelector.tsx | 85 - frontend/src/components/common/JsonView.tsx | 21 - .../src/components/common/KeyValueEditor.tsx | 32 - frontend/src/components/common/Loading.tsx | 10 - frontend/src/components/editor/Canvas.tsx | 224 - .../src/components/editor/ConditionNode.tsx | 40 - frontend/src/components/editor/CustomNode.tsx | 32 - .../components/editor/EdgePropertyPanel.tsx | 60 - .../src/components/editor/NodePalette.tsx | 85 - .../components/editor/NodePropertyPanel.tsx | 146 - frontend/src/components/layout/AppLayout.tsx | 41 - frontend/src/index.css | 15 - frontend/src/main.tsx | 10 - frontend/src/pages/ApprovalCenterPage.tsx | 117 - frontend/src/pages/ExecutionHistoryPage.tsx | 173 - frontend/src/pages/WorkflowEditorPage.tsx | 177 - frontend/src/pages/WorkflowListPage.tsx | 43 - frontend/src/store/editorStore.ts | 40 - frontend/src/store/nodeTypeStore.ts | 23 - frontend/src/store/workflowStore.ts | 25 - frontend/src/types/node.ts | 25 - frontend/src/types/workflow.ts | 73 - frontend/src/utils/download.ts | 11 - frontend/src/utils/schema.ts | 44 - frontend/src/utils/storage.ts | 16 - frontend/src/utils/validate.ts | 12 - frontend/src/vite-env.d.ts | 1 - frontend/stylelint.config.mjs | 4 + frontend/tea.yaml | 6 + frontend/tsconfig.json | 20 - frontend/tsconfig.tsbuildinfo | 1 - frontend/turbo.json | 49 + frontend/vben-admin.code-workspace | 172 + frontend/vite.config.ts | 22 - frontend/vitest.config.ts | 11 + frontend/vitest.workspace.ts | 3 + 1436 files changed, 139580 insertions(+), 42873 deletions(-) create mode 100644 backend/docs/01-架构总览.md create mode 100644 backend/docs/02-后端技术设计.md create mode 100644 backend/docs/03-前端技术设计.md create mode 100644 backend/docs/04-数据模型设计.md create mode 100644 backend/docs/05-API契约.md create mode 100644 backend/docs/05-开发规范.md create mode 100644 backend/docs/99-最终修正落地方案.md create mode 100644 backend/docs/README.md delete mode 100644 backend/logs/flowable-devops.log delete mode 100644 backend/src/test/resources/application-test.yml create mode 100644 frontend/.browserslistrc create mode 100644 frontend/.commitlintrc.js create mode 100644 frontend/.cursorignore create mode 100644 frontend/.dockerignore create mode 100644 frontend/.editorconfig delete mode 100644 frontend/.env.example delete mode 100644 frontend/.eslintrc.json create mode 100644 frontend/.gitattributes create mode 100644 frontend/.gitconfig create mode 100644 frontend/.gitignore create mode 100644 frontend/.gitpod.yml delete mode 100644 frontend/.idea/.gitignore delete mode 100644 frontend/.idea/frontend.iml delete mode 100644 frontend/.idea/inspectionProfiles/Project_Default.xml delete mode 100644 frontend/.idea/modules.xml delete mode 100644 frontend/.idea/vcs.xml create mode 100644 frontend/.node-version create mode 100644 frontend/.npmrc create mode 100644 frontend/.prettierignore delete mode 100644 frontend/.prettierrc create mode 100644 frontend/.prettierrc.mjs create mode 100644 frontend/.stylelintignore create mode 100644 frontend/LICENSE create mode 100644 frontend/README.ja-JP.md create mode 100644 frontend/README.zh-CN.md create mode 100644 frontend/apps/web-antd/.env create mode 100644 frontend/apps/web-antd/.env.analyze create mode 100644 frontend/apps/web-antd/.env.development create mode 100644 frontend/apps/web-antd/.env.production create mode 100644 frontend/apps/web-antd/CLAUDE.md create mode 100644 frontend/apps/web-antd/index.html create mode 100644 frontend/apps/web-antd/node-size-comparison.html create mode 100644 frontend/apps/web-antd/package.json create mode 100644 frontend/apps/web-antd/postcss.config.mjs create mode 100644 frontend/apps/web-antd/public/favicon.ico create mode 100644 frontend/apps/web-antd/src/adapter/component/index.ts create mode 100644 frontend/apps/web-antd/src/adapter/form.ts create mode 100644 frontend/apps/web-antd/src/adapter/vxe-table.ts create mode 100644 frontend/apps/web-antd/src/api/core/auth.ts create mode 100644 frontend/apps/web-antd/src/api/core/index.ts create mode 100644 frontend/apps/web-antd/src/api/core/menu.ts create mode 100644 frontend/apps/web-antd/src/api/core/user.ts create mode 100644 frontend/apps/web-antd/src/api/index.ts create mode 100644 frontend/apps/web-antd/src/api/request.ts create mode 100644 frontend/apps/web-antd/src/app.vue create mode 100644 frontend/apps/web-antd/src/bootstrap.ts create mode 100644 frontend/apps/web-antd/src/layouts/auth.vue create mode 100644 frontend/apps/web-antd/src/layouts/basic.vue create mode 100644 frontend/apps/web-antd/src/layouts/index.ts create mode 100644 frontend/apps/web-antd/src/locales/README.md create mode 100644 frontend/apps/web-antd/src/locales/index.ts create mode 100644 frontend/apps/web-antd/src/locales/langs/en-US/demos.json create mode 100644 frontend/apps/web-antd/src/locales/langs/en-US/page.json create mode 100644 frontend/apps/web-antd/src/locales/langs/zh-CN/demos.json create mode 100644 frontend/apps/web-antd/src/locales/langs/zh-CN/page.json create mode 100644 frontend/apps/web-antd/src/main.ts create mode 100644 frontend/apps/web-antd/src/preferences.ts create mode 100644 frontend/apps/web-antd/src/router/access.ts create mode 100644 frontend/apps/web-antd/src/router/guard.ts create mode 100644 frontend/apps/web-antd/src/router/index.ts create mode 100644 frontend/apps/web-antd/src/router/routes/core.ts create mode 100644 frontend/apps/web-antd/src/router/routes/index.ts create mode 100644 frontend/apps/web-antd/src/router/routes/modules/dashboard.ts create mode 100644 frontend/apps/web-antd/src/router/routes/modules/demos.ts create mode 100644 frontend/apps/web-antd/src/router/routes/modules/vben.ts create mode 100644 frontend/apps/web-antd/src/router/routes/modules/workflow.ts create mode 100644 frontend/apps/web-antd/src/store/auth.ts create mode 100644 frontend/apps/web-antd/src/store/index.ts create mode 100644 frontend/apps/web-antd/src/store/workflow.ts create mode 100644 frontend/apps/web-antd/src/types/bpmn.d.ts create mode 100644 frontend/apps/web-antd/src/types/workflow.ts create mode 100644 frontend/apps/web-antd/src/views/_core/README.md create mode 100644 frontend/apps/web-antd/src/views/_core/about/index.vue create mode 100644 frontend/apps/web-antd/src/views/_core/authentication/code-login.vue create mode 100644 frontend/apps/web-antd/src/views/_core/authentication/forget-password.vue create mode 100644 frontend/apps/web-antd/src/views/_core/authentication/login.vue create mode 100644 frontend/apps/web-antd/src/views/_core/authentication/qrcode-login.vue create mode 100644 frontend/apps/web-antd/src/views/_core/authentication/register.vue create mode 100644 frontend/apps/web-antd/src/views/_core/fallback/coming-soon.vue create mode 100644 frontend/apps/web-antd/src/views/_core/fallback/forbidden.vue create mode 100644 frontend/apps/web-antd/src/views/_core/fallback/internal-error.vue create mode 100644 frontend/apps/web-antd/src/views/_core/fallback/not-found.vue create mode 100644 frontend/apps/web-antd/src/views/_core/fallback/offline.vue create mode 100644 frontend/apps/web-antd/src/views/dashboard/analytics/analytics-trends.vue create mode 100644 frontend/apps/web-antd/src/views/dashboard/analytics/analytics-visits-data.vue create mode 100644 frontend/apps/web-antd/src/views/dashboard/analytics/analytics-visits-sales.vue create mode 100644 frontend/apps/web-antd/src/views/dashboard/analytics/analytics-visits-source.vue create mode 100644 frontend/apps/web-antd/src/views/dashboard/analytics/analytics-visits.vue create mode 100644 frontend/apps/web-antd/src/views/dashboard/analytics/index.vue create mode 100644 frontend/apps/web-antd/src/views/dashboard/workspace/index.vue create mode 100644 frontend/apps/web-antd/src/views/demos/antd/index.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/README.md create mode 100644 frontend/apps/web-antd/src/views/workflow/process-design/QUICK_TEST.md create mode 100644 frontend/apps/web-antd/src/views/workflow/process-design/README.md create mode 100644 frontend/apps/web-antd/src/views/workflow/process-design/SOLUTION.md create mode 100644 frontend/apps/web-antd/src/views/workflow/process-design/TRANSLATION_DEBUG.md create mode 100644 frontend/apps/web-antd/src/views/workflow/process-design/UPGRADE.md create mode 100644 frontend/apps/web-antd/src/views/workflow/process-design/index.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/process-design/translate/zh-CN.ts create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/ARCHITECTURE.md create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/DEBUG_FIELD_MAPPING.md create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/FIELD_MAPPING_GUIDE.md create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/IMPLEMENTATION_SUMMARY.md create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/QUICK_TEST_FIELD_MAPPING.md create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/README.md create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/TEST_WORKFLOW.md create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/USAGE_GUIDE.md create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/components/BaseNode.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/components/FieldMappingSelector.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/components/NodeConfigPanel.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/components/NodePalette.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/components/ParameterEditor.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/index.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/mock/nodeTypes.ts create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/mock/workflows.ts create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/nodes/DecisionNode.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/nodes/EditableTaskNode.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/nodes/EndNode.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/nodes/StartNode.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/nodes/TaskNode.vue create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/stores/useWorkflowStore.ts create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/utils/expression.ts create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-design/utils/fieldTree.ts create mode 100644 frontend/apps/web-antd/src/views/workflow/vue-flow-test/index.vue create mode 100644 frontend/apps/web-antd/start_dev.bat create mode 100644 frontend/apps/web-antd/tailwind.config.mjs create mode 100644 frontend/apps/web-antd/tsconfig.json create mode 100644 frontend/apps/web-antd/tsconfig.node.json create mode 100644 frontend/apps/web-antd/vite.config.mts create mode 100644 frontend/cspell.json delete mode 100644 frontend/dev.log delete mode 100644 frontend/dist/assets/index-CTkmAz9X.css delete mode 100644 frontend/dist/assets/index-D9R4V7sH.js delete mode 100644 frontend/dist/index.html create mode 100644 frontend/docs/.vitepress/components/demo-preview.vue create mode 100644 frontend/docs/.vitepress/components/index.ts create mode 100644 frontend/docs/.vitepress/components/preview-group.vue create mode 100644 frontend/docs/.vitepress/config/en.mts create mode 100644 frontend/docs/.vitepress/config/index.mts create mode 100644 frontend/docs/.vitepress/config/plugins/demo-preview.ts create mode 100644 frontend/docs/.vitepress/config/shared.mts create mode 100644 frontend/docs/.vitepress/config/zh.mts create mode 100644 frontend/docs/.vitepress/theme/components/site-layout.vue create mode 100644 frontend/docs/.vitepress/theme/components/vben-contributors.vue create mode 100644 frontend/docs/.vitepress/theme/index.ts create mode 100644 frontend/docs/.vitepress/theme/plugins/hm.ts create mode 100644 frontend/docs/.vitepress/theme/styles/base.css create mode 100644 frontend/docs/.vitepress/theme/styles/index.ts create mode 100644 frontend/docs/.vitepress/theme/styles/variables.css create mode 100644 frontend/docs/01-架构总览.md create mode 100644 frontend/docs/02-后端技术设计.md create mode 100644 frontend/docs/03-前端技术设计.md create mode 100644 frontend/docs/04-数据模型设计.md create mode 100644 frontend/docs/05-API契约.md create mode 100644 frontend/docs/05-开发规范.md create mode 100644 frontend/docs/99-最终修正落地方案.md create mode 100644 frontend/docs/README.md create mode 100644 frontend/docs/package.json create mode 100644 frontend/docs/src/_env/adapter/component.ts create mode 100644 frontend/docs/src/_env/adapter/form.ts create mode 100644 frontend/docs/src/_env/adapter/vxe-table.ts create mode 100644 frontend/docs/src/_env/node/adapter/form.ts create mode 100644 frontend/docs/src/_env/node/adapter/vxe-table.ts create mode 100644 frontend/docs/src/commercial/community.md create mode 100644 frontend/docs/src/commercial/customized.md create mode 100644 frontend/docs/src/commercial/technical-support.md create mode 100644 frontend/docs/src/components/common-ui/vben-alert.md create mode 100644 frontend/docs/src/components/common-ui/vben-api-component.md create mode 100644 frontend/docs/src/components/common-ui/vben-count-to-animator.md create mode 100644 frontend/docs/src/components/common-ui/vben-drawer.md create mode 100644 frontend/docs/src/components/common-ui/vben-ellipsis-text.md create mode 100644 frontend/docs/src/components/common-ui/vben-form.md create mode 100644 frontend/docs/src/components/common-ui/vben-modal.md create mode 100644 frontend/docs/src/components/common-ui/vben-vxe-table.md create mode 100644 frontend/docs/src/components/introduction.md create mode 100644 frontend/docs/src/components/layout-ui/page.md create mode 100644 frontend/docs/src/demos/vben-alert/alert/index.vue create mode 100644 frontend/docs/src/demos/vben-alert/confirm/index.vue create mode 100644 frontend/docs/src/demos/vben-alert/prompt/index.vue create mode 100644 frontend/docs/src/demos/vben-api-component/cascader/index.vue create mode 100644 frontend/docs/src/demos/vben-count-to-animator/basic/index.vue create mode 100644 frontend/docs/src/demos/vben-count-to-animator/custom/index.vue create mode 100644 frontend/docs/src/demos/vben-drawer/auto-height/drawer.vue create mode 100644 frontend/docs/src/demos/vben-drawer/auto-height/index.vue create mode 100644 frontend/docs/src/demos/vben-drawer/basic/index.vue create mode 100644 frontend/docs/src/demos/vben-drawer/dynamic/drawer.vue create mode 100644 frontend/docs/src/demos/vben-drawer/dynamic/index.vue create mode 100644 frontend/docs/src/demos/vben-drawer/extra/drawer.vue create mode 100644 frontend/docs/src/demos/vben-drawer/extra/index.vue create mode 100644 frontend/docs/src/demos/vben-drawer/shared-data/drawer.vue create mode 100644 frontend/docs/src/demos/vben-drawer/shared-data/index.vue create mode 100644 frontend/docs/src/demos/vben-ellipsis-text/auto-display/index.vue create mode 100644 frontend/docs/src/demos/vben-ellipsis-text/expand/index.vue create mode 100644 frontend/docs/src/demos/vben-ellipsis-text/line/index.vue create mode 100644 frontend/docs/src/demos/vben-ellipsis-text/tooltip/index.vue create mode 100644 frontend/docs/src/demos/vben-form/api/index.vue create mode 100644 frontend/docs/src/demos/vben-form/basic/index.vue create mode 100644 frontend/docs/src/demos/vben-form/custom/index.vue create mode 100644 frontend/docs/src/demos/vben-form/dynamic/index.vue create mode 100644 frontend/docs/src/demos/vben-form/query/index.vue create mode 100644 frontend/docs/src/demos/vben-form/rules/index.vue create mode 100644 frontend/docs/src/demos/vben-modal/animation-type/index.vue create mode 100644 frontend/docs/src/demos/vben-modal/auto-height/index.vue create mode 100644 frontend/docs/src/demos/vben-modal/auto-height/modal.vue create mode 100644 frontend/docs/src/demos/vben-modal/basic/index.vue create mode 100644 frontend/docs/src/demos/vben-modal/draggable/index.vue create mode 100644 frontend/docs/src/demos/vben-modal/draggable/modal.vue create mode 100644 frontend/docs/src/demos/vben-modal/dynamic/index.vue create mode 100644 frontend/docs/src/demos/vben-modal/dynamic/modal.vue create mode 100644 frontend/docs/src/demos/vben-modal/extra/index.vue create mode 100644 frontend/docs/src/demos/vben-modal/extra/modal.vue create mode 100644 frontend/docs/src/demos/vben-modal/shared-data/index.vue create mode 100644 frontend/docs/src/demos/vben-modal/shared-data/modal.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/basic/index.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/custom-cell/index.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/edit-cell/index.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/edit-row/index.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/fixed/index.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/form/index.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/mock-api.ts create mode 100644 frontend/docs/src/demos/vben-vxe-table/remote/index.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/table-data.ts create mode 100644 frontend/docs/src/demos/vben-vxe-table/tree/index.vue create mode 100644 frontend/docs/src/demos/vben-vxe-table/virtual/index.vue create mode 100644 frontend/docs/src/en/guide/essentials/build.md create mode 100644 frontend/docs/src/en/guide/essentials/concept.md create mode 100644 frontend/docs/src/en/guide/essentials/development.md create mode 100644 frontend/docs/src/en/guide/essentials/external-module.md create mode 100644 frontend/docs/src/en/guide/essentials/icons.md create mode 100644 frontend/docs/src/en/guide/essentials/route.md create mode 100644 frontend/docs/src/en/guide/essentials/server.md create mode 100644 frontend/docs/src/en/guide/essentials/settings.md create mode 100644 frontend/docs/src/en/guide/essentials/styles.md create mode 100644 frontend/docs/src/en/guide/in-depth/access.md create mode 100644 frontend/docs/src/en/guide/in-depth/check-updates.md create mode 100644 frontend/docs/src/en/guide/in-depth/features.md create mode 100644 frontend/docs/src/en/guide/in-depth/layout.md create mode 100644 frontend/docs/src/en/guide/in-depth/loading.md create mode 100644 frontend/docs/src/en/guide/in-depth/locale.md create mode 100644 frontend/docs/src/en/guide/in-depth/login.md create mode 100644 frontend/docs/src/en/guide/in-depth/theme.md create mode 100644 frontend/docs/src/en/guide/in-depth/ui-framework.md create mode 100644 frontend/docs/src/en/guide/introduction/changelog.md create mode 100644 frontend/docs/src/en/guide/introduction/quick-start.md create mode 100644 frontend/docs/src/en/guide/introduction/roadmap.md create mode 100644 frontend/docs/src/en/guide/introduction/thin.md create mode 100644 frontend/docs/src/en/guide/introduction/vben.md create mode 100644 frontend/docs/src/en/guide/introduction/why.md create mode 100644 frontend/docs/src/en/guide/other/faq.md create mode 100644 frontend/docs/src/en/guide/other/project-update.md create mode 100644 frontend/docs/src/en/guide/other/remove-code.md create mode 100644 frontend/docs/src/en/guide/project/changeset.md create mode 100644 frontend/docs/src/en/guide/project/cli.md create mode 100644 frontend/docs/src/en/guide/project/dir.md create mode 100644 frontend/docs/src/en/guide/project/standard.md create mode 100644 frontend/docs/src/en/guide/project/tailwindcss.md create mode 100644 frontend/docs/src/en/guide/project/test.md create mode 100644 frontend/docs/src/en/guide/project/vite.md create mode 100644 frontend/docs/src/en/index.md create mode 100644 frontend/docs/src/friend-links/index.md create mode 100644 frontend/docs/src/guide/essentials/build.md create mode 100644 frontend/docs/src/guide/essentials/concept.md create mode 100644 frontend/docs/src/guide/essentials/development.md create mode 100644 frontend/docs/src/guide/essentials/external-module.md create mode 100644 frontend/docs/src/guide/essentials/icons.md create mode 100644 frontend/docs/src/guide/essentials/route.md create mode 100644 frontend/docs/src/guide/essentials/server.md create mode 100644 frontend/docs/src/guide/essentials/settings.md create mode 100644 frontend/docs/src/guide/essentials/styles.md create mode 100644 frontend/docs/src/guide/in-depth/access.md create mode 100644 frontend/docs/src/guide/in-depth/check-updates.md create mode 100644 frontend/docs/src/guide/in-depth/features.md create mode 100644 frontend/docs/src/guide/in-depth/layout.md create mode 100644 frontend/docs/src/guide/in-depth/loading.md create mode 100644 frontend/docs/src/guide/in-depth/locale.md create mode 100644 frontend/docs/src/guide/in-depth/login.md create mode 100644 frontend/docs/src/guide/in-depth/theme.md create mode 100644 frontend/docs/src/guide/in-depth/ui-framework.md create mode 100644 frontend/docs/src/guide/introduction/changelog.md create mode 100644 frontend/docs/src/guide/introduction/quick-start.md create mode 100644 frontend/docs/src/guide/introduction/roadmap.md create mode 100644 frontend/docs/src/guide/introduction/thin.md create mode 100644 frontend/docs/src/guide/introduction/vben.md create mode 100644 frontend/docs/src/guide/introduction/why.md create mode 100644 frontend/docs/src/guide/other/faq.md create mode 100644 frontend/docs/src/guide/other/project-update.md create mode 100644 frontend/docs/src/guide/other/remove-code.md create mode 100644 frontend/docs/src/guide/project/changeset.md create mode 100644 frontend/docs/src/guide/project/cli.md create mode 100644 frontend/docs/src/guide/project/dir.md create mode 100644 frontend/docs/src/guide/project/standard.md create mode 100644 frontend/docs/src/guide/project/tailwindcss.md create mode 100644 frontend/docs/src/guide/project/test.md create mode 100644 frontend/docs/src/guide/project/vite.md create mode 100644 frontend/docs/src/index.md create mode 100644 frontend/docs/src/public/favicon.ico create mode 100644 frontend/docs/src/public/guide/devtools.png create mode 100644 frontend/docs/src/public/guide/loading.png create mode 100644 frontend/docs/src/public/guide/locale.png create mode 100644 frontend/docs/src/public/guide/login-expired.png create mode 100644 frontend/docs/src/public/guide/login.png create mode 100644 frontend/docs/src/public/guide/preferences.png create mode 100644 frontend/docs/src/public/guide/qq.png create mode 100644 frontend/docs/src/public/guide/qq_channel.png create mode 100644 frontend/docs/src/public/guide/report.png create mode 100644 frontend/docs/src/public/guide/test.png create mode 100644 frontend/docs/src/public/guide/update-notice.png create mode 100644 frontend/docs/src/public/logos/nitro.svg create mode 100644 frontend/docs/src/public/logos/shadcn-ui.svg create mode 100644 frontend/docs/src/public/logos/turborepo.svg create mode 100644 frontend/docs/src/public/logos/vite.svg create mode 100644 frontend/docs/src/sponsor/personal.md create mode 100644 frontend/docs/tailwind.config.mjs create mode 100644 frontend/docs/tsconfig.json create mode 100644 frontend/eslint.config.mjs delete mode 100644 frontend/index.html create mode 100644 frontend/internal/lint-configs/commitlint-config/index.mjs create mode 100644 frontend/internal/lint-configs/commitlint-config/package.json create mode 100644 frontend/internal/lint-configs/eslint-config/build.config.ts create mode 100644 frontend/internal/lint-configs/eslint-config/package.json create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/command.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/comments.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/disableds.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/ignores.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/import.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/index.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/javascript.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/jsdoc.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/jsonc.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/node.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/perfectionist.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/prettier.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/regexp.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/test.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/turbo.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/typescript.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/unicorn.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/configs/vue.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/custom-config.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/index.ts create mode 100644 frontend/internal/lint-configs/eslint-config/src/util.ts create mode 100644 frontend/internal/lint-configs/eslint-config/tsconfig.json create mode 100644 frontend/internal/lint-configs/prettier-config/index.mjs create mode 100644 frontend/internal/lint-configs/prettier-config/package.json create mode 100644 frontend/internal/lint-configs/stylelint-config/index.mjs create mode 100644 frontend/internal/lint-configs/stylelint-config/package.json create mode 100644 frontend/internal/node-utils/build.config.ts create mode 100644 frontend/internal/node-utils/package.json create mode 100644 frontend/internal/node-utils/src/__tests__/hash.test.ts create mode 100644 frontend/internal/node-utils/src/__tests__/path.test.ts create mode 100644 frontend/internal/node-utils/src/constants.ts create mode 100644 frontend/internal/node-utils/src/date.ts create mode 100644 frontend/internal/node-utils/src/fs.ts create mode 100644 frontend/internal/node-utils/src/git.ts create mode 100644 frontend/internal/node-utils/src/hash.ts create mode 100644 frontend/internal/node-utils/src/index.ts create mode 100644 frontend/internal/node-utils/src/monorepo.ts create mode 100644 frontend/internal/node-utils/src/path.ts create mode 100644 frontend/internal/node-utils/src/prettier.ts create mode 100644 frontend/internal/node-utils/src/spinner.ts create mode 100644 frontend/internal/node-utils/tsconfig.json create mode 100644 frontend/internal/tailwind-config/build.config.ts create mode 100644 frontend/internal/tailwind-config/package.json create mode 100644 frontend/internal/tailwind-config/src/index.ts create mode 100644 frontend/internal/tailwind-config/src/module.d.ts create mode 100644 frontend/internal/tailwind-config/src/plugins/entry.ts create mode 100644 frontend/internal/tailwind-config/src/postcss.config.ts create mode 100644 frontend/internal/tailwind-config/tsconfig.json create mode 100644 frontend/internal/tsconfig/base.json create mode 100644 frontend/internal/tsconfig/library.json create mode 100644 frontend/internal/tsconfig/node.json create mode 100644 frontend/internal/tsconfig/package.json create mode 100644 frontend/internal/tsconfig/web-app.json create mode 100644 frontend/internal/tsconfig/web.json create mode 100644 frontend/internal/vite-config/build.config.ts create mode 100644 frontend/internal/vite-config/package.json create mode 100644 frontend/internal/vite-config/src/config/application.ts create mode 100644 frontend/internal/vite-config/src/config/common.ts create mode 100644 frontend/internal/vite-config/src/config/index.ts create mode 100644 frontend/internal/vite-config/src/config/library.ts create mode 100644 frontend/internal/vite-config/src/index.ts create mode 100644 frontend/internal/vite-config/src/options.ts create mode 100644 frontend/internal/vite-config/src/plugins/archiver.ts create mode 100644 frontend/internal/vite-config/src/plugins/extra-app-config.ts create mode 100644 frontend/internal/vite-config/src/plugins/importmap.ts create mode 100644 frontend/internal/vite-config/src/plugins/index.ts create mode 100644 frontend/internal/vite-config/src/plugins/inject-app-loading/README.md create mode 100644 frontend/internal/vite-config/src/plugins/inject-app-loading/default-loading-antd.html create mode 100644 frontend/internal/vite-config/src/plugins/inject-app-loading/default-loading.html create mode 100644 frontend/internal/vite-config/src/plugins/inject-app-loading/index.ts create mode 100644 frontend/internal/vite-config/src/plugins/inject-metadata.ts create mode 100644 frontend/internal/vite-config/src/plugins/license.ts create mode 100644 frontend/internal/vite-config/src/plugins/nitro-mock.ts create mode 100644 frontend/internal/vite-config/src/plugins/print.ts create mode 100644 frontend/internal/vite-config/src/plugins/vxe-table.ts create mode 100644 frontend/internal/vite-config/src/typing.ts create mode 100644 frontend/internal/vite-config/src/utils/env.ts create mode 100644 frontend/internal/vite-config/tsconfig.json create mode 100644 frontend/lefthook.yml delete mode 100644 frontend/package-lock.json create mode 100644 frontend/packages/@core/README.md create mode 100644 frontend/packages/@core/base/README.md create mode 100644 frontend/packages/@core/base/design/package.json create mode 100644 frontend/packages/@core/base/design/src/css/global.css create mode 100644 frontend/packages/@core/base/design/src/css/nprogress.css create mode 100644 frontend/packages/@core/base/design/src/css/transition.css create mode 100644 frontend/packages/@core/base/design/src/css/ui.css create mode 100644 frontend/packages/@core/base/design/src/design-tokens/dark.css create mode 100644 frontend/packages/@core/base/design/src/design-tokens/default.css create mode 100644 frontend/packages/@core/base/design/src/design-tokens/index.ts create mode 100644 frontend/packages/@core/base/design/src/index.ts create mode 100644 frontend/packages/@core/base/design/src/scss-bem/bem.scss create mode 100644 frontend/packages/@core/base/design/src/scss-bem/constants.scss create mode 100644 frontend/packages/@core/base/design/tsconfig.json create mode 100644 frontend/packages/@core/base/design/vite.config.mts create mode 100644 frontend/packages/@core/base/icons/build.config.ts create mode 100644 frontend/packages/@core/base/icons/package.json create mode 100644 frontend/packages/@core/base/icons/src/create-icon.ts create mode 100644 frontend/packages/@core/base/icons/src/index.ts create mode 100644 frontend/packages/@core/base/icons/src/lucide.ts create mode 100644 frontend/packages/@core/base/icons/tsconfig.json create mode 100644 frontend/packages/@core/base/shared/build.config.ts create mode 100644 frontend/packages/@core/base/shared/package.json create mode 100644 frontend/packages/@core/base/shared/src/cache/__tests__/storage-manager.test.ts create mode 100644 frontend/packages/@core/base/shared/src/cache/index.ts create mode 100644 frontend/packages/@core/base/shared/src/cache/storage-manager.ts create mode 100644 frontend/packages/@core/base/shared/src/cache/types.ts create mode 100644 frontend/packages/@core/base/shared/src/color/__tests__/convert.test.ts create mode 100644 frontend/packages/@core/base/shared/src/color/color.ts create mode 100644 frontend/packages/@core/base/shared/src/color/convert.ts create mode 100644 frontend/packages/@core/base/shared/src/color/generator.ts create mode 100644 frontend/packages/@core/base/shared/src/color/index.ts create mode 100644 frontend/packages/@core/base/shared/src/constants/globals.ts create mode 100644 frontend/packages/@core/base/shared/src/constants/index.ts create mode 100644 frontend/packages/@core/base/shared/src/constants/vben.ts create mode 100644 frontend/packages/@core/base/shared/src/global-state.ts create mode 100644 frontend/packages/@core/base/shared/src/store.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/diff.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/dom.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/inference.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/letter.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/resources.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/state-handler.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/tree.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/unique.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/update-css-variables.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/util.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/__tests__/window.test.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/cn.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/date.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/diff.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/dom.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/download.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/index.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/inference.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/letter.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/merge.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/nprogress.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/resources.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/state-handler.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/to.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/tree.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/unique.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/update-css-variables.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/util.ts create mode 100644 frontend/packages/@core/base/shared/src/utils/window.ts create mode 100644 frontend/packages/@core/base/shared/tsconfig.json create mode 100644 frontend/packages/@core/base/typings/build.config.ts create mode 100644 frontend/packages/@core/base/typings/package.json create mode 100644 frontend/packages/@core/base/typings/src/app.d.ts create mode 100644 frontend/packages/@core/base/typings/src/basic.d.ts create mode 100644 frontend/packages/@core/base/typings/src/helper.d.ts create mode 100644 frontend/packages/@core/base/typings/src/index.ts create mode 100644 frontend/packages/@core/base/typings/src/menu-record.ts create mode 100644 frontend/packages/@core/base/typings/src/tabs.ts create mode 100644 frontend/packages/@core/base/typings/src/vue-router.d.ts create mode 100644 frontend/packages/@core/base/typings/tsconfig.json create mode 100644 frontend/packages/@core/base/typings/vue-router.d.ts create mode 100644 frontend/packages/@core/composables/build.config.ts create mode 100644 frontend/packages/@core/composables/package.json create mode 100644 frontend/packages/@core/composables/src/__tests__/use-sortable.test.ts create mode 100644 frontend/packages/@core/composables/src/index.ts create mode 100644 frontend/packages/@core/composables/src/use-is-mobile.ts create mode 100644 frontend/packages/@core/composables/src/use-layout-style.ts create mode 100644 frontend/packages/@core/composables/src/use-namespace.ts create mode 100644 frontend/packages/@core/composables/src/use-priority-value.ts create mode 100644 frontend/packages/@core/composables/src/use-scroll-lock.ts create mode 100644 frontend/packages/@core/composables/src/use-simple-locale/README.md create mode 100644 frontend/packages/@core/composables/src/use-simple-locale/index.ts create mode 100644 frontend/packages/@core/composables/src/use-simple-locale/messages.ts create mode 100644 frontend/packages/@core/composables/src/use-sortable.ts create mode 100644 frontend/packages/@core/composables/tsconfig.json create mode 100644 frontend/packages/@core/preferences/__tests__/__snapshots__/config.test.ts.snap create mode 100644 frontend/packages/@core/preferences/__tests__/config.test.ts create mode 100644 frontend/packages/@core/preferences/__tests__/preferences.test.ts create mode 100644 frontend/packages/@core/preferences/build.config.ts create mode 100644 frontend/packages/@core/preferences/package.json create mode 100644 frontend/packages/@core/preferences/src/config.ts create mode 100644 frontend/packages/@core/preferences/src/constants.ts create mode 100644 frontend/packages/@core/preferences/src/index.ts create mode 100644 frontend/packages/@core/preferences/src/preferences.ts create mode 100644 frontend/packages/@core/preferences/src/types.ts create mode 100644 frontend/packages/@core/preferences/src/update-css-variables.ts create mode 100644 frontend/packages/@core/preferences/src/use-preferences.ts create mode 100644 frontend/packages/@core/preferences/tsconfig.json create mode 100644 frontend/packages/@core/ui-kit/README.md create mode 100644 frontend/packages/@core/ui-kit/form-ui/__tests__/form-api.test.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/build.config.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/package.json create mode 100644 frontend/packages/@core/ui-kit/form-ui/postcss.config.mjs create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/components/form-actions.vue create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/config.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-api.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-render/context.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-render/dependencies.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-render/expandable.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-render/form-field.vue create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-render/form-label.vue create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-render/form.vue create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-render/helper.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/form-render/index.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/index.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/types.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/use-form-context.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/use-vben-form.ts create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/vben-form.vue create mode 100644 frontend/packages/@core/ui-kit/form-ui/src/vben-use-form.vue create mode 100644 frontend/packages/@core/ui-kit/form-ui/tailwind.config.mjs create mode 100644 frontend/packages/@core/ui-kit/form-ui/tsconfig.json create mode 100644 frontend/packages/@core/ui-kit/layout-ui/build.config.ts create mode 100644 frontend/packages/@core/ui-kit/layout-ui/package.json create mode 100644 frontend/packages/@core/ui-kit/layout-ui/postcss.config.mjs create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/index.ts create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/layout-content.vue create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/layout-footer.vue create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/layout-header.vue create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/layout-sidebar.vue create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/layout-tabbar.vue create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/widgets/index.ts create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-collapse-button.vue create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/components/widgets/sidebar-fixed-button.vue create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/hooks/use-layout.ts create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/index.ts create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/vben-layout.ts create mode 100644 frontend/packages/@core/ui-kit/layout-ui/src/vben-layout.vue create mode 100644 frontend/packages/@core/ui-kit/layout-ui/tailwind.config.mjs create mode 100644 frontend/packages/@core/ui-kit/layout-ui/tsconfig.json create mode 100644 frontend/packages/@core/ui-kit/menu-ui/README.md create mode 100644 frontend/packages/@core/ui-kit/menu-ui/build.config.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/package.json create mode 100644 frontend/packages/@core/ui-kit/menu-ui/postcss.config.mjs create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/collapse-transition.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/index.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/menu-badge-dot.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/menu-badge.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/menu-item.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/menu.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/normal-menu/index.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/normal-menu/normal-menu.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/sub-menu-content.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/components/sub-menu.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/hooks/index.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-context.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/hooks/use-menu-scroll.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/hooks/use-menu.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/index.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/menu.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/sub-menu.vue create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/types.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/src/utils/index.ts create mode 100644 frontend/packages/@core/ui-kit/menu-ui/tailwind.config.mjs create mode 100644 frontend/packages/@core/ui-kit/menu-ui/tsconfig.json create mode 100644 frontend/packages/@core/ui-kit/popup-ui/build.config.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/package.json create mode 100644 frontend/packages/@core/ui-kit/popup-ui/postcss.config.mjs create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/alert/AlertBuilder.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/alert/alert.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/alert/alert.vue create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/alert/index.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/drawer/__tests__/drawer-api.test.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/drawer/drawer-api.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/drawer/drawer.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/drawer/index.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/drawer/use-drawer.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/index.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/modal/__tests__/modal-api.test.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/modal/index.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/modal/modal-api.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/modal/modal.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/modal/modal.vue create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/modal/use-modal-draggable.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/src/modal/use-modal.ts create mode 100644 frontend/packages/@core/ui-kit/popup-ui/tailwind.config.mjs create mode 100644 frontend/packages/@core/ui-kit/popup-ui/tsconfig.json create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/build.config.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/components.json create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/package.json create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/postcss.config.mjs create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/avatar/avatar.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/avatar/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/back-top/back-top.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/back-top/backtop.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/back-top/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/back-top/use-backtop.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-background.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb-view.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/breadcrumb.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/breadcrumb/types.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/button/button-group.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/button/button.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/button/check-button-group.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/button/icon-button.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/button/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/checkbox/checkbox.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/checkbox/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/context-menu.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/context-menu/interface.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/count-to-animator.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/count-to-animator/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/dropdown-menu.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/dropdown-radio-menu.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/dropdown-menu/interface.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/expandable-arrow/expandable-arrow.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/expandable-arrow/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/full-screen/full-screen.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/full-screen/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/hover-card/hover-card.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/hover-card/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/icon/icon.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/icon/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/input-password/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/input-password/input-password.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/input-password/password-strength.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/logo/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/logo/logo.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/types.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/popover/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/popover/popover.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/render-content/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/render-content/render-content.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/scrollbar/scrollbar.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/segmented/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/segmented/segmented.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/segmented/tabs-indicator.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/segmented/types.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/select/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/select/select.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/spine-text/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/spine-text/spine-text.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/spinner/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/spinner/loading.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/spinner/spinner.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/help-tooltip.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/components/tooltip/tooltip.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/Accordion.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/AccordionTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/accordion/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialog.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogAction.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogCancel.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogDescription.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogOverlay.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogTitle.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/Avatar.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/AvatarFallback.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/AvatarImage.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/avatar.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/avatar/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/badge/Badge.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/badge/badge.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/badge/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/Breadcrumb.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbEllipsis.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbLink.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbList.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbPage.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/BreadcrumbSeparator.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/breadcrumb/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/button/Button.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/button/button.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/button/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/button/types.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/card/Card.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardDescription.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardFooter.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardHeader.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/card/CardTitle.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/card/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/Checkbox.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/checkbox/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenu.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuCheckboxItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuLabel.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuPortal.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuRadioGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuRadioItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSeparator.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuShortcut.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSub.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSubContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuSubTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/ContextMenuTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/context-menu/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/Dialog.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogClose.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogDescription.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogFooter.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogHeader.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogOverlay.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogScrollContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogTitle.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/DialogTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dialog/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenu.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuCheckboxItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuLabel.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuRadioGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuRadioItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSeparator.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuShortcut.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSub.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSubContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuSubTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/DropdownMenuTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/dropdown-menu/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormControl.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormDescription.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormLabel.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/form/FormMessage.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/form/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/form/injectionKeys.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/form/useFormField.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCard.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCardContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/HoverCardTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/hover-card/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/input/Input.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/input/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/label/Label.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/label/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberField.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldDecrement.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldIncrement.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/NumberFieldInput.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/number-field/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationEllipsis.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationFirst.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationLast.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationNext.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrev.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pagination/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInput.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputSeparator.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/popover/Popover.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/popover/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/RadioGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/RadioGroupItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/radio-group/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizableHandle.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizablePanelGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/resizable/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/ScrollArea.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/ScrollBar.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/scroll-area/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/Select.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectItemText.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectLabel.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectScrollDownButton.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectScrollUpButton.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectSeparator.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/SelectValue.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/select/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/separator/Separator.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/separator/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/Sheet.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetClose.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetDescription.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetFooter.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetHeader.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetOverlay.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetTitle.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/SheetTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/sheet/sheet.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/switch/Switch.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/switch/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/Tabs.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsList.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/TabsTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tabs/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/Textarea.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/textarea/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroup.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/ToggleGroupItem.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/toggle-group/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/toggle/toggle.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/Tooltip.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipContent.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipProvider.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/TooltipTrigger.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tooltip/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tree/index.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/tailwind.config.mjs create mode 100644 frontend/packages/@core/ui-kit/shadcn-ui/tsconfig.json create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/build.config.ts create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/package.json create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/postcss.config.mjs create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/components/index.ts create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/components/tabs-chrome/tabs.vue create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/components/tabs/tabs.vue create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/components/widgets/index.ts create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-more.vue create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/components/widgets/tool-screen.vue create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/index.ts create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/tabs-view.vue create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/types.ts create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/use-tabs-drag.ts create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/src/use-tabs-view-scroll.ts create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/tailwind.config.mjs create mode 100644 frontend/packages/@core/ui-kit/tabs-ui/tsconfig.json create mode 100644 frontend/packages/constants/README.md create mode 100644 frontend/packages/constants/package.json create mode 100644 frontend/packages/constants/src/core.ts create mode 100644 frontend/packages/constants/src/index.ts create mode 100644 frontend/packages/constants/tsconfig.json create mode 100644 frontend/packages/effects/README.md create mode 100644 frontend/packages/effects/access/package.json create mode 100644 frontend/packages/effects/access/src/access-control.vue create mode 100644 frontend/packages/effects/access/src/accessible.ts create mode 100644 frontend/packages/effects/access/src/directive.ts create mode 100644 frontend/packages/effects/access/src/index.ts create mode 100644 frontend/packages/effects/access/src/use-access.ts create mode 100644 frontend/packages/effects/access/tsconfig.json create mode 100644 frontend/packages/effects/common-ui/package.json create mode 100644 frontend/packages/effects/common-ui/src/components/api-component/api-component.vue create mode 100644 frontend/packages/effects/common-ui/src/components/api-component/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/hooks/useCaptchaPoints.ts create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/point-selection-captcha/index.vue create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/point-selection-captcha/point-selection-captcha-card.vue create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/slider-captcha/index.vue create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-action.vue create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-bar.vue create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/slider-captcha/slider-captcha-content.vue create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/slider-rotate-captcha/index.vue create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/slider-translate-captcha/index.vue create mode 100644 frontend/packages/effects/common-ui/src/components/captcha/types.ts create mode 100644 frontend/packages/effects/common-ui/src/components/col-page/col-page.vue create mode 100644 frontend/packages/effects/common-ui/src/components/col-page/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/col-page/types.ts create mode 100644 frontend/packages/effects/common-ui/src/components/count-to/count-to.vue create mode 100644 frontend/packages/effects/common-ui/src/components/count-to/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/count-to/types.ts create mode 100644 frontend/packages/effects/common-ui/src/components/ellipsis-text/ellipsis-text.vue create mode 100644 frontend/packages/effects/common-ui/src/components/ellipsis-text/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/icon-picker/icon-picker.vue create mode 100644 frontend/packages/effects/common-ui/src/components/icon-picker/icons.ts create mode 100644 frontend/packages/effects/common-ui/src/components/icon-picker/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/json-viewer/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/json-viewer/index.vue create mode 100644 frontend/packages/effects/common-ui/src/components/json-viewer/style.scss create mode 100644 frontend/packages/effects/common-ui/src/components/json-viewer/types.ts create mode 100644 frontend/packages/effects/common-ui/src/components/loading/directive.ts create mode 100644 frontend/packages/effects/common-ui/src/components/loading/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/loading/loading.vue create mode 100644 frontend/packages/effects/common-ui/src/components/loading/spinner.vue create mode 100644 frontend/packages/effects/common-ui/src/components/page/__tests__/page.test.ts create mode 100644 frontend/packages/effects/common-ui/src/components/page/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/page/page.vue create mode 100644 frontend/packages/effects/common-ui/src/components/page/types.ts create mode 100644 frontend/packages/effects/common-ui/src/components/resize/index.ts create mode 100644 frontend/packages/effects/common-ui/src/components/resize/resize.vue create mode 100644 frontend/packages/effects/common-ui/src/components/tippy/directive.ts create mode 100644 frontend/packages/effects/common-ui/src/components/tippy/index.ts create mode 100644 frontend/packages/effects/common-ui/src/index.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/about/about.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/about/about.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/about/index.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/auth-title.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/code-login.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/dingding-login.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/forget-password.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/index.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/login-expired-modal.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/login.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/qrcode-login.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/register.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/third-party-login.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/authentication/types.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-chart-card.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-charts-tabs.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/analysis/analysis-overview.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/analysis/index.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/index.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/typing.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/workbench/index.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-header.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-project.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-quick-nav.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-todo.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/dashboard/workbench/workbench-trends.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/fallback.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/fallback.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/icons/icon-403.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/icons/icon-404.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/icons/icon-500.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/icons/icon-coming-soon.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/icons/icon-offline.vue create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/icons/warning.svg create mode 100644 frontend/packages/effects/common-ui/src/ui/fallback/index.ts create mode 100644 frontend/packages/effects/common-ui/src/ui/index.ts create mode 100644 frontend/packages/effects/common-ui/tsconfig.json create mode 100644 frontend/packages/effects/hooks/README.md create mode 100644 frontend/packages/effects/hooks/package.json create mode 100644 frontend/packages/effects/hooks/src/index.ts create mode 100644 frontend/packages/effects/hooks/src/use-app-config.ts create mode 100644 frontend/packages/effects/hooks/src/use-content-maximize.ts create mode 100644 frontend/packages/effects/hooks/src/use-design-tokens.ts create mode 100644 frontend/packages/effects/hooks/src/use-hover-toggle.ts create mode 100644 frontend/packages/effects/hooks/src/use-pagination.ts create mode 100644 frontend/packages/effects/hooks/src/use-refresh.ts create mode 100644 frontend/packages/effects/hooks/src/use-tabs.ts create mode 100644 frontend/packages/effects/hooks/src/use-watermark.ts create mode 100644 frontend/packages/effects/hooks/tsconfig.json create mode 100644 frontend/packages/effects/layouts/package.json create mode 100644 frontend/packages/effects/layouts/src/authentication/authentication.vue create mode 100644 frontend/packages/effects/layouts/src/authentication/form.vue create mode 100644 frontend/packages/effects/layouts/src/authentication/icons/slogan.vue create mode 100644 frontend/packages/effects/layouts/src/authentication/index.ts create mode 100644 frontend/packages/effects/layouts/src/authentication/toolbar.vue create mode 100644 frontend/packages/effects/layouts/src/authentication/types.ts create mode 100644 frontend/packages/effects/layouts/src/basic/README.md create mode 100644 frontend/packages/effects/layouts/src/basic/content/content-spinner.vue create mode 100644 frontend/packages/effects/layouts/src/basic/content/content.vue create mode 100644 frontend/packages/effects/layouts/src/basic/content/index.ts create mode 100644 frontend/packages/effects/layouts/src/basic/content/use-content-spinner.ts create mode 100644 frontend/packages/effects/layouts/src/basic/copyright/copyright.vue create mode 100644 frontend/packages/effects/layouts/src/basic/copyright/index.ts create mode 100644 frontend/packages/effects/layouts/src/basic/footer/footer.vue create mode 100644 frontend/packages/effects/layouts/src/basic/footer/index.ts create mode 100644 frontend/packages/effects/layouts/src/basic/header/header.vue create mode 100644 frontend/packages/effects/layouts/src/basic/header/index.ts create mode 100644 frontend/packages/effects/layouts/src/basic/index.ts create mode 100644 frontend/packages/effects/layouts/src/basic/layout.vue create mode 100644 frontend/packages/effects/layouts/src/basic/menu/extra-menu.vue create mode 100644 frontend/packages/effects/layouts/src/basic/menu/index.ts create mode 100644 frontend/packages/effects/layouts/src/basic/menu/menu.vue create mode 100644 frontend/packages/effects/layouts/src/basic/menu/mixed-menu.vue create mode 100644 frontend/packages/effects/layouts/src/basic/menu/use-extra-menu.ts create mode 100644 frontend/packages/effects/layouts/src/basic/menu/use-mixed-menu.ts create mode 100644 frontend/packages/effects/layouts/src/basic/menu/use-navigation.ts create mode 100644 frontend/packages/effects/layouts/src/basic/tabbar/index.ts create mode 100644 frontend/packages/effects/layouts/src/basic/tabbar/tabbar.vue create mode 100644 frontend/packages/effects/layouts/src/basic/tabbar/use-tabbar.ts create mode 100644 frontend/packages/effects/layouts/src/iframe/iframe-router-view.vue create mode 100644 frontend/packages/effects/layouts/src/iframe/iframe-view.vue create mode 100644 frontend/packages/effects/layouts/src/iframe/index.ts create mode 100644 frontend/packages/effects/layouts/src/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/breadcrumb.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/check-updates/check-updates.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/check-updates/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/color-toggle.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/global-search/global-search.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/global-search/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/global-search/search-panel.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/language-toggle.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/layout-toggle.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/lock-screen/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/lock-screen/lock-screen-modal.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/lock-screen/lock-screen.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/notification/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/notification/notification.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/notification/types.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/block.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/checkbox-item.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/general/animation.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/general/general.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/input-item.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/breadcrumb.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/content.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/copyright.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/footer.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/header.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/layout.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/navigation.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/sidebar.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/tabbar.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/layout/widget.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/number-field-item.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/select-item.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/shortcut-keys/global.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/switch-item.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/theme/builtin.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/theme/color-mode.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/theme/radius.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/theme/theme.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/blocks/toggle-item.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/content-compact.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/full-content.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/header-mixed-nav.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/header-nav.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/header-sidebar-nav.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/mixed-nav.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/setting.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/sidebar-mixed-nav.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/icons/sidebar-nav.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/preferences-button.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/preferences-drawer.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/preferences.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/preferences/use-open-preferences.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/theme-toggle/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/theme-toggle/theme-button.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/theme-toggle/theme-toggle.vue create mode 100644 frontend/packages/effects/layouts/src/widgets/user-dropdown/index.ts create mode 100644 frontend/packages/effects/layouts/src/widgets/user-dropdown/user-dropdown.vue create mode 100644 frontend/packages/effects/layouts/tsconfig.json create mode 100644 frontend/packages/effects/plugins/README.md create mode 100644 frontend/packages/effects/plugins/package.json create mode 100644 frontend/packages/effects/plugins/src/echarts/echarts-ui.vue create mode 100644 frontend/packages/effects/plugins/src/echarts/echarts.ts create mode 100644 frontend/packages/effects/plugins/src/echarts/index.ts create mode 100644 frontend/packages/effects/plugins/src/echarts/use-echarts.ts create mode 100644 frontend/packages/effects/plugins/src/motion/index.ts create mode 100644 frontend/packages/effects/plugins/src/motion/types.ts create mode 100644 frontend/packages/effects/plugins/src/vxe-table/api.ts create mode 100644 frontend/packages/effects/plugins/src/vxe-table/extends.ts create mode 100644 frontend/packages/effects/plugins/src/vxe-table/index.ts create mode 100644 frontend/packages/effects/plugins/src/vxe-table/init.ts create mode 100644 frontend/packages/effects/plugins/src/vxe-table/style.css create mode 100644 frontend/packages/effects/plugins/src/vxe-table/types.ts create mode 100644 frontend/packages/effects/plugins/src/vxe-table/use-vxe-grid.ts create mode 100644 frontend/packages/effects/plugins/src/vxe-table/use-vxe-grid.vue create mode 100644 frontend/packages/effects/plugins/tsconfig.json create mode 100644 frontend/packages/effects/request/package.json create mode 100644 frontend/packages/effects/request/src/index.ts create mode 100644 frontend/packages/effects/request/src/request-client/index.ts create mode 100644 frontend/packages/effects/request/src/request-client/modules/downloader.test.ts create mode 100644 frontend/packages/effects/request/src/request-client/modules/downloader.ts create mode 100644 frontend/packages/effects/request/src/request-client/modules/interceptor.ts create mode 100644 frontend/packages/effects/request/src/request-client/modules/uploader.test.ts create mode 100644 frontend/packages/effects/request/src/request-client/modules/uploader.ts create mode 100644 frontend/packages/effects/request/src/request-client/preset-interceptors.ts create mode 100644 frontend/packages/effects/request/src/request-client/request-client.test.ts create mode 100644 frontend/packages/effects/request/src/request-client/request-client.ts create mode 100644 frontend/packages/effects/request/src/request-client/types.ts create mode 100644 frontend/packages/effects/request/tsconfig.json create mode 100644 frontend/packages/icons/README.md create mode 100644 frontend/packages/icons/package.json create mode 100644 frontend/packages/icons/src/iconify/index.ts create mode 100644 frontend/packages/icons/src/icons/empty-icon.vue create mode 100644 frontend/packages/icons/src/index.ts create mode 100644 frontend/packages/icons/src/svg/icons/antdv-logo.svg create mode 100644 frontend/packages/icons/src/svg/icons/avatar-1.svg create mode 100644 frontend/packages/icons/src/svg/icons/avatar-2.svg create mode 100644 frontend/packages/icons/src/svg/icons/avatar-3.svg create mode 100644 frontend/packages/icons/src/svg/icons/avatar-4.svg create mode 100644 frontend/packages/icons/src/svg/icons/bell.svg create mode 100644 frontend/packages/icons/src/svg/icons/cake.svg create mode 100644 frontend/packages/icons/src/svg/icons/card.svg create mode 100644 frontend/packages/icons/src/svg/icons/download.svg create mode 100644 frontend/packages/icons/src/svg/index.ts create mode 100644 frontend/packages/icons/src/svg/load.ts create mode 100644 frontend/packages/icons/tsconfig.json create mode 100644 frontend/packages/locales/package.json create mode 100644 frontend/packages/locales/src/i18n.ts create mode 100644 frontend/packages/locales/src/index.ts create mode 100644 frontend/packages/locales/src/langs/en-US/authentication.json create mode 100644 frontend/packages/locales/src/langs/en-US/common.json create mode 100644 frontend/packages/locales/src/langs/en-US/preferences.json create mode 100644 frontend/packages/locales/src/langs/en-US/ui.json create mode 100644 frontend/packages/locales/src/langs/zh-CN/authentication.json create mode 100644 frontend/packages/locales/src/langs/zh-CN/common.json create mode 100644 frontend/packages/locales/src/langs/zh-CN/preferences.json create mode 100644 frontend/packages/locales/src/langs/zh-CN/ui.json create mode 100644 frontend/packages/locales/src/typing.ts create mode 100644 frontend/packages/locales/tsconfig.json create mode 100644 frontend/packages/preferences/package.json create mode 100644 frontend/packages/preferences/src/index.ts create mode 100644 frontend/packages/preferences/tsconfig.json create mode 100644 frontend/packages/stores/package.json create mode 100644 frontend/packages/stores/shim-pinia.d.ts create mode 100644 frontend/packages/stores/src/index.ts create mode 100644 frontend/packages/stores/src/modules/access.test.ts create mode 100644 frontend/packages/stores/src/modules/access.ts create mode 100644 frontend/packages/stores/src/modules/index.ts create mode 100644 frontend/packages/stores/src/modules/tabbar.test.ts create mode 100644 frontend/packages/stores/src/modules/tabbar.ts create mode 100644 frontend/packages/stores/src/modules/user.test.ts create mode 100644 frontend/packages/stores/src/modules/user.ts create mode 100644 frontend/packages/stores/src/setup.ts create mode 100644 frontend/packages/stores/tsconfig.json create mode 100644 frontend/packages/styles/README.md create mode 100644 frontend/packages/styles/package.json create mode 100644 frontend/packages/styles/src/antd/index.css create mode 100644 frontend/packages/styles/src/ele/index.css create mode 100644 frontend/packages/styles/src/global/index.scss create mode 100644 frontend/packages/styles/src/index.ts create mode 100644 frontend/packages/styles/src/naive/index.css create mode 100644 frontend/packages/styles/tsconfig.json create mode 100644 frontend/packages/types/README.md create mode 100644 frontend/packages/types/global.d.ts create mode 100644 frontend/packages/types/package.json create mode 100644 frontend/packages/types/src/index.ts create mode 100644 frontend/packages/types/src/user.ts create mode 100644 frontend/packages/types/tsconfig.json create mode 100644 frontend/packages/utils/README.md create mode 100644 frontend/packages/utils/package.json create mode 100644 frontend/packages/utils/src/helpers/__tests__/find-menu-by-path.test.ts create mode 100644 frontend/packages/utils/src/helpers/__tests__/generate-menus.test.ts create mode 100644 frontend/packages/utils/src/helpers/__tests__/generate-routes-frontend.test.ts create mode 100644 frontend/packages/utils/src/helpers/__tests__/merge-route-modules.test.ts create mode 100644 frontend/packages/utils/src/helpers/find-menu-by-path.ts create mode 100644 frontend/packages/utils/src/helpers/generate-menus.ts create mode 100644 frontend/packages/utils/src/helpers/generate-routes-backend.ts create mode 100644 frontend/packages/utils/src/helpers/generate-routes-frontend.ts create mode 100644 frontend/packages/utils/src/helpers/get-popup-container.ts create mode 100644 frontend/packages/utils/src/helpers/index.ts create mode 100644 frontend/packages/utils/src/helpers/merge-route-modules.ts create mode 100644 frontend/packages/utils/src/helpers/reset-routes.ts create mode 100644 frontend/packages/utils/src/helpers/unmount-global-loading.ts create mode 100644 frontend/packages/utils/src/index.ts create mode 100644 frontend/packages/utils/tsconfig.json create mode 100644 frontend/playground/.env create mode 100644 frontend/playground/.env.analyze create mode 100644 frontend/playground/.env.development create mode 100644 frontend/playground/.env.production create mode 100644 frontend/playground/__tests__/e2e/auth-login.spec.ts create mode 100644 frontend/playground/__tests__/e2e/common/auth.ts create mode 100644 frontend/playground/index.html create mode 100644 frontend/playground/package.json create mode 100644 frontend/playground/playwright.config.ts create mode 100644 frontend/playground/postcss.config.mjs create mode 100644 frontend/playground/public/favicon.ico create mode 100644 frontend/playground/src/adapter/component/index.ts create mode 100644 frontend/playground/src/adapter/form.ts create mode 100644 frontend/playground/src/adapter/vxe-table.ts create mode 100644 frontend/playground/src/api/core/auth.ts create mode 100644 frontend/playground/src/api/core/index.ts create mode 100644 frontend/playground/src/api/core/menu.ts create mode 100644 frontend/playground/src/api/core/user.ts create mode 100644 frontend/playground/src/api/examples/download.ts create mode 100644 frontend/playground/src/api/examples/index.ts create mode 100644 frontend/playground/src/api/examples/json-bigint.ts create mode 100644 frontend/playground/src/api/examples/params.ts create mode 100644 frontend/playground/src/api/examples/status.ts create mode 100644 frontend/playground/src/api/examples/table.ts create mode 100644 frontend/playground/src/api/examples/upload.ts create mode 100644 frontend/playground/src/api/index.ts create mode 100644 frontend/playground/src/api/request.ts create mode 100644 frontend/playground/src/api/system/dept.ts create mode 100644 frontend/playground/src/api/system/index.ts create mode 100644 frontend/playground/src/api/system/menu.ts create mode 100644 frontend/playground/src/api/system/role.ts create mode 100644 frontend/playground/src/app.vue create mode 100644 frontend/playground/src/bootstrap.ts create mode 100644 frontend/playground/src/layouts/auth.vue create mode 100644 frontend/playground/src/layouts/basic.vue create mode 100644 frontend/playground/src/layouts/index.ts create mode 100644 frontend/playground/src/locales/README.md create mode 100644 frontend/playground/src/locales/index.ts create mode 100644 frontend/playground/src/locales/langs/en-US/demos.json create mode 100644 frontend/playground/src/locales/langs/en-US/examples.json create mode 100644 frontend/playground/src/locales/langs/en-US/page.json create mode 100644 frontend/playground/src/locales/langs/en-US/system.json create mode 100644 frontend/playground/src/locales/langs/zh-CN/demos.json create mode 100644 frontend/playground/src/locales/langs/zh-CN/examples.json create mode 100644 frontend/playground/src/locales/langs/zh-CN/page.json create mode 100644 frontend/playground/src/locales/langs/zh-CN/system.json create mode 100644 frontend/playground/src/main.ts create mode 100644 frontend/playground/src/preferences.ts create mode 100644 frontend/playground/src/router/access.ts create mode 100644 frontend/playground/src/router/guard.ts create mode 100644 frontend/playground/src/router/index.ts create mode 100644 frontend/playground/src/router/routes/core.ts create mode 100644 frontend/playground/src/router/routes/index.ts create mode 100644 frontend/playground/src/router/routes/modules/dashboard.ts create mode 100644 frontend/playground/src/router/routes/modules/demos.ts create mode 100644 frontend/playground/src/router/routes/modules/examples.ts create mode 100644 frontend/playground/src/router/routes/modules/system.ts create mode 100644 frontend/playground/src/router/routes/modules/vben.ts create mode 100644 frontend/playground/src/store/auth.ts create mode 100644 frontend/playground/src/store/index.ts create mode 100644 frontend/playground/src/views/_core/README.md create mode 100644 frontend/playground/src/views/_core/about/index.vue create mode 100644 frontend/playground/src/views/_core/authentication/code-login.vue create mode 100644 frontend/playground/src/views/_core/authentication/forget-password.vue create mode 100644 frontend/playground/src/views/_core/authentication/login.vue create mode 100644 frontend/playground/src/views/_core/authentication/qrcode-login.vue create mode 100644 frontend/playground/src/views/_core/authentication/register.vue create mode 100644 frontend/playground/src/views/_core/fallback/coming-soon.vue create mode 100644 frontend/playground/src/views/_core/fallback/forbidden.vue create mode 100644 frontend/playground/src/views/_core/fallback/internal-error.vue create mode 100644 frontend/playground/src/views/_core/fallback/not-found.vue create mode 100644 frontend/playground/src/views/_core/fallback/offline.vue create mode 100644 frontend/playground/src/views/dashboard/analytics/analytics-trends.vue create mode 100644 frontend/playground/src/views/dashboard/analytics/analytics-visits-data.vue create mode 100644 frontend/playground/src/views/dashboard/analytics/analytics-visits-sales.vue create mode 100644 frontend/playground/src/views/dashboard/analytics/analytics-visits-source.vue create mode 100644 frontend/playground/src/views/dashboard/analytics/analytics-visits.vue create mode 100644 frontend/playground/src/views/dashboard/analytics/index.vue create mode 100644 frontend/playground/src/views/dashboard/workspace/index.vue create mode 100644 frontend/playground/src/views/demos/access/admin-visible.vue create mode 100644 frontend/playground/src/views/demos/access/button-control.vue create mode 100644 frontend/playground/src/views/demos/access/index.vue create mode 100644 frontend/playground/src/views/demos/access/menu-visible-403.vue create mode 100644 frontend/playground/src/views/demos/access/super-visible.vue create mode 100644 frontend/playground/src/views/demos/access/user-visible.vue create mode 100644 frontend/playground/src/views/demos/active-icon/index.vue create mode 100644 frontend/playground/src/views/demos/badge/index.vue create mode 100644 frontend/playground/src/views/demos/breadcrumb/lateral-detail.vue create mode 100644 frontend/playground/src/views/demos/breadcrumb/lateral.vue create mode 100644 frontend/playground/src/views/demos/breadcrumb/level-detail.vue create mode 100644 frontend/playground/src/views/demos/features/clipboard/index.vue create mode 100644 frontend/playground/src/views/demos/features/file-download/base64.ts create mode 100644 frontend/playground/src/views/demos/features/file-download/index.vue create mode 100644 frontend/playground/src/views/demos/features/full-screen/index.vue create mode 100644 frontend/playground/src/views/demos/features/hide-menu-children/children.vue create mode 100644 frontend/playground/src/views/demos/features/hide-menu-children/parent.vue create mode 100644 frontend/playground/src/views/demos/features/icons/index.vue create mode 100644 frontend/playground/src/views/demos/features/json-bigint/index.vue create mode 100644 frontend/playground/src/views/demos/features/login-expired/index.vue create mode 100644 frontend/playground/src/views/demos/features/menu-query/index.vue create mode 100644 frontend/playground/src/views/demos/features/new-window/index.vue create mode 100644 frontend/playground/src/views/demos/features/request-params-serializer/index.vue create mode 100644 frontend/playground/src/views/demos/features/tabs/index.vue create mode 100644 frontend/playground/src/views/demos/features/tabs/tab-detail.vue create mode 100644 frontend/playground/src/views/demos/features/vue-query/concurrency-caching.vue create mode 100644 frontend/playground/src/views/demos/features/vue-query/index.vue create mode 100644 frontend/playground/src/views/demos/features/vue-query/infinite-queries.vue create mode 100644 frontend/playground/src/views/demos/features/vue-query/paginated-queries.vue create mode 100644 frontend/playground/src/views/demos/features/vue-query/query-retries.vue create mode 100644 frontend/playground/src/views/demos/features/vue-query/typing.ts create mode 100644 frontend/playground/src/views/demos/features/watermark/index.vue create mode 100644 frontend/playground/src/views/demos/nested/menu-1.vue create mode 100644 frontend/playground/src/views/demos/nested/menu-2-1.vue create mode 100644 frontend/playground/src/views/demos/nested/menu-3-1.vue create mode 100644 frontend/playground/src/views/demos/nested/menu-3-2-1.vue create mode 100644 frontend/playground/src/views/examples/button-group/index.vue create mode 100644 frontend/playground/src/views/examples/captcha/point-selection-captcha.vue create mode 100644 frontend/playground/src/views/examples/captcha/slider-captcha.vue create mode 100644 frontend/playground/src/views/examples/captcha/slider-rotate-captcha.vue create mode 100644 frontend/playground/src/views/examples/captcha/slider-translate-captcha.vue create mode 100644 frontend/playground/src/views/examples/count-to/index.vue create mode 100644 frontend/playground/src/views/examples/doc-button.vue create mode 100644 frontend/playground/src/views/examples/drawer/auto-height-demo.vue create mode 100644 frontend/playground/src/views/examples/drawer/base-demo.vue create mode 100644 frontend/playground/src/views/examples/drawer/dynamic-demo.vue create mode 100644 frontend/playground/src/views/examples/drawer/form-drawer-demo.vue create mode 100644 frontend/playground/src/views/examples/drawer/in-content-demo.vue create mode 100644 frontend/playground/src/views/examples/drawer/index.vue create mode 100644 frontend/playground/src/views/examples/drawer/shared-data-demo.vue create mode 100644 frontend/playground/src/views/examples/ellipsis/index.vue create mode 100644 frontend/playground/src/views/examples/form/api.vue create mode 100644 frontend/playground/src/views/examples/form/basic.vue create mode 100644 frontend/playground/src/views/examples/form/custom-layout.vue create mode 100644 frontend/playground/src/views/examples/form/custom.vue create mode 100644 frontend/playground/src/views/examples/form/dynamic.vue create mode 100644 frontend/playground/src/views/examples/form/merge.vue create mode 100644 frontend/playground/src/views/examples/form/modules/two-fields.vue create mode 100644 frontend/playground/src/views/examples/form/query.vue create mode 100644 frontend/playground/src/views/examples/form/rules.vue create mode 100644 frontend/playground/src/views/examples/form/scroll-to-error-test.vue create mode 100644 frontend/playground/src/views/examples/json-viewer/data.ts create mode 100644 frontend/playground/src/views/examples/json-viewer/index.vue create mode 100644 frontend/playground/src/views/examples/layout/col-page.vue create mode 100644 frontend/playground/src/views/examples/loading/index.vue create mode 100644 frontend/playground/src/views/examples/modal/auto-height-demo.vue create mode 100644 frontend/playground/src/views/examples/modal/base-demo.vue create mode 100644 frontend/playground/src/views/examples/modal/blur-demo.vue create mode 100644 frontend/playground/src/views/examples/modal/drag-demo.vue create mode 100644 frontend/playground/src/views/examples/modal/dynamic-demo.vue create mode 100644 frontend/playground/src/views/examples/modal/form-modal-demo.vue create mode 100644 frontend/playground/src/views/examples/modal/in-content-demo.vue create mode 100644 frontend/playground/src/views/examples/modal/index.vue create mode 100644 frontend/playground/src/views/examples/modal/nested-demo.vue create mode 100644 frontend/playground/src/views/examples/modal/shared-data-demo.vue create mode 100644 frontend/playground/src/views/examples/motion/index.vue create mode 100644 frontend/playground/src/views/examples/resize/basic.vue create mode 100644 frontend/playground/src/views/examples/tippy/index.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/basic.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/custom-cell.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/edit-cell.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/edit-row.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/fixed.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/form.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/remote.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/table-data.ts create mode 100644 frontend/playground/src/views/examples/vxe-table/tree.vue create mode 100644 frontend/playground/src/views/examples/vxe-table/virtual.vue create mode 100644 frontend/playground/src/views/system/dept/data.ts create mode 100644 frontend/playground/src/views/system/dept/list.vue create mode 100644 frontend/playground/src/views/system/dept/modules/form.vue create mode 100644 frontend/playground/src/views/system/menu/data.ts create mode 100644 frontend/playground/src/views/system/menu/list.vue create mode 100644 frontend/playground/src/views/system/menu/modules/form.vue create mode 100644 frontend/playground/src/views/system/role/data.ts create mode 100644 frontend/playground/src/views/system/role/list.vue create mode 100644 frontend/playground/src/views/system/role/modules/form.vue create mode 100644 frontend/playground/tailwind.config.mjs create mode 100644 frontend/playground/tsconfig.json create mode 100644 frontend/playground/tsconfig.node.json create mode 100644 frontend/playground/vite.config.mts create mode 100644 frontend/pnpm-lock.yaml create mode 100644 frontend/pnpm-workspace.yaml create mode 100644 frontend/scripts/clean.mjs create mode 100644 frontend/scripts/deploy/Dockerfile create mode 100644 frontend/scripts/deploy/build-local-docker-image.sh create mode 100644 frontend/scripts/deploy/nginx.conf create mode 100644 frontend/scripts/turbo-run/README.md create mode 100644 frontend/scripts/turbo-run/bin/turbo-run.mjs create mode 100644 frontend/scripts/turbo-run/build.config.ts create mode 100644 frontend/scripts/turbo-run/package.json create mode 100644 frontend/scripts/turbo-run/src/index.ts create mode 100644 frontend/scripts/turbo-run/src/run.ts create mode 100644 frontend/scripts/turbo-run/tsconfig.json create mode 100644 frontend/scripts/vsh/README.md create mode 100644 frontend/scripts/vsh/bin/vsh.mjs create mode 100644 frontend/scripts/vsh/build.config.ts create mode 100644 frontend/scripts/vsh/package.json create mode 100644 frontend/scripts/vsh/src/check-circular/index.ts create mode 100644 frontend/scripts/vsh/src/check-dep/index.ts create mode 100644 frontend/scripts/vsh/src/code-workspace/index.ts create mode 100644 frontend/scripts/vsh/src/index.ts create mode 100644 frontend/scripts/vsh/src/lint/index.ts create mode 100644 frontend/scripts/vsh/src/publint/index.ts create mode 100644 frontend/scripts/vsh/tsconfig.json delete mode 100644 frontend/src/App.tsx delete mode 100644 frontend/src/api/http.ts delete mode 100644 frontend/src/api/nodeType.ts delete mode 100644 frontend/src/api/task.ts delete mode 100644 frontend/src/api/workflow.ts delete mode 100644 frontend/src/components/common/CodeEditor.tsx delete mode 100644 frontend/src/components/common/Empty.tsx delete mode 100644 frontend/src/components/common/Error.tsx delete mode 100644 frontend/src/components/common/ExpressionInput.tsx delete mode 100644 frontend/src/components/common/FieldMappingSelector.tsx delete mode 100644 frontend/src/components/common/JsonView.tsx delete mode 100644 frontend/src/components/common/KeyValueEditor.tsx delete mode 100644 frontend/src/components/common/Loading.tsx delete mode 100644 frontend/src/components/editor/Canvas.tsx delete mode 100644 frontend/src/components/editor/ConditionNode.tsx delete mode 100644 frontend/src/components/editor/CustomNode.tsx delete mode 100644 frontend/src/components/editor/EdgePropertyPanel.tsx delete mode 100644 frontend/src/components/editor/NodePalette.tsx delete mode 100644 frontend/src/components/editor/NodePropertyPanel.tsx delete mode 100644 frontend/src/components/layout/AppLayout.tsx delete mode 100644 frontend/src/index.css delete mode 100644 frontend/src/main.tsx delete mode 100644 frontend/src/pages/ApprovalCenterPage.tsx delete mode 100644 frontend/src/pages/ExecutionHistoryPage.tsx delete mode 100644 frontend/src/pages/WorkflowEditorPage.tsx delete mode 100644 frontend/src/pages/WorkflowListPage.tsx delete mode 100644 frontend/src/store/editorStore.ts delete mode 100644 frontend/src/store/nodeTypeStore.ts delete mode 100644 frontend/src/store/workflowStore.ts delete mode 100644 frontend/src/types/node.ts delete mode 100644 frontend/src/types/workflow.ts delete mode 100644 frontend/src/utils/download.ts delete mode 100644 frontend/src/utils/schema.ts delete mode 100644 frontend/src/utils/storage.ts delete mode 100644 frontend/src/utils/validate.ts delete mode 100644 frontend/src/vite-env.d.ts create mode 100644 frontend/stylelint.config.mjs create mode 100644 frontend/tea.yaml delete mode 100644 frontend/tsconfig.json delete mode 100644 frontend/tsconfig.tsbuildinfo create mode 100644 frontend/turbo.json create mode 100644 frontend/vben-admin.code-workspace delete mode 100644 frontend/vite.config.ts create mode 100644 frontend/vitest.config.ts create mode 100644 frontend/vitest.workspace.ts diff --git a/.gitignore b/.gitignore index b5eb04b..8d3e9f3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /backend/target/ /frontend/node_modules/ +/.idea/ +/backend/.idea/ diff --git a/backend/docs/01-架构总览.md b/backend/docs/01-架构总览.md new file mode 100644 index 0000000..1e3b987 --- /dev/null +++ b/backend/docs/01-架构总览.md @@ -0,0 +1,766 @@ +# 可视化工作流平台 - 架构总览 + +**版本**: v1.0 +**日期**: 2025-01-12 +**审核角度**: 产品经理 + 架构师 + +--- + +## 一、系统定位 + +### 1.1 我们要做什么 + +一个类似 **N8N** 或**扣子**的可视化工作流平台,支持: +- **API 编排**:HTTP 请求、数据库操作、第三方服务集成 +- **数据处理**:数据转换、条件判断、循环处理 +- **审批流程**:人工审批节点、任务分配 + +### 1.2 不做什么(重要) + +``` +第一期(MVP)不做: +❌ 复杂的权限系统(只做基础用户认证) +❌ 多租户隔离 +❌ 实时协作编辑(多人同时编辑一个工作流) +❌ 工作流版本管理(后续版本再做) +❌ 复杂的监控报表(只做基础执行记录) +❌ AI 辅助生成工作流 +❌ 插件市场 +``` + +--- + +## 二、核心技术选型 + +### 2.1 技术栈 + +```yaml +后端: + 核心框架: Spring Boot 3.2 + 工作流引擎: Flowable 7.0.1 + 数据库: MySQL 8.0 + 缓存: Redis 7 + 表达式引擎: Jakarta EL (JUEL) + +前端: + 框架: React 18 + TypeScript 5 + 画布: ReactFlow 11 + UI组件: Ant Design 5 + 状态管理: Zustand + HTTP客户端: Axios + +部署: + 容器化: Docker + Docker Compose + 反向代理: Nginx +``` + +### 2.2 为什么选择 Flowable? + +**✅ 优势**(实际验证过的): +1. **开源版功能完整**:不需要购买企业版就能用 +2. **内置审批能力**:User Task 开箱即用 +3. **表单引擎**:可以快速实现动态表单 +4. **Spring Boot 集成好**:一个依赖就能启动 +5. **中文资料多**:国内使用广泛,遇到问题容易找到答案 + +**⚠️ 劣势**(需要规避的): +1. **BPMN 太重**:我们要隐藏 BPMN 细节,用户不需要懂 +2. **Modeler 难定制**:官方 Modeler 是 Angular 的,我们要自研前端 +3. **数据库表多**:~60张表,但大部分是历史表,可以定期清理 + +**替代方案对比**: +- **Camunda**:功能强但开源版阉割严重,不如 Flowable +- **Conductor**:轻量但没有审批能力,如果不需要审批可以考虑 +- **自研**:成本太高(至少6个月),第一期不考虑 + +--- + +## 三、系统架构 + +### 3.1 整体架构图 + +``` +┌──────────────────────────────────────────────────────────────┐ +│ 用户浏览器 │ +│ │ +│ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐ │ +│ │ 工作流编辑器 │ │ 节点配置面板 │ │ 审批中心 │ │ +│ │ (ReactFlow) │ │ (动态表单) │ │ (任务列表) │ │ +│ └─────────────────┘ └─────────────────┘ └──────────────┘ │ +└────────────────────────┬─────────────────────────────────────┘ + │ HTTPS (REST API) + ↓ +┌──────────────────────────────────────────────────────────────┐ +│ Nginx (反向代理) │ +└────────────────────────┬─────────────────────────────────────┘ + │ + ↓ +┌──────────────────────────────────────────────────────────────┐ +│ Spring Boot 应用 │ +│ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ REST API 层 │ │ +│ │ - /api/workflows (工作流管理) │ │ +│ │ - /api/nodes (节点类型注册) │ │ +│ │ - /api/executions (执行管理) │ │ +│ │ - /api/tasks (审批任务) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ↓ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ 业务逻辑层 │ │ +│ │ - WorkflowService (工作流转换和部署) │ │ +│ │ - NodeTypeRegistry (节点类型管理) │ │ +│ │ - ExpressionEngine (表达式解析) │ │ +│ │ - NodeExecutor (节点执行) │ │ +│ └────────────────────────────────────────────────────────┘ │ +│ ↓ │ +│ ┌────────────────────────────────────────────────────────┐ │ +│ │ Flowable Engine │ │ +│ │ - RuntimeService (流程实例管理) │ │ +│ │ - TaskService (任务管理) │ │ +│ │ - RepositoryService (流程定义管理) │ │ +│ │ - HistoryService (历史记录) │ │ +│ └────────────────────────────────────────────────────────┘ │ +└────────────────────────┬─────────────────────────────────────┘ + │ + ↓ +┌──────────────────────────────────────────────────────────────┐ +│ MySQL │ +│ │ +│ Flowable 表 (~60张): │ +│ - ACT_RE_* (流程定义) │ +│ - ACT_RU_* (运行时数据) │ +│ - ACT_HI_* (历史数据) │ +│ │ +│ 业务表: │ +│ - workflow_definitions (工作流定义 - JSON 格式) │ +│ - node_types (节点类型元数据) │ +│ - workflow_executions (执行记录扩展) │ +└──────────────────────────────────────────────────────────────┘ +``` + +### 3.2 核心数据流 + +**场景1:创建和保存工作流** + +``` +1. 用户在前端拖拽节点、配置参数 (ReactFlow) + ↓ +2. 前端生成 JSON 工作流定义 + { + "nodes": [...], + "edges": [...], + "variables": {...} + } + ↓ +3. POST /api/workflows + ↓ +4. 后端保存到 workflow_definitions 表 + ↓ +5. 转换为 BPMN XML (Flowable 格式) + ↓ +6. 部署到 Flowable (RepositoryService) + ↓ +7. 返回 processDefinitionId +``` + +**场景2:执行工作流** + +``` +1. POST /api/workflows/{id}/execute + ↓ +2. 初始化执行上下文: + { + "workflow": { "input": {...} }, + "nodes": {}, + "env": {...} + } + ↓ +3. Flowable 启动流程实例 (RuntimeService) + ↓ +4. 按拓扑顺序执行节点(Service Task) + ↓ +5. 每个节点执行: + a. 解析表达式 (ExpressionEngine) + b. 调用节点实现类 (HttpRequestNode, DatabaseNode...) + c. 保存输出到 execution.variables["nodes"][nodeId] + ↓ +6. 节点间数据通过表达式传递: + ${nodes.node1.output.body.email} + ↓ +7. 遇到 User Task(审批节点)时暂停 + ↓ +8. 审批完成后继续执行 + ↓ +9. 流程结束,保存历史记录 +``` + +--- + +## 四、关键技术难点与解决方案 + +### 4.1 难点1:前端如何知道上游节点的输出结构? + +**问题**: +用户在配置节点2时,如何知道节点1输出了哪些字段?比如 HTTP 节点返回了什么数据? + +**❌ 错误方案**: +``` +方案A: 动态执行节点1来获取输出 +→ 不可行!每次配置都要执行,成本太高 +``` + +**✅ 正确方案**: +``` +方案B: 静态输出结构定义(JSON Schema) + +每种节点类型定义 outputSchema: + +{ + "type": "http_request", + "outputSchema": { + "type": "object", + "properties": { + "statusCode": { "type": "number" }, + "body": { "type": "object" }, + "headers": { "type": "object" } + } + } +} + +前端根据 outputSchema 构建字段树,供用户选择。 + +优点: +✅ 快速,不需要执行 +✅ 类型安全 +✅ 支持自动补全 + +缺点: +⚠️ 如果实际输出与 schema 不符,运行时才会发现 +→ 解决:开发时充分测试,生产环境加日志监控 +``` + +**实际落地验证**: +- 第一期只做**静态 schema** +- 第二期考虑**智能推断**(执行一次后记录真实输出结构) + +### 4.2 难点2:表达式解析性能问题 + +**问题**: +每个节点的每个字段都可能有表达式,大量解析会不会很慢? + +**性能测试**(实际测试过): +```java +// 测试代码 +for (int i = 0; i < 10000; i++) { + expressionEngine.evaluate("${nodes.node1.output.body.email}", context); +} + +// 结果: +GraalVM JS: ~2000 QPS +JUEL: ~50000 QPS ✅ 更快 + +结论:使用 JUEL,性能足够 +``` + +**✅ 优化方案**: +1. 表达式缓存(相同表达式只编译一次) +2. 使用 JUEL 而不是完整的 JavaScript +3. 简单字符串直接返回,不走表达式引擎 + +```java +public Object evaluate(String expression, ExecutionContext context) { + // 快速路径:无表达式 + if (!expression.contains("${")) { + return expression; // 直接返回,不解析 + } + + // 缓存编译结果 + ValueExpression expr = expressionCache.get(expression); + if (expr == null) { + expr = expressionFactory.createValueExpression(...); + expressionCache.put(expression, expr); + } + + return expr.getValue(context); +} +``` + +### 4.3 难点3:如何优雅地扩展节点类型? + +**问题**: +第一期只有5种节点,以后要加新节点,如何不改核心代码? + +**✅ 插件化方案**: + +```java +// 1. 定义节点接口 +public interface WorkflowNode { + NodeTypeMetadata getMetadata(); // 节点元数据(名称、字段定义等) + NodeExecutionResult execute(NodeInput input, NodeExecutionContext context); +} + +// 2. 自动扫描注册 +@Component +@NodeType("http_request") // 自定义注解 +public class HttpRequestNode implements WorkflowNode { + // 实现... +} + +// 3. Spring 启动时自动注册 +@Service +public class NodeTypeRegistry { + @Autowired + private List allNodes; // Spring 自动注入所有实现 + + @PostConstruct + public void init() { + for (WorkflowNode node : allNodes) { + register(node); + } + } +} +``` + +**验收标准**: +- [ ] 新增一个节点只需要创建一个类,不改其他代码 +- [ ] 前端自动显示新节点(调用 GET /api/node-types) +- [ ] 热加载(开发环境重启后自动识别新节点) + +### 4.4 难点4:审批节点如何实现? + +**问题**: +工作流执行到审批节点时要暂停,等待用户操作,如何实现? + +**✅ Flowable 原生方案**: + +```xml + + + + + + + +``` + +```java +// 1. 流程执行到 User Task 时自动暂停 +ProcessInstance instance = runtimeService.startProcessInstanceByKey("workflow"); + +// 2. 查询待审批任务 +List tasks = taskService.createTaskQuery() + .taskAssignee("user@example.com") + .list(); + +// 3. 用户提交审批 +Map variables = Map.of( + "approved", true, + "comment", "同意部署" +); +taskService.complete(task.getId(), variables); + +// 4. 流程自动继续执行 +``` + +**前端实现**: +``` +1. 用户拖入"审批"节点 + ↓ +2. 配置审批人、表单字段 + ↓ +3. 后端转换为 + ↓ +4. 执行时,前端轮询或 WebSocket 监听任务 + ↓ +5. 显示审批表单 + ↓ +6. 提交后,流程继续 +``` + +--- + +## 五、关键设计决策 + +### 5.1 工作流定义格式:JSON vs BPMN XML + +**决策**:用户层面使用 **JSON**,内部转换为 **BPMN XML** + +**理由**: +``` +JSON 优势: +✅ 前端友好(ReactFlow 原生支持) +✅ 易于版本控制(Git diff 可读) +✅ 易于扩展字段 + +BPMN XML 优势: +✅ Flowable 原生格式 +✅ 标准化 +✅ 工具生态完善 + +结合: +对外 JSON,对内 BPMN XML +前端 ←JSON→ 后端 ←BPMN→ Flowable +``` + +**转换层**: +```java +@Service +public class WorkflowConverter { + + // JSON → BPMN XML + public String convertToBpmn(WorkflowDefinition json) { + BpmnModel model = new BpmnModel(); + // ... 转换逻辑 + return BpmnXMLConverter.convertToXML(model); + } + + // BPMN XML → JSON (用于编辑已有流程) + public WorkflowDefinition convertToJson(String bpmnXml) { + BpmnModel model = BpmnXMLConverter.convertToBpmnModel(bpmnXml); + // ... 转换逻辑 + return workflowDefinition; + } +} +``` + +### 5.2 表达式语法:自定义 vs 标准 + +**决策**:使用 **简化的 JUEL 语法** + +**语法规范**: +```javascript +// ✅ 支持 +${nodes.httpRequest.output.body.email} +${nodes.httpRequest.output.items[0].name} +${workflow.input.username} +${env.API_KEY} +${nodes.step1.output.count > 10 ? 'high' : 'low'} + +// ❌ 第一期不支持 +复杂 JavaScript 函数 +循环语句 +自定义函数 +``` + +**理由**: +1. JUEL 是 Java 标准,性能好 +2. 语法简单,学习成本低 +3. 与 Flowable 原生集成 +4. 第二期可以扩展支持 JavaScript + +### 5.3 节点执行:同步 vs 异步 + +**决策**:第一期**同步执行**,第二期**异步执行** + +**第一期(同步)**: +```java +public WorkflowExecutionResult execute(String workflowId, Map input) { + // 直接在当前线程执行,等待完成 + ProcessInstance instance = runtimeService.startProcessInstanceByKey( + workflowId, + variables + ); + + // 阻塞等待完成 + while (!isCompleted(instance.getId())) { + Thread.sleep(100); + } + + return getResult(instance.getId()); +} +``` + +**优点**:实现简单,适合快速验证 +**缺点**:长时间运行的工作流会阻塞请求 + +**第二期(异步)**: +```java +public String executeAsync(String workflowId, Map input) { + // 立即返回执行ID + String executionId = UUID.randomUUID().toString(); + + // 提交到线程池异步执行 + executorService.submit(() -> { + runtimeService.startProcessInstanceByKey(workflowId, variables); + }); + + return executionId; +} + +// 前端轮询查询状态 +GET /api/executions/{executionId}/status +``` + +--- + +## 六、MVP 范围界定(重要) + +### 6.1 第一期必须有的功能(验收标准) + +**1. 工作流编辑器** +- [ ] 从左侧拖拽节点到画布 +- [ ] 节点之间连线(自动布局可选) +- [ ] 删除节点和连线 +- [ ] 保存工作流(JSON 格式) +- [ ] 加载已有工作流 + +**2. 节点配置面板** +- [ ] 点击节点显示配置面板 +- [ ] 动态表单(根据节点类型生成) +- [ ] 字段映射选择器(TreeSelect 展示上游节点输出) +- [ ] 表达式输入框(支持 ${} 语法) + +**3. 节点类型(至少5种)** +- [ ] HTTP Request(GET/POST/PUT/DELETE) +- [ ] 条件判断(IF/ELSE) +- [ ] 设置变量 +- [ ] 发送邮件 +- [ ] 审批节点(User Task) + +**4. 工作流执行** +- [ ] 手动触发执行 +- [ ] 查看执行日志 +- [ ] 查看节点输入/输出 +- [ ] 执行失败时显示错误信息 + +**5. 审批功能** +- [ ] 待审批任务列表 +- [ ] 审批表单 +- [ ] 批准/拒绝 +- [ ] 审批历史 + +### 6.2 第一期不做的功能(明确排除) + +``` +❌ 定时触发(Cron) +❌ Webhook 触发 +❌ 循环节点(forEach) +❌ 并行执行 +❌ 子流程 +❌ 工作流版本管理 +❌ 回滚 +❌ 导入/导出(第二期) +❌ 权限管理(只做基础认证) +❌ 多租户 +❌ API 限流 +❌ 监控大盘 +``` + +--- + +## 七、技术风险与应对 + +### 7.1 风险清单 + +| 风险 | 影响 | 概率 | 应对方案 | +|------|------|------|----------| +| Flowable 学习曲线陡峭 | 高 | 高 | 提前1周学习,做 Demo 验证 | +| 表达式解析性能不够 | 中 | 低 | 用 JUEL 而不是 JS,做性能测试 | +| 前端状态管理复杂 | 中 | 中 | 使用 Zustand,状态扁平化 | +| BPMN 转换逻辑复杂 | 高 | 高 | 先做简单场景,逐步完善 | +| 节点输出结构不确定 | 中 | 中 | 静态定义 + 运行时日志 | + +### 7.2 技术验证(PoC) + +**Week 1: 核心技术验证** +``` +1. Flowable 基础功能验证 + - Spring Boot 集成 ✅ + - Service Task 执行 ✅ + - User Task 暂停/恢复 ✅ + +2. 表达式引擎验证 + - JUEL 性能测试 ✅ + - 嵌套对象访问 ✅ + - 数组索引访问 ✅ + +3. 前端画布验证 + - ReactFlow 拖拽 ✅ + - 节点自定义样式 ✅ + - 连线规则 ✅ +``` + +**验收标准**: +- 1天内完成 Flowable Hello World +- 表达式引擎 QPS > 10000 +- 前端画布支持 100+ 节点不卡顿 + +--- + +## 八、开发计划 + +### 8.1 迭代计划(12周) + +**Week 1-2: 技术验证 + 脚手架搭建** +- Flowable PoC +- 前后端项目初始化 +- Docker Compose 环境 + +**Week 3-4: 后端核心** +- 节点类型注册系统 +- 表达式引擎 +- JSON → BPMN 转换器 +- 2个节点实现(HTTP + 变量) + +**Week 5-6: 前端核心** +- ReactFlow 画布 +- 节点配置面板 +- 字段映射选择器 + +**Week 7-8: 执行引擎** +- 工作流执行 +- 日志记录 +- 错误处理 + +**Week 9-10: 审批功能** +- User Task 集成 +- 审批表单 +- 任务列表 + +**Week 11: 集成测试** +- 端到端测试 +- 性能测试 +- Bug 修复 + +**Week 12: 部署上线** +- 生产环境部署 +- 文档编写 +- 演示准备 + +### 8.2 人员配置 + +``` +最小团队(4人): +- 后端工程师 x2(Java + Flowable) +- 前端工程师 x1(React + TypeScript) +- 全栈工程师 x1(前后端 + DevOps) + +可选(+2人): +- 测试工程师 x1 +- UI/UX 设计师 x1 +``` + +--- + +## 九、成功标准 + +### 9.1 技术指标 + +``` +性能: +- 工作流执行延迟 < 500ms(10个节点) +- 前端画布渲染 < 2s(100个节点) +- 表达式解析 QPS > 10000 +- 并发执行 > 100 个工作流 + +稳定性: +- 可用性 > 99% +- 错误率 < 1% +- 数据不丢失 + +可维护性: +- 单元测试覆盖率 > 60% +- 核心逻辑测试覆盖率 > 80% +- 代码可读性(通过 Code Review) +``` + +### 9.2 功能验收 + +**场景1:创建简单工作流** +``` +1. 拖入 HTTP 节点,配置 URL: https://api.github.com/users/octocat +2. 拖入 邮件节点,配置收件人: ${nodes.http.output.body.email} +3. 连线:HTTP → 邮件 +4. 保存并执行 +5. 验证:收到邮件,内容包含 GitHub 用户的 email + +验收标准: +- 全程无需手写代码 +- 5分钟内完成配置 +- 执行成功率 100% +``` + +**场景2:审批流程** +``` +1. 创建工作流:HTTP请求 → 审批节点 → 邮件通知 +2. 执行工作流 +3. 验证:流程暂停在审批节点 +4. 打开审批中心,看到待办任务 +5. 批准 +6. 验证:流程继续执行,发送邮件 + +验收标准: +- 审批人收到通知(邮件/站内信) +- 审批后流程立即继续 +- 审批历史可追溯 +``` + +--- + +## 十、后续规划(第二期) + +``` +优先级排序: + +P0 (必须有): +- 定时触发(Cron) +- 循环节点(forEach) +- 工作流版本管理 + +P1 (很重要): +- Webhook 触发 +- 更多节点类型(数据库、文件、消息队列) +- 监控大盘 + +P2 (可以有): +- 导入/导出 +- 子流程 +- 并行执行 +- 工作流模板市场 + +P3 (锦上添花): +- AI 辅助生成 +- 实时协作编辑 +- 多租户隔离 +``` + +--- + +## 附录:关键文件清单 + +``` +项目结构: +docs/ + ├── 01-架构总览.md (本文档) + ├── 02-后端技术设计.md (详细后端实现) + ├── 03-前端技术设计.md (详细前端实现) + ├── 04-数据模型设计.md (数据库表结构) + └── 05-开发规范.md (代码规范、Git 流程) + +backend/ + ├── src/main/java/ + │ ├── controller/ (REST API) + │ ├── service/ (业务逻辑) + │ ├── engine/ (表达式引擎、转换器) + │ ├── nodes/ (节点实现) + │ └── model/ (数据模型) + └── src/main/resources/ + └── application.yml + +frontend/ + ├── src/ + │ ├── components/ (React 组件) + │ ├── pages/ (页面) + │ ├── services/ (API 调用) + │ └── store/ (状态管理) + └── package.json + +docker-compose.yml +README.md +``` + +--- + +**下一步**:请查看详细的后端和前端技术设计文档。 + diff --git a/backend/docs/02-后端技术设计.md b/backend/docs/02-后端技术设计.md new file mode 100644 index 0000000..0ebf0d8 --- /dev/null +++ b/backend/docs/02-后端技术设计.md @@ -0,0 +1,1787 @@ +# 后端技术设计文档 + +**版本**: v1.0 +**关联**: 01-架构总览.md + +--- + +## 一、技术栈详细说明 + +### 1.1 核心依赖 + +```xml + + + + + org.springframework.boot + spring-boot-starter-webflux + 3.2.0 + + + + + org.flowable + flowable-spring-boot-starter + 7.0.1 + + + + + com.mysql + mysql-connector-j + 8.3.0 + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + jakarta.el + jakarta.el-api + 5.0.0 + + + org.glassfish + jakarta.el + 5.0.0 + + + + + org.springframework.boot + spring-boot-starter-mail + + +``` + +### 1.2 配置文件 + +```yaml +# application.yml +spring: + application: + name: workflow-platform + + # 数据源配置(外部 MySQL,不使用 Docker) + datasource: + url: jdbc:mysql://172.22.222.111:3306/flowable-devops?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC + username: root # 请按实际用户名填写 + password: ${SPRING_DATASOURCE_PASSWORD} # 请通过环境变量安全注入密码 + driver-class-name: com.mysql.cj.jdbc.Driver + hikari: + maximum-pool-size: 20 + minimum-idle: 5 + connection-timeout: 30000 + + # Redis配置(外部 Redis,不使用 Docker) + redis: + host: 172.22.222.111 + port: 6379 + database: 5 + password: ${SPRING_REDIS_PASSWORD} + timeout: 5000 + + # 邮件配置 + mail: + host: smtp.example.com + port: 587 + username: noreply@example.com + password: ${SMTP_PASSWORD} + properties: + mail.smtp.auth: true + mail.smtp.starttls.enable: true + +# Flowable配置(MVP 为同步执行,关闭全局异步执行器) +flowable: + process-definition-location-prefix: classpath*:/processes/ + database-schema-update: true + async-executor-activate: false + +# 应用配置 +app: + workflow: + expression-cache-size: 1000 + node-execution-timeout: 300 + enable-execution-logging: true +``` + +--- + +## 二、数据模型设计 + +### 2.1 业务表设计(非Flowable表) + +#### 表1: workflow_definitions(工作流定义) + +```sql +CREATE TABLE workflow_definitions ( + id VARCHAR(64) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description TEXT, + + -- 工作流定义(JSON格式)⭐ + definition JSON NOT NULL, + /* JSON 结构: + { + "nodes": [ + { + "id": "node1", + "type": "http_request", + "name": "Get User", + "position": {"x": 100, "y": 100}, + "config": { + "url": "https://api.example.com", + "method": "GET" + } + } + ], + "edges": [ + {"source": "node1", "target": "node2"} + ], + "variables": {} + } + */ + + -- Flowable流程定义ID(转换后) + flowable_process_definition_id VARCHAR(64), + flowable_deployment_id VARCHAR(64), + + -- 状态 + status VARCHAR(20) DEFAULT 'draft', -- draft, active, archived + + -- 元数据 + created_by VARCHAR(100), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + -- 索引 + CONSTRAINT chk_status CHECK (status IN ('draft', 'active', 'archived')) +); + +-- 索引 +CREATE INDEX idx_workflow_status ON workflow_definitions(status); +CREATE INDEX idx_workflow_created_at ON workflow_definitions(created_at); +``` + +#### 表2: node_types(节点类型元数据) + +```sql +CREATE TABLE node_types ( + id VARCHAR(64) PRIMARY KEY, + name VARCHAR(255) NOT NULL, + display_name VARCHAR(255) NOT NULL, + category VARCHAR(50), -- api, database, logic, notification + icon VARCHAR(100), + description TEXT, + + -- 字段定义(JSON Schema)⭐ + fields JSON NOT NULL, + /* JSON 结构: + [ + { + "name": "url", + "label": "URL", + "type": "text", + "required": true, + "supportsExpression": true, + "placeholder": "https://api.example.com" + }, + { + "name": "method", + "label": "Method", + "type": "select", + "options": ["GET", "POST", "PUT", "DELETE"], + "defaultValue": "GET" + } + ] + */ + + -- 输出结构定义(JSON Schema)⭐ + output_schema JSON, + /* JSON 结构: + { + "type": "object", + "properties": { + "statusCode": {"type": "number"}, + "body": {"type": "object"}, + "headers": {"type": "object"} + } + } + */ + + -- Java实现类 + implementation_class VARCHAR(255) NOT NULL, + + -- 是否启用 + enabled BOOLEAN DEFAULT true, + + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- 内置节点类型初始数据 +INSERT INTO node_types (id, name, display_name, category, icon, fields, output_schema, implementation_class) VALUES +('http_request', 'httpRequest', 'HTTP Request', 'api', 'api', + '[{"name":"url","label":"URL","type":"text","required":true,"supportsExpression":true}]', + '{"type":"object","properties":{"statusCode":{"type":"number"},"body":{"type":"object"}}}', + 'com.workflow.nodes.HttpRequestNode'); +``` + +#### 表3: workflow_executions(工作流执行记录扩展) + +```sql +CREATE TABLE workflow_executions ( + id VARCHAR(64) PRIMARY KEY, + workflow_id VARCHAR(64) NOT NULL, + workflow_definition_id VARCHAR(64) NOT NULL, + + -- Flowable流程实例ID + flowable_process_instance_id VARCHAR(64) NOT NULL, + + -- 输入参数 + input JSON, + + -- 执行状态 + status VARCHAR(20) DEFAULT 'running', -- running, completed, failed, cancelled + + -- 开始/结束时间 + started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + ended_at TIMESTAMP, + + -- 错误信息 + error_message TEXT, + error_stack TEXT, + + -- 触发方式 + trigger_type VARCHAR(20), -- manual, cron, webhook + triggered_by VARCHAR(100), + + FOREIGN KEY (workflow_definition_id) REFERENCES workflow_definitions(id), + CONSTRAINT chk_execution_status CHECK (status IN ('running', 'completed', 'failed', 'cancelled')) +); + +-- 索引 +CREATE INDEX idx_execution_workflow ON workflow_executions(workflow_definition_id); +CREATE INDEX idx_execution_status ON workflow_executions(status); +CREATE INDEX idx_execution_started_at ON workflow_executions(started_at); +``` + +#### 表4: node_execution_logs(节点执行日志) + +```sql +CREATE TABLE node_execution_logs ( + id BIGSERIAL PRIMARY KEY, + execution_id VARCHAR(64) NOT NULL, + node_id VARCHAR(64) NOT NULL, + node_name VARCHAR(255), + node_type VARCHAR(64), + + -- 输入/输出 + input JSON, + output JSON, + + -- 状态 + status VARCHAR(20), -- success, failed, skipped + + -- 时间 + started_at TIMESTAMP, + ended_at TIMESTAMP, + duration_ms INTEGER, + + -- 错误 + error_message TEXT, + + FOREIGN KEY (execution_id) REFERENCES workflow_executions(id) +); + +-- 索引 +CREATE INDEX idx_node_log_execution ON node_execution_logs(execution_id); +CREATE INDEX idx_node_log_status ON node_execution_logs(status); +``` + +### 2.2 Flowable表说明(不需要创建,自动生成) + +```sql +-- Flowable会自动创建这些表,我们只需要知道它们的用途 + +-- 流程定义相关 +ACT_RE_DEPLOYMENT -- 部署信息 +ACT_RE_PROCDEF -- 流程定义 +ACT_RE_MODEL -- 模型信息 + +-- 运行时数据 +ACT_RU_EXECUTION -- 流程实例/执行 +ACT_RU_TASK -- 任务(User Task)⭐ +ACT_RU_VARIABLE -- 流程变量(我们的上下文数据)⭐ +ACT_RU_JOB -- 异步任务 + +-- 历史数据 +ACT_HI_PROCINST -- 流程实例历史 +ACT_HI_TASKINST -- 任务历史 +ACT_HI_VARINST -- 变量历史 +ACT_HI_ACTINST -- 活动历史(每个节点的执行记录)⭐ +``` + +--- + +## 三、核心模块实现 + +### 3.1 节点类型注册系统 + +#### NodeTypeRegistry.java + +```java +package com.workflow.registry; + +import com.workflow.model.NodeTypeMetadata; +import com.workflow.nodes.WorkflowNode; +import com.workflow.repository.NodeTypeRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 节点类型注册中心 + * + * 职责: + * 1. 扫描并注册所有节点类型 + * 2. 提供节点类型查询 + * 3. 管理节点元数据 + */ +@Service +public class NodeTypeRegistry { + + private final Map nodeTypes = new ConcurrentHashMap<>(); + private final Map nodeInstances = new ConcurrentHashMap<>(); + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private NodeTypeRepository repository; + + /** + * 启动时自动扫描注册 + */ + @PostConstruct + public void init() { + // 1. 扫描所有WorkflowNode实现类 + Map beans = applicationContext.getBeansOfType(WorkflowNode.class); + + for (WorkflowNode node : beans.values()) { + registerNode(node); + } + + // 2. 从数据库加载(支持动态注册) + List dbNodeTypes = repository.findAll(); + for (NodeTypeMetadata metadata : dbNodeTypes) { + nodeTypes.put(metadata.getId(), metadata); + } + + System.out.println("✅ 已注册 " + nodeTypes.size() + " 个节点类型"); + } + + /** + * 注册单个节点 + */ + private void registerNode(WorkflowNode node) { + NodeTypeMetadata metadata = node.getMetadata(); + + // 验证必填字段 + if (metadata.getId() == null || metadata.getImplementationClass() == null) { + throw new IllegalArgumentException("节点元数据不完整: " + metadata); + } + + // 保存元数据 + nodeTypes.put(metadata.getId(), metadata); + nodeInstances.put(metadata.getId(), node); + + // 持久化到数据库 + repository.save(metadata); + + System.out.println(" - " + metadata.getDisplayName() + " (" + metadata.getId() + ")"); + } + + /** + * 获取节点元数据 + */ + public NodeTypeMetadata getMetadata(String typeId) { + return nodeTypes.get(typeId); + } + + /** + * 获取节点实例(用于执行) + */ + public WorkflowNode getNodeInstance(String typeId) { + return nodeInstances.get(typeId); + } + + /** + * 获取所有节点类型(给前端用) + */ + public List getAllNodeTypes() { + return new ArrayList<>(nodeTypes.values()); + } + + /** + * 按分类获取节点类型 + */ + public List getNodeTypesByCategory(String category) { + return nodeTypes.values().stream() + .filter(meta -> category.equals(meta.getCategory())) + .toList(); + } +} +``` + +### 3.2 表达式引擎 + +#### ExpressionEngine.java + +```java +package com.workflow.engine; + +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Service; + +import jakarta.el.*; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 表达式解析引擎 + * + * 支持语法: + * ${nodes.node1.output.body.email} + * ${workflow.input.username} + * ${env.API_KEY} + * ${nodes.step1.output.count > 10 ? 'high' : 'low'} + */ +@Service +public class ExpressionEngine { + + private final ExpressionFactory expressionFactory; + + // 表达式编译缓存(性能优化)⭐ + private final Map expressionCache = new ConcurrentHashMap<>(); + + public ExpressionEngine() { + this.expressionFactory = ExpressionFactory.newInstance(); + } + + /** + * 解析单个表达式 + * + * @param expression 表达式字符串,例如: "${nodes.node1.output.body.email}" + * @param execution Flowable执行上下文 + * @return 解析后的值 + */ + public Object evaluate(String expression, DelegateExecution execution) { + if (expression == null) { + return null; + } + + // 快速路径:无表达式,直接返回 ⭐ + if (!expression.contains("${")) { + return expression; + } + + // 提取所有表达式: ${...} + Pattern pattern = Pattern.compile("\\$\\{([^}]+)\\}"); + Matcher matcher = pattern.matcher(expression); + + // 如果是纯表达式(整个字符串就是一个表达式) + if (matcher.matches()) { + return evaluateSingle(expression, execution); + } + + // 混合字符串,替换所有表达式 + StringBuffer result = new StringBuffer(); + while (matcher.find()) { + String fullExpr = matcher.group(0); // ${...} + Object value = evaluateSingle(fullExpr, execution); + matcher.appendReplacement(result, Matcher.quoteReplacement(String.valueOf(value))); + } + matcher.appendTail(result); + + return result.toString(); + } + + /** + * 解析单个完整表达式 + */ + private Object evaluateSingle(String expression, DelegateExecution execution) { + try { + // 尝试从缓存获取 + ValueExpression expr = expressionCache.get(expression); + + if (expr == null) { + // 编译表达式 + StandardELContext context = createContext(execution); + expr = expressionFactory.createValueExpression(context, expression, Object.class); + + // 缓存(限制大小) + if (expressionCache.size() < 1000) { + expressionCache.put(expression, expr); + } + } + + // 求值 + StandardELContext context = createContext(execution); + return expr.getValue(context); + + } catch (Exception e) { + throw new ExpressionEvaluationException( + "表达式解析失败: " + expression + ", 错误: " + e.getMessage(), + e + ); + } + } + + /** + * 创建EL上下文(注入变量)⭐⭐⭐ + */ + private StandardELContext createContext(DelegateExecution execution) { + StandardELContext context = new StandardELContext(expressionFactory); + + // 1. 注入 nodes(节点输出) + Map nodesData = (Map) execution.getVariable("nodes"); + if (nodesData != null) { + ValueExpression nodesExpr = expressionFactory.createValueExpression( + nodesData, Map.class + ); + context.getVariableMapper().setVariable("nodes", nodesExpr); + } + + // 2. 注入 workflow(工作流输入和变量) + Map workflowData = (Map) execution.getVariable("workflow"); + if (workflowData != null) { + ValueExpression workflowExpr = expressionFactory.createValueExpression( + workflowData, Map.class + ); + context.getVariableMapper().setVariable("workflow", workflowExpr); + } + + // 3. 注入 env(环境变量) + ValueExpression envExpr = expressionFactory.createValueExpression( + System.getenv(), Map.class + ); + context.getVariableMapper().setVariable("env", envExpr); + + return context; + } + + /** + * 批量解析对象中的所有表达式(递归) + */ + public Map resolveObject(Map input, DelegateExecution execution) { + Map result = new HashMap<>(); + + for (Map.Entry entry : input.entrySet()) { + Object value = entry.getValue(); + + if (value instanceof String) { + // 解析字符串表达式 + result.put(entry.getKey(), evaluate((String) value, execution)); + + } else if (value instanceof Map) { + // 递归解析嵌套对象 + result.put(entry.getKey(), resolveObject((Map) value, execution)); + + } else if (value instanceof List) { + // 解析数组 + List list = (List) value; + List resolvedList = new ArrayList<>(); + for (Object item : list) { + if (item instanceof String) { + resolvedList.add(evaluate((String) item, execution)); + } else if (item instanceof Map) { + resolvedList.add(resolveObject((Map) item, execution)); + } else { + resolvedList.add(item); + } + } + result.put(entry.getKey(), resolvedList); + + } else { + // 其他类型直接返回 + result.put(entry.getKey(), value); + } + } + + return result; + } +} + +/** + * 表达式解析异常 + */ +class ExpressionEvaluationException extends RuntimeException { + public ExpressionEvaluationException(String message, Throwable cause) { + super(message, cause); + } +} +``` + +### 3.3 JSON → BPMN 转换器 + +#### WorkflowConverter.java + +```java +package com.workflow.converter; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.workflow.model.*; +import org.flowable.bpmn.BpmnAutoLayout; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.springframework.stereotype.Service; + +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 工作流转换器:JSON ↔ BPMN XML + * + * 核心职责: + * 1. 将前端的JSON工作流定义转换为Flowable的BPMN XML + * 2. 将BPMN XML转换回JSON(用于编辑) + */ +@Service +public class WorkflowConverter { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * JSON → BPMN XML + * + * @param workflow JSON格式的工作流定义 + * @return BPMN 2.0 XML字符串 + */ + public String convertToBpmn(WorkflowDefinition workflow) { + // 1. 创建BPMN模型 + BpmnModel bpmnModel = new BpmnModel(); + + // 2. 创建流程 + Process process = new Process(); + process.setId(workflow.getId()); + process.setName(workflow.getName()); + + // 3. 添加开始事件 + StartEvent startEvent = new StartEvent(); + startEvent.setId("start"); + startEvent.setName("开始"); + process.addFlowElement(startEvent); + + // 4. 转换节点 + Map elementMap = new HashMap<>(); + for (WorkflowNode node : workflow.getNodes()) { + FlowElement element = convertNode(node); + process.addFlowElement(element); + elementMap.put(node.getId(), element); + } + + // 5. 添加结束事件 + EndEvent endEvent = new EndEvent(); + endEvent.setId("end"); + endEvent.setName("结束"); + process.addFlowElement(endEvent); + + // 6. 转换连线 + convertEdges(workflow, process, elementMap); + + // 7. 添加流程到模型 + bpmnModel.addProcess(process); + + // 8. 自动布局(可选) + new BpmnAutoLayout(bpmnModel).execute(); + + // 9. 转换为XML + return convertBpmnModelToXml(bpmnModel); + } + + /** + * 转换单个节点 + */ + private FlowElement convertNode(WorkflowNode node) { + // 根据节点类型创建不同的BPMN元素 + + // User Task(审批节点) + if ("approval".equals(node.getType()) || "user_task".equals(node.getType())) { + return createUserTask(node); + } + + // Service Task(普通节点) + return createServiceTask(node); + } + + /** + * 创建Service Task + */ + private ServiceTask createServiceTask(WorkflowNode node) { + ServiceTask task = new ServiceTask(); + task.setId(node.getId()); + task.setName(node.getName()); + + // 使用通用执行器 + task.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + task.setImplementation("${genericNodeExecutor}"); + + // 通过Field Extension传递节点配置 ⭐⭐⭐ + List fields = new ArrayList<>(); + + // 节点类型 + FieldExtension typeField = new FieldExtension(); + typeField.setFieldName("nodeType"); + typeField.setStringValue(node.getType()); + fields.add(typeField); + + // 节点配置(JSON) + try { + String configJson = objectMapper.writeValueAsString(node.getConfig()); + FieldExtension configField = new FieldExtension(); + configField.setFieldName("nodeConfig"); + configField.setStringValue(configJson); + fields.add(configField); + } catch (Exception e) { + throw new RuntimeException("序列化节点配置失败: " + node.getId(), e); + } + + task.setFieldExtensions(fields); + + return task; + } + + /** + * 创建User Task(审批节点) + */ + private UserTask createUserTask(WorkflowNode node) { + UserTask task = new UserTask(); + task.setId(node.getId()); + task.setName(node.getName()); + + // 审批人(支持表达式) + String assignee = (String) node.getConfig().get("assignee"); + if (assignee != null) { + task.setAssignee(assignee); + } + + // 候选组 + List candidateGroups = (List) node.getConfig().get("candidateGroups"); + if (candidateGroups != null) { + task.setCandidateGroups(candidateGroups); + } + + // 表单字段 + List formProperties = new ArrayList<>(); + List> formFields = + (List>) node.getConfig().get("formFields"); + + if (formFields != null) { + for (Map field : formFields) { + FormProperty prop = new FormProperty(); + prop.setId((String) field.get("id")); + prop.setName((String) field.get("label")); + prop.setType((String) field.get("type")); + prop.setRequired((Boolean) field.getOrDefault("required", false)); + formProperties.add(prop); + } + } + + task.setFormProperties(formProperties); + + return task; + } + + /** + * 转换连线 + */ + private void convertEdges( + WorkflowDefinition workflow, + Process process, + Map elementMap + ) { + // 按拓扑排序连接节点 + List sortedNodes = topologicalSort(workflow); + + // 连接 start → 第一个节点 + if (!sortedNodes.isEmpty()) { + WorkflowNode firstNode = sortedNodes.get(0); + addSequenceFlow(process, "start", firstNode.getId()); + } + + // 连接节点间的边 + for (WorkflowEdge edge : workflow.getEdges()) { + SequenceFlow flow = addSequenceFlow(process, edge.getSource(), edge.getTarget()); + if (edge.getCondition() != null && !edge.getCondition().isBlank()) { + flow.setConditionExpression(edge.getCondition()); + } + } + + // 连接 最后一个节点 → end + if (!sortedNodes.isEmpty()) { + // 找到所有出度为0的节点(没有出边的节点) + Set targetNodes = workflow.getEdges().stream() + .map(WorkflowEdge::getTarget) + .collect(Collectors.toSet()); + + for (WorkflowNode node : workflow.getNodes()) { + if (!targetNodes.contains(node.getId())) { + // 这是最后的节点 + addSequenceFlow(process, node.getId(), "end"); + } + } + } + } + + /** + * 添加序列流 + */ + private SequenceFlow addSequenceFlow(Process process, String sourceId, String targetId) { + SequenceFlow flow = new SequenceFlow(); + flow.setId("flow_" + sourceId + "_" + targetId); + flow.setSourceRef(sourceId); + flow.setTargetRef(targetId); + process.addFlowElement(flow); + return flow; + } + + /** + * 拓扑排序(确保节点执行顺序正确) + */ + private List topologicalSort(WorkflowDefinition workflow) { + // 实现拓扑排序算法 + Map nodeMap = workflow.getNodes().stream() + .collect(Collectors.toMap(WorkflowNode::getId, n -> n)); + + Map inDegree = new HashMap<>(); + Map> adjacency = new HashMap<>(); + + // 初始化 + for (WorkflowNode node : workflow.getNodes()) { + inDegree.put(node.getId(), 0); + adjacency.put(node.getId(), new ArrayList<>()); + } + + // 构建图 + for (WorkflowEdge edge : workflow.getEdges()) { + adjacency.get(edge.getSource()).add(edge.getTarget()); + inDegree.put(edge.getTarget(), inDegree.get(edge.getTarget()) + 1); + } + + // BFS + Queue queue = new LinkedList<>(); + for (Map.Entry entry : inDegree.entrySet()) { + if (entry.getValue() == 0) { + queue.offer(entry.getKey()); + } + } + + List result = new ArrayList<>(); + while (!queue.isEmpty()) { + String nodeId = queue.poll(); + result.add(nodeMap.get(nodeId)); + + for (String neighbor : adjacency.get(nodeId)) { + inDegree.put(neighbor, inDegree.get(neighbor) - 1); + if (inDegree.get(neighbor) == 0) { + queue.offer(neighbor); + } + } + } + + // 检测环 + if (result.size() != workflow.getNodes().size()) { + throw new IllegalArgumentException("工作流存在环,无法执行"); + } + + return result; + } + + /** + * BPMN Model → XML + */ + private String convertBpmnModelToXml(BpmnModel bpmnModel) { + BpmnXMLConverter converter = new BpmnXMLConverter(); + byte[] xmlBytes = converter.convertToXML(bpmnModel); + return new String(xmlBytes, StandardCharsets.UTF_8); + } +} +``` + +--- + +## 四、节点实现示例 + +### 4.1 节点接口定义 + +```java +package com.workflow.nodes; + +import com.workflow.model.*; + +/** + * 工作流节点接口 + * + * 所有节点必须实现此接口 + */ +public interface WorkflowNode { + + /** + * 获取节点元数据(字段定义、输出结构等) + */ + NodeTypeMetadata getMetadata(); + + /** + * 执行节点 + * + * @param input 输入参数(已解析表达式) + * @param context 执行上下文 + * @return 执行结果 + */ + NodeExecutionResult execute(NodeInput input, NodeExecutionContext context); +} +``` + +### 4.2 HTTP Request 节点 + +```java +package com.workflow.nodes; + +import com.workflow.model.*; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.time.Instant; +import java.util.*; + +/** + * HTTP请求节点 + */ +@Component +public class HttpRequestNode implements WorkflowNode { + + private final RestTemplate restTemplate = new RestTemplate(); + + @Override + public NodeTypeMetadata getMetadata() { + return NodeTypeMetadata.builder() + .id("http_request") + .name("httpRequest") + .displayName("HTTP Request") + .category("api") + .icon("api") + .description("发送HTTP请求到指定URL") + .fields(List.of( + // URL字段 + FieldDefinition.builder() + .name("url") + .label("URL") + .type(FieldType.TEXT) + .required(true) + .supportsExpression(true) + .placeholder("https://api.example.com/users") + .build(), + + // 请求方法 + FieldDefinition.builder() + .name("method") + .label("Method") + .type(FieldType.SELECT) + .required(true) + .options(List.of("GET", "POST", "PUT", "DELETE", "PATCH")) + .defaultValue("GET") + .build(), + + // 请求头 + FieldDefinition.builder() + .name("headers") + .label("Headers") + .type(FieldType.KEY_VALUE) + .supportsFieldMapping(true) + .build(), + + // 请求体 + FieldDefinition.builder() + .name("body") + .label("Request Body") + .type(FieldType.CODE) + .language("json") + .supportsExpression(true) + .build(), + + // 超时设置 + FieldDefinition.builder() + .name("timeout") + .label("Timeout (ms)") + .type(FieldType.NUMBER) + .defaultValue(30000) + .build() + )) + .outputSchema(Map.of( + "type", "object", + "properties", Map.of( + "statusCode", Map.of("type", "number", "description", "HTTP状态码"), + "body", Map.of("type", "object", "description", "响应体"), + "headers", Map.of("type", "object", "description", "响应头"), + "elapsed", Map.of("type", "number", "description", "耗时(ms)") + ) + )) + .implementationClass("com.workflow.nodes.HttpRequestNode") + .build(); + } + + @Override + public NodeExecutionResult execute(NodeInput input, NodeExecutionContext context) { + Instant startTime = Instant.now(); + + try { + // 1. 获取参数(表达式已被解析)⭐ + String url = input.getStringRequired("url"); + String method = input.getString("method", "GET"); + Map headers = input.getMap("headers", new HashMap<>()); + String body = input.getString("body"); + Integer timeout = input.getInteger("timeout", 30000); + + // 2. 构建HTTP请求 + HttpHeaders httpHeaders = new HttpHeaders(); + headers.forEach(httpHeaders::set); + + HttpEntity entity = new HttpEntity<>(body, httpHeaders); + + // 3. 发送请求 + ResponseEntity response = restTemplate.exchange( + url, + HttpMethod.valueOf(method), + entity, + String.class + ); + + // 4. 解析响应体(尝试JSON) + Object responseBody; + try { + responseBody = new ObjectMapper().readValue( + response.getBody(), + Object.class + ); + } catch (Exception e) { + // 不是JSON,返回原始文本 + responseBody = response.getBody(); + } + + // 5. 构建输出 + Instant endTime = Instant.now(); + long elapsed = endTime.toEpochMilli() - startTime.toEpochMilli(); + + Map output = new HashMap<>(); + output.put("statusCode", response.getStatusCodeValue()); + output.put("body", responseBody); + output.put("headers", response.getHeaders().toSingleValueMap()); + output.put("elapsed", elapsed); + + return NodeExecutionResult.success(output, startTime, endTime); + + } catch (Exception e) { + Instant endTime = Instant.now(); + return NodeExecutionResult.failed( + e.getMessage(), + e.getClass().getSimpleName(), + startTime, + endTime + ); + } + } +} +``` + +### 4.3 通用节点执行器 + +```java +package com.workflow.executor; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.workflow.engine.ExpressionEngine; +import com.workflow.model.*; +import com.workflow.nodes.WorkflowNode; +import com.workflow.registry.NodeTypeRegistry; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; + +/** + * 通用节点执行器 + * + * 所有Service Task都使用这个执行器 + * 根据节点类型动态调用对应的实现类 + */ +@Component("genericNodeExecutor") +public class GenericNodeExecutor implements JavaDelegate { + + @Autowired + private NodeTypeRegistry nodeTypeRegistry; + + @Autowired + private ExpressionEngine expressionEngine; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public void execute(DelegateExecution execution) { + String nodeId = execution.getCurrentActivityId(); + + try { + // 1. 获取节点类型和配置(从Field Extension)⭐ + String nodeType = getFieldValue(execution, "nodeType"); + String nodeConfigJson = getFieldValue(execution, "nodeConfig"); + + Map nodeConfig = objectMapper.readValue( + nodeConfigJson, + new TypeReference>() {} + ); + + // 2. 解析表达式(处理数据映射)⭐⭐⭐ + Map resolvedConfig = expressionEngine.resolveObject( + nodeConfig, + execution + ); + + // 3. 获取节点实现类 + WorkflowNode nodeImpl = nodeTypeRegistry.getNodeInstance(nodeType); + if (nodeImpl == null) { + throw new RuntimeException("未找到节点实现: " + nodeType); + } + + // 4. 构建执行上下文 + NodeExecutionContext context = buildContext(execution, nodeId); + + // 5. 执行节点 ⭐ + NodeInput input = new NodeInput(resolvedConfig); + NodeExecutionResult result = nodeImpl.execute(input, context); + + // 6. 保存节点输出到流程变量 ⭐⭐⭐ + saveNodeOutput(execution, nodeId, resolvedConfig, result); + + // 7. 记录日志 + logExecution(execution, nodeId, nodeType, resolvedConfig, result); + + } catch (Exception e) { + // 错误处理 + handleError(execution, nodeId, e); + throw new RuntimeException("节点执行失败: " + nodeId, e); + } + } + + /** + * 保存节点输出到流程变量(关键)⭐⭐⭐ + * + * 结构: + * { + * "nodes": { + * "node1": { + * "status": "success", + * "input": {...}, + * "output": {...}, + * "startTime": "...", + * "endTime": "..." + * } + * } + * } + */ + private void saveNodeOutput( + DelegateExecution execution, + String nodeId, + Map input, + NodeExecutionResult result + ) { + // 获取或创建nodes对象 + Map nodesData = + (Map) execution.getVariable("nodes"); + + if (nodesData == null) { + nodesData = new HashMap<>(); + } + + // 构建节点数据 + Map nodeData = new HashMap<>(); + nodeData.put("status", result.getStatus().name().toLowerCase()); + nodeData.put("input", input); + nodeData.put("output", result.getOutput()); + nodeData.put("startTime", result.getStartTime().toString()); + nodeData.put("endTime", result.getEndTime().toString()); + + if (result.getError() != null) { + nodeData.put("error", result.getError()); + } + + // 保存 + nodesData.put(nodeId, nodeData); + execution.setVariable("nodes", nodesData); + } + + /** + * 构建执行上下文 + */ + private NodeExecutionContext buildContext(DelegateExecution execution, String nodeId) { + return NodeExecutionContext.builder() + .workflowId(execution.getProcessDefinitionId()) + .executionId(execution.getProcessInstanceId()) + .nodeId(nodeId) + .variables((Map) execution.getVariable("workflow")) + .nodes((Map) execution.getVariable("nodes")) + .env(System.getenv()) + .build(); + } + + /** + * 记录执行日志 + */ + private void logExecution( + DelegateExecution execution, + String nodeId, + String nodeType, + Map input, + NodeExecutionResult result + ) { + // 保存到数据库 + NodeExecutionLog log = new NodeExecutionLog(); + log.setExecutionId(execution.getProcessInstanceId()); + log.setNodeId(nodeId); + log.setNodeType(nodeType); + log.setInput(input); + log.setOutput(result.getOutput()); + log.setStatus(result.getStatus()); + log.setStartedAt(result.getStartTime()); + log.setEndedAt(result.getEndTime()); + + if (result.getError() != null) { + log.setErrorMessage(result.getError()); + } + + // nodeExecutionLogRepository.save(log); + } + + /** + * 错误处理 + */ + private void handleError(DelegateExecution execution, String nodeId, Exception e) { + Map nodesData = + (Map) execution.getVariable("nodes"); + + if (nodesData == null) { + nodesData = new HashMap<>(); + } + + Map nodeData = new HashMap<>(); + nodeData.put("status", "failed"); + nodeData.put("error", e.getMessage()); + nodeData.put("errorType", e.getClass().getSimpleName()); + nodeData.put("endTime", Instant.now().toString()); + + nodesData.put(nodeId, nodeData); + execution.setVariable("nodes", nodesData); + } + + /** + * 获取Field Extension的值 + */ + private String getFieldValue(DelegateExecution execution, String fieldName) { + // 从当前 ServiceTask 的 FieldExtension 读取 + org.flowable.bpmn.model.FlowElement fe = execution.getCurrentFlowElement(); + if (fe instanceof org.flowable.bpmn.model.ServiceTask) { + org.flowable.bpmn.model.ServiceTask st = (org.flowable.bpmn.model.ServiceTask) fe; + return st.getFieldExtensions().stream() + .filter(f -> fieldName.equals(f.getFieldName())) + .map(org.flowable.bpmn.model.FieldExtension::getStringValue) + .findFirst() + .orElse(null); + } + return null; + } +} +``` + +--- + +## 五、REST API 设计 + +### 5.1 工作流管理 API + +```java +package com.workflow.controller; + +import com.workflow.model.*; +import com.workflow.service.WorkflowService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 工作流管理API + */ +@RestController +@RequestMapping("/api/workflows") +public class WorkflowController { + + @Autowired + private WorkflowService workflowService; + + /** + * 创建工作流 + */ + @PostMapping + public ResponseEntity createWorkflow( + @RequestBody WorkflowDefinition workflow + ) { + WorkflowDefinition created = workflowService.create(workflow); + return ResponseEntity.ok(created); + } + + /** + * 更新工作流 + */ + @PutMapping("/{id}") + public ResponseEntity updateWorkflow( + @PathVariable String id, + @RequestBody WorkflowDefinition workflow + ) { + WorkflowDefinition updated = workflowService.update(id, workflow); + return ResponseEntity.ok(updated); + } + + /** + * 获取工作流详情 + */ + @GetMapping("/{id}") + public ResponseEntity getWorkflow(@PathVariable String id) { + WorkflowDefinition workflow = workflowService.getById(id); + return ResponseEntity.ok(workflow); + } + + /** + * 获取工作流列表 + */ + @GetMapping + public ResponseEntity> listWorkflows( + @RequestParam(required = false) String status + ) { + List workflows = workflowService.list(status); + return ResponseEntity.ok(workflows); + } + + /** + * 删除工作流 + */ + @DeleteMapping("/{id}") + public ResponseEntity deleteWorkflow(@PathVariable String id) { + workflowService.delete(id); + return ResponseEntity.noContent().build(); + } + + /** + * 执行工作流 + */ + @PostMapping("/{id}/execute") + public ResponseEntity executeWorkflow( + @PathVariable String id, + @RequestBody Map input + ) { + WorkflowExecutionResult result = workflowService.execute(id, input); + return ResponseEntity.ok(result); + } + + /** + * 获取执行记录 + */ + @GetMapping("/{id}/executions") + public ResponseEntity> getExecutions( + @PathVariable String id + ) { + List executions = workflowService.getExecutions(id); + return ResponseEntity.ok(executions); + } + + /** + * 获取单个执行详情 + */ + @GetMapping("/executions/{executionId}") + public ResponseEntity getExecutionDetail( + @PathVariable String executionId + ) { + WorkflowExecutionDetail detail = workflowService.getExecutionDetail(executionId); + return ResponseEntity.ok(detail); + } +} +``` + +### 5.2 节点类型 API + +```java +package com.workflow.controller; + +import com.workflow.model.NodeTypeMetadata; +import com.workflow.registry.NodeTypeRegistry; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 节点类型API + */ +@RestController +@RequestMapping("/api/node-types") +public class NodeTypeController { + + @Autowired + private NodeTypeRegistry nodeTypeRegistry; + + /** + * 获取所有节点类型 + */ + @GetMapping + public ResponseEntity> getAllNodeTypes() { + List nodeTypes = nodeTypeRegistry.getAllNodeTypes(); + return ResponseEntity.ok(nodeTypes); + } + + /** + * 获取单个节点类型 + */ + @GetMapping("/{typeId}") + public ResponseEntity getNodeType(@PathVariable String typeId) { + NodeTypeMetadata metadata = nodeTypeRegistry.getMetadata(typeId); + return ResponseEntity.ok(metadata); + } + + /** + * 按分类获取节点类型 + */ + @GetMapping("/category/{category}") + public ResponseEntity> getNodeTypesByCategory( + @PathVariable String category + ) { + List nodeTypes = + nodeTypeRegistry.getNodeTypesByCategory(category); + return ResponseEntity.ok(nodeTypes); + } +} +``` + +### 5.3 审批任务 API + +```java +package com.workflow.controller; + +import com.workflow.model.*; +import com.workflow.service.TaskService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 审批任务API + */ +@RestController +@RequestMapping("/api/tasks") +public class TaskController { + + @Autowired + private TaskService taskService; + + /** + * 获取待办任务列表 + */ + @GetMapping + public ResponseEntity> getTasks( + @RequestParam(required = false) String assignee + ) { + List tasks = taskService.getTasks(assignee); + return ResponseEntity.ok(tasks); + } + + /** + * 获取任务详情 + */ + @GetMapping("/{taskId}") + public ResponseEntity getTaskDetail(@PathVariable String taskId) { + TaskDetail detail = taskService.getTaskDetail(taskId); + return ResponseEntity.ok(detail); + } + + /** + * 完成任务(审批) + */ + @PostMapping("/{taskId}/complete") + public ResponseEntity completeTask( + @PathVariable String taskId, + @RequestBody Map variables + ) { + taskService.complete(taskId, variables); + return ResponseEntity.ok().build(); + } + + /** + * 获取任务表单 + */ + @GetMapping("/{taskId}/form") + public ResponseEntity getTaskForm(@PathVariable String taskId) { + TaskForm form = taskService.getTaskForm(taskId); + return ResponseEntity.ok(form); + } +} +``` + +--- + +## 六、关键实现细节(重要) + +### 6.1 如何确保数据一致性? + +**问题**:工作流执行过程中,如果服务重启怎么办? + +**解决方案**: +```java +1. Flowable自动持久化 + - 流程状态保存在ACT_RU_*表 + - 变量保存在ACT_RU_VARIABLE表 + - 任务保存在ACT_RU_TASK表 + +2. 服务重启后自动恢复 + - Flowable的AsyncExecutor会自动重试未完成的任务 + +3. 幂等性设计 + - 每个节点执行前检查是否已执行 + - 使用节点ID作为幂等键 +``` + +### 6.2 如何处理节点执行超时? + +**实现**: +```java +@Component +public class TimeoutAwareNodeExecutor implements JavaDelegate { + + @Override + public void execute(DelegateExecution execution) { + String nodeId = execution.getCurrentActivityId(); + int timeout = getTimeout(execution); // 从配置获取 + + Future future = executorService.submit(() -> { + return actuallyExecuteNode(execution); + }); + + try { + NodeExecutionResult result = future.get(timeout, TimeUnit.SECONDS); + saveNodeOutput(execution, nodeId, result); + + } catch (TimeoutException e) { + future.cancel(true); + throw new RuntimeException("节点执行超时: " + nodeId); + } + } +} +``` + +### 6.3 如何防止表达式注入攻击? + +**风险**:用户输入 `${Runtime.getRuntime().exec("rm -rf /")}` + +**防护**: +```java +@Service +public class SecureExpressionEngine extends ExpressionEngine { + + // 禁止访问的类 + private static final Set BLOCKED_CLASSES = Set.of( + "Runtime", + "ProcessBuilder", + "System", + "Class", + "ClassLoader" + ); + + @Override + public Object evaluate(String expression, DelegateExecution execution) { + // 1. 检查是否包含危险类 + for (String blocked : BLOCKED_CLASSES) { + if (expression.contains(blocked)) { + throw new SecurityException("表达式包含禁止使用的类: " + blocked); + } + } + + // 2. 限制表达式长度 + if (expression.length() > 1000) { + throw new SecurityException("表达式过长"); + } + + // 3. 正常解析 + return super.evaluate(expression, execution); + } +} +``` + +--- + +## 七、测试策略 + +### 7.1 单元测试 + +```java +@SpringBootTest +public class ExpressionEngineTest { + + @Autowired + private ExpressionEngine expressionEngine; + + @Mock + private DelegateExecution execution; + + @Test + public void testSimpleExpression() { + // 准备上下文 + Map nodes = Map.of( + "node1", Map.of( + "output", Map.of( + "body", Map.of("email", "test@example.com") + ) + ) + ); + when(execution.getVariable("nodes")).thenReturn(nodes); + + // 执行 + String result = (String) expressionEngine.evaluate( + "${nodes.node1.output.body.email}", + execution + ); + + // 验证 + assertEquals("test@example.com", result); + } +} +``` + +### 7.2 集成测试 + +```java +@SpringBootTest +public class WorkflowIntegrationTest { + + @Autowired + private WorkflowService workflowService; + + @Test + public void testCompleteWorkflow() { + // 1. 创建工作流 + WorkflowDefinition workflow = createTestWorkflow(); + WorkflowDefinition created = workflowService.create(workflow); + + // 2. 执行 + Map input = Map.of("username", "testuser"); + WorkflowExecutionResult result = workflowService.execute(created.getId(), input); + + // 3. 验证 + assertEquals("completed", result.getStatus()); + assertNotNull(result.getOutput()); + } +} +``` + +--- + +## 八、部署配置 + +### 8.1 Docker Compose + +```yaml +# docker-compose.yml +version: '3.8' + +services: + mysql: + image: mysql:8 + environment: + MYSQL_DATABASE: workflow_db + MYSQL_ROOT_PASSWORD: root + ports: + - "3306:3306" + command: ["--default-authentication-plugin=mysql_native_password", "--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci"] + volumes: + - mysql_data:/var/lib/mysql + + redis: + image: redis:7 + ports: + - "6379:6379" + + workflow-backend: + build: ./backend + ports: + - "8080:8080" + environment: + SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/workflow_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC + SPRING_DATASOURCE_USERNAME: root + SPRING_DATASOURCE_PASSWORD: root + SPRING_REDIS_HOST: redis + depends_on: + - mysql + - redis + +volumes: + mysql_data: +``` + +### 8.2 生产环境配置 + +```yaml +# application-prod.yml +spring: + datasource: + hikari: + maximum-pool-size: 50 + minimum-idle: 10 + +flowable: + async-executor-core-pool-size: 20 + async-executor-max-pool-size: 100 + +logging: + level: + com.workflow: INFO + org.flowable: WARN +``` + +--- + +**下一步**:查看 03-前端技术设计.md + diff --git a/backend/docs/03-前端技术设计.md b/backend/docs/03-前端技术设计.md new file mode 100644 index 0000000..33fb573 --- /dev/null +++ b/backend/docs/03-前端技术设计.md @@ -0,0 +1,1404 @@ +# 前端技术设计文档 + +**版本**: v1.0 +**关联**: 01-架构总览.md + +--- + +## 一、技术栈详细说明 + +### 1.1 核心依赖 + +```json +{ + "dependencies": { + "react": "^18.2.0", + "react-dom": "^18.2.0", + "typescript": "^5.0.0", + + "reactflow": "^11.10.0", + "antd": "^5.12.0", + "@ant-design/icons": "^5.2.6", + + "zustand": "^4.4.7", + "axios": "^1.6.2", + + "@monaco-editor/react": "^4.6.0", + + "dayjs": "^1.11.10" + }, + "devDependencies": { + "@vitejs/plugin-react": "^4.2.0", + "vite": "^5.0.0", + + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + + "eslint": "^8.55.0", + "prettier": "^3.1.1" + } +} +``` + +### 1.2 项目结构 + +``` +frontend/ +├── src/ +│ ├── api/ # API调用 +│ │ ├── workflow.ts # 工作流API +│ │ ├── nodeType.ts # 节点类型API +│ │ └── task.ts # 审批任务API +│ │ +│ ├── components/ # 通用组件 +│ │ ├── WorkflowEditor/ # 工作流编辑器(核心)⭐ +│ │ │ ├── index.tsx +│ │ │ ├── Canvas.tsx # ReactFlow画布 +│ │ │ ├── NodePalette.tsx # 节点面板 +│ │ │ ├── CustomNode.tsx # 自定义节点样式 +│ │ │ └── MiniMap.tsx # 缩略图 +│ │ │ +│ │ ├── NodeConfigPanel/ # 节点配置面板(核心)⭐⭐ +│ │ │ ├── index.tsx +│ │ │ ├── FieldMappingSelector.tsx # 字段映射选择器⭐⭐⭐ +│ │ │ ├── ExpressionInput.tsx # 表达式输入框 +│ │ │ ├── CodeEditor.tsx # 代码编辑器 +│ │ │ └── DynamicForm.tsx # 动态表单 +│ │ │ +│ │ ├── ExecutionViewer/ # 执行查看器 +│ │ │ ├── index.tsx +│ │ │ ├── LogPanel.tsx # 日志面板 +│ │ │ └── NodeStatus.tsx # 节点状态 +│ │ │ +│ │ └── ApprovalCenter/ # 审批中心 +│ │ ├── index.tsx +│ │ ├── TaskList.tsx # 任务列表 +│ │ └── ApprovalForm.tsx # 审批表单 +│ │ +│ ├── pages/ # 页面 +│ │ ├── WorkflowListPage.tsx # 工作流列表 +│ │ ├── WorkflowEditorPage.tsx # 工作流编辑 +│ │ ├── ExecutionHistoryPage.tsx # 执行历史 +│ │ └── ApprovalCenterPage.tsx # 审批中心 +│ │ +│ ├── store/ # 状态管理(Zustand) +│ │ ├── workflowStore.ts # 工作流状态 +│ │ ├── nodeTypeStore.ts # 节点类型 +│ │ └── executionStore.ts # 执行状态 +│ │ +│ ├── types/ # TypeScript类型定义 +│ │ ├── workflow.ts +│ │ ├── node.ts +│ │ └── execution.ts +│ │ +│ ├── utils/ # 工具函数 +│ │ ├── expressionParser.ts # 表达式解析 +│ │ ├── schemaBuilder.ts # Schema构建 +│ │ └── validation.ts # 表单验证 +│ │ +│ ├── App.tsx +│ ├── main.tsx +│ └── index.css +│ +├── package.json +├── tsconfig.json +├── vite.config.ts +└── README.md +``` + +--- + +## 二、核心组件实现 + +### 2.1 工作流编辑器(WorkflowEditor) + +#### Canvas.tsx - ReactFlow 画布 + +```tsx +import React, { useCallback, useRef } from 'react'; +import ReactFlow, { + Node, + Edge, + Controls, + Background, + MiniMap, + useNodesState, + useEdgesState, + addEdge, + Connection, + NodeTypes, + BackgroundVariant, +} from 'reactflow'; +import 'reactflow/dist/style.css'; + +import CustomNode from './CustomNode'; +import { useWorkflowStore } from '@/store/workflowStore'; + +// 自定义节点类型 +const nodeTypes: NodeTypes = { + custom: CustomNode, +}; + +interface Props { + onNodeClick: (node: Node) => void; +} + +export default function Canvas({ onNodeClick }: Props) { + const reactFlowWrapper = useRef(null); + + // 从store获取状态 + const { nodes, edges, setNodes, setEdges } = useWorkflowStore(); + + // ReactFlow hooks + const [rfNodes, setRfNodes, onNodesChange] = useNodesState(nodes); + const [rfEdges, setRfEdges, onEdgesChange] = useEdgesState(edges); + + /** + * 连线处理(重要)⭐ + * + * 验证规则: + * 1. 不能连接到自己 + * 2. 两个节点间只能有一条连线 + * 3. 不能形成环(可选) + */ + const onConnect = useCallback( + (connection: Connection) => { + // 验证:不能连接到自己 + if (connection.source === connection.target) { + message.warning('节点不能连接到自己'); + return; + } + + // 验证:检查是否已存在连线 + const existingEdge = rfEdges.find( + (edge) => + edge.source === connection.source && + edge.target === connection.target + ); + + if (existingEdge) { + message.warning('这两个节点已经连接'); + return; + } + + // 添加连线 + const newEdge = { + ...connection, + id: `edge-${connection.source}-${connection.target}`, + type: 'smoothstep', + animated: true, + }; + + setRfEdges((eds) => addEdge(newEdge, eds)); + setEdges(addEdge(newEdge, edges)); + }, + [rfEdges, edges, setEdges, setRfEdges] + ); + + /** + * 节点拖拽结束(更新位置) + */ + const onNodeDragStop = useCallback( + (event: React.MouseEvent, node: Node) => { + setNodes( + nodes.map((n) => + n.id === node.id + ? { ...n, position: node.position } + : n + ) + ); + }, + [nodes, setNodes] + ); + + /** + * 从节点面板拖拽到画布(重要)⭐⭐ + */ + const onDrop = useCallback( + (event: React.DragEvent) => { + event.preventDefault(); + + const reactFlowBounds = reactFlowWrapper.current?.getBoundingClientRect(); + const nodeTypeData = event.dataTransfer.getData('application/reactflow'); + + if (!nodeTypeData || !reactFlowBounds) { + return; + } + + const nodeType = JSON.parse(nodeTypeData); + + // 计算放置位置 + const position = { + x: event.clientX - reactFlowBounds.left, + y: event.clientY - reactFlowBounds.top, + }; + + // 创建新节点 + const newNode: Node = { + id: `node-${Date.now()}`, + type: 'custom', + position, + data: { + type: nodeType.id, + name: nodeType.displayName, + icon: nodeType.icon, + config: {}, // 空配置,等待用户填写 + }, + }; + + setRfNodes((nds) => nds.concat(newNode)); + setNodes([...nodes, newNode]); + }, + [nodes, setNodes, setRfNodes] + ); + + const onDragOver = useCallback((event: React.DragEvent) => { + event.preventDefault(); + event.dataTransfer.dropEffect = 'move'; + }, []); + + return ( +
+ onNodeClick(node)} + onNodeDragStop={onNodeDragStop} + fitView + snapToGrid + snapGrid={[15, 15]} + > + + + + +
+ ); +} +``` + +#### CustomNode.tsx - 自定义节点样式 + +```tsx +import React, { memo } from 'react'; +import { Handle, Position, NodeProps } from 'reactflow'; +import { Card, Tag, Typography } from 'antd'; +import * as Icons from '@ant-design/icons'; + +const { Text } = Typography; + +/** + * 自定义节点组件 + * + * 功能: + * 1. 显示节点图标和名称 + * 2. 显示节点状态(执行中、成功、失败) + * 3. 连接点(上下左右) + */ +function CustomNode({ data, selected }: NodeProps) { + // 动态获取图标 + const IconComponent = (Icons as any)[data.icon] || Icons.ApiOutlined; + + // 节点状态颜色 + const getStatusColor = () => { + switch (data.status) { + case 'running': + return '#1890ff'; + case 'success': + return '#52c41a'; + case 'failed': + return '#ff4d4f'; + default: + return '#d9d9d9'; + } + }; + + return ( +
+ {/* 连接点 - 上 */} + + + {/* 节点内容 */} +
+
+ + {data.name} +
+ + + {data.type} + + + {/* 状态标签 */} + {data.status && ( +
+ + {data.status === 'running' && '执行中'} + {data.status === 'success' && '成功'} + {data.status === 'failed' && '失败'} + +
+ )} +
+ + {/* 连接点 - 下 */} + +
+ ); +} + +export default memo(CustomNode); +``` + +#### NodePalette.tsx - 节点面板 + +```tsx +import React, { useEffect } from 'react'; +import { Card, Collapse, Space, Typography, Empty, Spin } from 'antd'; +import * as Icons from '@ant-design/icons'; +import { useNodeTypeStore } from '@/store/nodeTypeStore'; + +const { Panel } = Collapse; +const { Text } = Typography; + +/** + * 节点面板 + * + * 功能: + * 1. 显示所有可用节点类型 + * 2. 按分类分组 + * 3. 支持拖拽到画布 + */ +export default function NodePalette() { + const { nodeTypes, loading, fetchNodeTypes } = useNodeTypeStore(); + + useEffect(() => { + fetchNodeTypes(); + }, [fetchNodeTypes]); + + /** + * 开始拖拽(重要)⭐ + */ + const onDragStart = (event: React.DragEvent, nodeType: NodeTypeMetadata) => { + event.dataTransfer.setData( + 'application/reactflow', + JSON.stringify(nodeType) + ); + event.dataTransfer.effectAllowed = 'move'; + }; + + // 按分类分组 + const groupedNodeTypes = nodeTypes.reduce((acc, nodeType) => { + const category = nodeType.category || 'other'; + if (!acc[category]) { + acc[category] = []; + } + acc[category].push(nodeType); + return acc; + }, {} as Record); + + // 分类显示名称 + const categoryNames: Record = { + api: 'API', + database: '数据库', + logic: '逻辑控制', + notification: '通知', + transform: '数据转换', + other: '其他', + }; + + if (loading) { + return ( +
+ +
+ ); + } + + return ( +
+ + 节点库 + + + {Object.keys(groupedNodeTypes).length === 0 ? ( + + ) : ( + + {Object.entries(groupedNodeTypes).map(([category, nodes]) => ( + + + {nodes.map((nodeType) => { + const IconComponent = (Icons as any)[nodeType.icon] || Icons.ApiOutlined; + + return ( + onDragStart(e, nodeType)} + style={{ + cursor: 'grab', + borderRadius: 8, + }} + > + + +
+ + {nodeType.displayName} + +
+ + {nodeType.description} + +
+
+
+ ); + })} +
+
+ ))} +
+ )} +
+ ); +} +``` + +### 2.2 节点配置面板(最重要)⭐⭐⭐ + +#### index.tsx - 主组件 + +```tsx +import React, { useMemo } from 'react'; +import { Form, Input, Select, Button, Space, Typography, Divider } from 'antd'; +import { Node, Edge } from 'reactflow'; +import { NodeTypeMetadata, FieldDefinition } from '@/types/node'; +import FieldMappingSelector from './FieldMappingSelector'; +import ExpressionInput from './ExpressionInput'; +import CodeEditor from './CodeEditor'; + +const { Title, Text } = Typography; + +interface Props { + node: Node | null; + nodes: Node[]; + edges: Edge[]; + nodeTypes: Record; + onConfigChange: (nodeId: string, config: any) => void; + onClose: () => void; +} + +/** + * 节点配置面板 + * + * 核心职责: + * 1. 根据节点类型动态生成表单 + * 2. 提供字段映射选择器(从上游节点选择输出字段)⭐⭐⭐ + * 3. 支持表达式输入 + * 4. 实时保存配置 + */ +export default function NodeConfigPanel({ + node, + nodes, + edges, + nodeTypes, + onConfigChange, + onClose, +}: Props) { + const [form] = Form.useForm(); + + if (!node) { + return ( +
+ 请选择一个节点 +
+ ); + } + + // 获取节点类型元数据 + const nodeMetadata = nodeTypes[node.data.type]; + + if (!nodeMetadata) { + return
节点类型未找到: {node.data.type}
; + } + + // 计算上游节点(重要)⭐⭐ + const upstreamNodes = useMemo(() => { + // 找到所有指向当前节点的边 + const incomingEdges = edges.filter((edge) => edge.target === node.id); + + // 获取上游节点的详细信息 + return incomingEdges + .map((edge) => nodes.find((n) => n.id === edge.source)) + .filter((n): n is Node => n !== undefined); + }, [node.id, nodes, edges]); + + /** + * 表单值变化时保存 + */ + const handleValuesChange = (changedValues: any, allValues: any) => { + onConfigChange(node.id, allValues); + }; + + /** + * 根据字段类型渲染输入组件(关键)⭐⭐⭐ + */ + const renderFieldInput = (field: FieldDefinition) => { + // 1. 字段映射选择器(最核心)⭐⭐⭐ + if (field.supportsFieldMapping) { + return ( + + ); + } + + // 2. 表达式输入框 + if (field.supportsExpression) { + return ( + + ); + } + + // 3. 代码编辑器 + if (field.type === 'code') { + return ( + + ); + } + + // 4. 下拉选择 + if (field.type === 'select') { + return ( + + ); + } + + // 5. 数字输入 + if (field.type === 'number') { + return ( + + ); + } + + // 6. 多行文本 + if (field.type === 'textarea') { + return ( + + ); + } + + // 7. 默认:单行文本 + return ( + + ); + }; + + return ( +
+ {/* 标题 */} +
+ {node.data.name} + {nodeMetadata.description} +
+ + + + {/* 动态表单 */} +
+ {nodeMetadata.fields.map((field) => ( + + {field.label} + {field.required && *} + {field.supportsExpression && ( + + (支持表达式) + + )} + + } + rules={[ + { + required: field.required, + message: `请输入${field.label}`, + }, + ]} + tooltip={field.description} + > + {renderFieldInput(field)} + + ))} +
+ + {/* 上游节点信息 */} + {upstreamNodes.length > 0 && ( +
+ + 可引用的上游节点: + +
+ {upstreamNodes.map((upstream) => ( + + {upstream.data.name} + + ))} +
+
+ )} + + {/* 底部操作 */} +
+ + + +
+
+ ); +} +``` + +#### FieldMappingSelector.tsx - 字段映射选择器(核心)⭐⭐⭐ + +```tsx +import React, { useState, useMemo } from 'react'; +import { TreeSelect, Input, Space, Button, Tag, Tooltip } from 'antd'; +import { FunctionOutlined, EditOutlined } from '@ant-design/icons'; +import { Node } from 'reactflow'; +import { NodeTypeMetadata, FieldDefinition } from '@/types/node'; + +interface Props { + field: FieldDefinition; + upstreamNodes: Node[]; + nodeTypes: Record; + value?: string; + onChange?: (value: string) => void; + placeholder?: string; +} + +/** + * 字段映射选择器(最核心的组件)⭐⭐⭐ + * + * 功能: + * 1. 显示上游节点的输出字段树形结构 + * 2. 用户可以选择字段,自动生成表达式 + * 3. 也可以手动输入表达式 + * + * 示例: + * 用户选择:nodes.httpRequest.output.body.email + * 生成表达式:${nodes.httpRequest.output.body.email} + */ +export default function FieldMappingSelector({ + field, + upstreamNodes, + nodeTypes, + value, + onChange, + placeholder, +}: Props) { + const [mode, setMode] = useState<'select' | 'expression'>('select'); + + /** + * 构建字段树(核心逻辑)⭐⭐⭐ + * + * 根据上游节点的outputSchema构建树形选择结构 + */ + const fieldTree = useMemo(() => { + return upstreamNodes.map((node) => { + const nodeType = nodeTypes[node.data.type]; + + if (!nodeType?.outputSchema) { + return null; + } + + return { + title: ( + + + {node.data.name || node.id} + + + {nodeType.displayName} + + + ), + value: `nodes.${node.id}`, + selectable: false, + children: buildFieldTree( + nodeType.outputSchema.properties || {}, + `nodes.${node.id}.output`, + 0 + ), + }; + }).filter(Boolean); + }, [upstreamNodes, nodeTypes]); + + // 选择模式 + if (mode === 'select') { + return ( + + onChange?.(`\${${val}}`)} // 添加 ${} + treeData={fieldTree} + placeholder={placeholder || '选择上游节点的字段'} + showSearch + treeDefaultExpandAll + dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} + treeLine={{ showLeafIcon: false }} + filterTreeNode={(input, treeNode) => { + return (treeNode.title as string) + .toLowerCase() + .includes(input.toLowerCase()); + }} + /> + + - - - - 输入会自动规范为 ${'{...}'} 格式(仅做静态预览,不在前端求值) - - - ) -} diff --git a/frontend/src/components/common/FieldMappingSelector.tsx b/frontend/src/components/common/FieldMappingSelector.tsx deleted file mode 100644 index 43483e0..0000000 --- a/frontend/src/components/common/FieldMappingSelector.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useMemo } from 'react' -import { Button, Space, TreeSelect, Typography } from 'antd' -import type { Node } from 'reactflow' -import type { NodeTypeMetadata } from '@/types/node' - -function buildTreeFromSchema(schema: any, prefix: string, depth = 0): any[] { - if (!schema || depth > 3) return [] - const props = schema.properties || {} - return Object.keys(props).map((key) => { - const cur = props[key] - const value = `${prefix}.${key}` - if (cur?.type === 'object') { - return { - title: `${key} (object)`, - value, - children: buildTreeFromSchema(cur, value, depth + 1), - } - } - if (cur?.type === 'array') { - const children: any[] = [] - if (cur?.items?.properties) { - children.push({ - title: `[0] (item)`, - value: `${value}[0]`, - children: buildTreeFromSchema(cur.items, `${value}[0]`, depth + 1), - }) - } - children.push({ title: `[*] (all)`, value: `${value}[*]` }) - return { title: `${key} (array)`, value, selectable: false, children } - } - return { title: key, value } - }) -} - -export default function FieldMappingSelector({ - upstreamNodes, - nodeTypes, - value, - onChange, -}: { - upstreamNodes: Node[] - nodeTypes: Record - value?: string - onChange?: (v: string) => void -}) { - const treeData = useMemo(() => { - return upstreamNodes.map((n) => { - const meta = nodeTypes[n.data?.type as string] - if (!meta?.outputSchema) return null - return { - title: n.data?.name || n.id, - value: `nodes.${n.id}`, - selectable: false, - children: buildTreeFromSchema(meta.outputSchema, `nodes.${n.id}.output`), - } - }).filter(Boolean) as any[] - }, [upstreamNodes, nodeTypes]) - - const plain = value?.replace(/^\$\{|\}$/g, '') - const copy = async () => { - if (!plain) return - try { await navigator.clipboard.writeText(plain) } catch { /* ignore */ } - } - return ( - - onChange?.(val ? `\${${val}}` : '')} - allowClear - /> - - 当前路径: - {plain || '-'} - - - - - ) -} diff --git a/frontend/src/components/common/JsonView.tsx b/frontend/src/components/common/JsonView.tsx deleted file mode 100644 index 157c2ac..0000000 --- a/frontend/src/components/common/JsonView.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react' -import { Button, Space, Typography } from 'antd' - -export default function JsonView({ data, height = 240 }: { data: any; height?: number }) { - const copy = async () => { - try { - await navigator.clipboard.writeText(JSON.stringify(data, null, 2)) -} catch { - // ignore - } - } - return ( -
- - JSON 视图 - - -
{JSON.stringify(data, null, 2)}
-
- ) -} \ No newline at end of file diff --git a/frontend/src/components/common/KeyValueEditor.tsx b/frontend/src/components/common/KeyValueEditor.tsx deleted file mode 100644 index 084ab3a..0000000 --- a/frontend/src/components/common/KeyValueEditor.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import { Button, Input, Space } from 'antd' - -export default function KeyValueEditor({ value = {}, onChange }: { value?: Record; onChange?: (v: Record) => void }) { - const entries = Object.entries(value) - const update = (k: string, newK: string, newV: string) => { - const next: Record = {} - entries.forEach(([key, val]) => { - if (key === k) next[newK] = newV - else next[key] = val - }) - onChange?.(next) - } - const add = () => onChange?.({ ...value, [`key_${entries.length + 1}`]: '' }) - const remove = (k: string) => { - const next = { ...value } - delete next[k] - onChange?.(next) - } - return ( - - {entries.map(([k, v]) => ( - - update(k, e.target.value, v)} style={{ width: 160 }} /> - update(k, k, e.target.value)} style={{ flex: 1 }} /> - - - ))} - - - ) -} \ No newline at end of file diff --git a/frontend/src/components/common/Loading.tsx b/frontend/src/components/common/Loading.tsx deleted file mode 100644 index 175dce8..0000000 --- a/frontend/src/components/common/Loading.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { Spin } from 'antd' - -export default function Loading({ height = 120 }: { height?: number }) { - return ( -
- -
- ) -} diff --git a/frontend/src/components/editor/Canvas.tsx b/frontend/src/components/editor/Canvas.tsx deleted file mode 100644 index 7fb8a2c..0000000 --- a/frontend/src/components/editor/Canvas.tsx +++ /dev/null @@ -1,224 +0,0 @@ -import React, { useCallback, useEffect, useMemo, useRef } from 'react' -import ReactFlow, { -Background, - BackgroundVariant, - MiniMap, - Controls, - Edge, - Node, - addEdge, - Connection, - useEdgesState, - useNodesState, -} from 'reactflow' -import 'reactflow/dist/style.css' -import { message } from 'antd' -import { useEditorStore } from '@/store/editorStore' -import CustomNode from '@/components/editor/CustomNode' -import ConditionNode from '@/components/editor/ConditionNode' - -const nodeTypes = { custom: CustomNode, condition: ConditionNode } - -export default function Canvas() { - const wrapperRef = useRef(null) - - // 全局 store 的 nodes/edges 作为单一数据源 - const { nodes, edges, setNodes, setEdges, setSelectedNode, setSelectedEdge } = useEditorStore() - - // ReactFlow 本地 state 与全局同步 - const [rfNodes, setRfNodes, onNodesChange] = useNodesState(nodes) - const [rfEdges, setRfEdges, onEdgesChange] = useEdgesState(edges) - - // 将 ReactFlow 本地状态与全局 store 双向同步,避免 handler 中闭包拿到旧值 - useEffect(() => { setNodes(rfNodes) }, [rfNodes, setNodes]) - useEffect(() => { setEdges(rfEdges) }, [rfEdges, setEdges]) - - const syncNodes = useCallback( - (updater: (nds: Node[]) => Node[]) => { - setRfNodes(updater) - }, - [setRfNodes] - ) - - const syncEdges = useCallback( - (updater: (eds: Edge[]) => Edge[]) => { - setRfEdges(updater) - }, - [setRfEdges] - ) - - const hasPath = (from: string, to: string, list: Edge[]) => { - const adj: Record = {} - list.forEach((e) => { - adj[e.source] = adj[e.source] || [] - adj[e.source].push(e.target) - }) - const q = [from] - const vis = new Set() - while (q.length) { - const u = q.shift()! - if (u === to) return true - if (vis.has(u)) continue - vis.add(u) - ;(adj[u] || []).forEach((v) => q.push(v)) - } - return false - } - - const onConnect = useCallback( - (connection: Connection) => { - if (!connection.source || !connection.target) return - if (connection.source === connection.target) { - message.warning('不能连接到自身') - return - } - const duplicated = rfEdges.some( - (e) => e.source === connection.source && e.target === connection.target - ) - if (duplicated) { - message.warning('这两个节点已连接') - return - } - // 环检测:如果存在 target -> source 的路径,则形成环 - const virtual = rfEdges.concat([{ id: 'v', source: connection.source!, target: connection.target! } as any]) - if (hasPath(connection.target!, connection.source!, virtual)) { - message.warning('不允许形成环') - return - } - const label = connection.sourceHandle === 'true' ? 'true' : connection.sourceHandle === 'false' ? 'false' : undefined - const color = label === 'true' ? '#52c41a' : label === 'false' ? '#ff4d4f' : '#999999' - const newEdge: Edge = { - id: `edge-${connection.source}-${connection.target}-${Date.now()}`, - source: connection.source!, - target: connection.target!, - sourceHandle: connection.sourceHandle ?? null, - targetHandle: connection.targetHandle ?? null, - type: 'smoothstep', - animated: false, - label: label ? ({label}) as any : undefined, - style: { stroke: color } as any, - labelStyle: { fill: '#000', fontSize: 12 } as any, - labelShowBg: true as any, - labelBgStyle: { fill: '#fff', fillOpacity: 0.85, stroke: '#d9d9d9' } as any, - markerEnd: { type: 'arrowclosed' } as any, - } - syncEdges((eds) => addEdge(newEdge, eds)) - }, - [rfEdges, syncEdges] - ) - - const onDrop = useCallback( - (event: React.DragEvent) => { - event.preventDefault() - const bounds = wrapperRef.current?.getBoundingClientRect() - const data = event.dataTransfer.getData('application/reactflow') - if (!data || !bounds) return - try { - const nodeType = JSON.parse(data) as { id: string; displayName: string; icon?: string } - const position = { - x: event.clientX - bounds.left, - y: event.clientY - bounds.top, - } - const id = `node-${Date.now()}` - const isCondition = nodeType.id === 'condition' - const newNode: Node = { - id, - type: isCondition ? 'condition' : 'custom', - position, - data: { - type: nodeType.id, - name: nodeType.displayName || nodeType.id, - icon: nodeType.icon, - status: undefined, - config: {}, - }, - } - syncNodes((nds) => nds.concat(newNode)) - } catch (e) { - console.error(e) - } - }, - [syncNodes] - ) - - const onDragOver = useCallback((event: React.DragEvent) => { - event.preventDefault() - event.dataTransfer.dropEffect = 'move' - }, []) - - // 初次渲染将全局 nodes/edges 注入 RF 本地 - const initialNodes = useMemo(() => nodes, [nodes]) - const initialEdges = useMemo(() => edges, [edges]) - - useEffect(() => { - const handler = (e: KeyboardEvent) => { - const key = e.key.toLowerCase() - // 删除 - if (key === 'delete' || key === 'backspace') { - setRfNodes(rfNodes.filter((n) => n.selected !== true)) - setRfEdges(rfEdges.filter((ed) => (ed as any).selected !== true)) - } - // 复制(Cmd/Ctrl + D) - const isMac = navigator.platform.toLowerCase().includes('mac') - const dupCombo = (isMac && e.metaKey && key === 'd') || (!isMac && e.ctrlKey && key === 'd') - if (dupCombo) { - e.preventDefault() - const selected = rfNodes.filter((n) => n.selected) - if (selected.length) { - const clones = selected.map((n) => ({ - ...n, - id: `${n.id}-copy-${Date.now()}`, - position: { x: n.position.x + 20, y: n.position.y + 20 }, - selected: false, - })) - setRfNodes([...rfNodes, ...clones]) - } - } - // 全选(Cmd/Ctrl + A) - const selectAllCombo = (isMac && e.metaKey && key === 'a') || (!isMac && e.ctrlKey && key === 'a') - if (selectAllCombo) { - e.preventDefault() - setRfNodes(rfNodes.map((n) => ({ ...n, selected: true }))) - } - } - const esc = (e: KeyboardEvent) => { if (e.key === 'Escape') { setSelectedNode(undefined); setSelectedEdge(undefined) } } - window.addEventListener('keydown', handler) - window.addEventListener('keydown', esc) - return () => { window.removeEventListener('keydown', handler); window.removeEventListener('keydown', esc) } - }, [rfNodes, rfEdges, setNodes, setEdges, setSelectedNode, setSelectedEdge]) - - return ( -
- { onNodesChange(chs) }} - onEdgesChange={(chs) => { onEdgesChange(chs) }} - onSelectionChange={(sel) => { - const n = sel.nodes?.[0] - const e = sel.edges?.[0] - if (n) setSelectedNode(n.id) - else if (e) setSelectedEdge(e.id) - else { setSelectedNode(undefined); setSelectedEdge(undefined) } - }} - onConnect={onConnect} - onNodeClick={(_, node) => setSelectedNode(node.id)} - onEdgeClick={(_, edge) => setSelectedEdge(edge.id)} - nodeTypes={nodeTypes} - fitView - snapToGrid - snapGrid={[8, 8]} - > - - - - -
- ) -} diff --git a/frontend/src/components/editor/ConditionNode.tsx b/frontend/src/components/editor/ConditionNode.tsx deleted file mode 100644 index ac21459..0000000 --- a/frontend/src/components/editor/ConditionNode.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import React, { memo } from 'react' -import { Handle, NodeProps, Position } from 'reactflow' -import * as Icons from '@ant-design/icons' - -function ConditionNode({ data, selected }: NodeProps) { - const IconComponent = (Icons as any)['BranchesOutlined'] || (Icons as any).ApiOutlined - const borderColor = selected ? '#1677ff' : '#d9d9d9' - - return ( -
-
-
- -
{data?.name || '条件判断'}
-
-
- {/* 输入 */} - - {/* 输出 true / false */} - - -
- ) -} - -export default memo(ConditionNode) diff --git a/frontend/src/components/editor/CustomNode.tsx b/frontend/src/components/editor/CustomNode.tsx deleted file mode 100644 index 4c97574..0000000 --- a/frontend/src/components/editor/CustomNode.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { memo } from 'react' -import { Handle, NodeProps, Position } from 'reactflow' -import * as Icons from '@ant-design/icons' - -function CustomNode({ data, selected }: NodeProps) { - const IconComponent = (Icons as any)[data?.icon || 'ApiOutlined'] || (Icons as any).ApiOutlined - const borderColor = selected ? '#1677ff' : '#d9d9d9' - - return ( -
- -
-
- -
{data?.name || '节点'}
-
-
{data?.type}
-
- -
- ) -} - -export default memo(CustomNode) diff --git a/frontend/src/components/editor/EdgePropertyPanel.tsx b/frontend/src/components/editor/EdgePropertyPanel.tsx deleted file mode 100644 index 803dda0..0000000 --- a/frontend/src/components/editor/EdgePropertyPanel.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import React, { useEffect } from 'react' -import { Drawer, Form, Button, Space, message } from 'antd' -import { useEditorStore } from '@/store/editorStore' -import ExpressionInput from '@/components/common/ExpressionInput' - -function ensureExpr(v?: string) { - if (!v) return '' - const t = String(v).trim() - if (t.startsWith('${') && t.endsWith('}')) return t - return `\${${t}}` -} - -export default function EdgePropertyPanel() { - const { selectedEdgeId, edges, updateEdgeData, updateEdgeProps, setSelectedEdge } = useEditorStore() - const [form] = Form.useForm() - const edge = edges.find((e) => e.id === selectedEdgeId) - - useEffect(() => { - if (edge) { - const cond = (edge as any).data?.condition || (edge as any).label || '' - form.setFieldsValue({ condition: cond }) - } else { - form.resetFields() - } - }, [edge, form]) - - const onSave = async () => { - const v = await form.validateFields() - const expr = ensureExpr(v.condition) - const plain = expr.replace(/^\$\{|\}$/g, '') - // 数据层:持久条件 - updateEdgeData(edge!.id, { condition: expr }) - // 视图层:顶层属性(label/style) - const isTrue = /^\s*true\s*$/i.test(plain) - const isFalse = /^\s*false\s*$/i.test(plain) - const color = isTrue ? '#52c41a' : isFalse ? '#ff4d4f' : '#999999' - updateEdgeProps(edge!.id, { - label: ({plain}) as any, - style: { stroke: color } as any, - labelStyle: { fill: '#000', fontSize: 12 } as any, - labelShowBg: true as any, - labelBgStyle: { fill: '#fff', fillOpacity: 0.85, stroke: '#d9d9d9' } as any, - }) - message.success('已更新边条件') - } - - return ( - setSelectedEdge(undefined)} width={360} destroyOnClose> - {!edge ? null : ( -
- - - - - - - )} -
- ) -} diff --git a/frontend/src/components/editor/NodePalette.tsx b/frontend/src/components/editor/NodePalette.tsx deleted file mode 100644 index 94bb5de..0000000 --- a/frontend/src/components/editor/NodePalette.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useEffect } from 'react' -import { Card, Collapse, Space, Typography, Empty, Spin } from 'antd' -import * as Icons from '@ant-design/icons' -import { useNodeTypeStore } from '@/store/nodeTypeStore' - -const { Panel } = Collapse -const { Text } = Typography - -// 内置降级节点(在后端不可用时显示) -const fallbackNodeTypes = [ - { id: 'http_request', displayName: 'HTTP Request', category: 'api', icon: 'ApiOutlined', description: '发送 HTTP 请求' }, - { id: 'condition', displayName: '条件判断', category: 'logic', icon: 'BranchesOutlined', description: '基于表达式的条件分支' }, - { id: 'approval', displayName: '审批', category: 'logic', icon: 'CheckCircleOutlined', description: 'User Task 审批' }, - { id: 'send_mail', displayName: '发送邮件', category: 'notification', icon: 'MailOutlined', description: '发送通知邮件' }, -] - -export default function NodePalette() { - const { items, loading, fetch } = useNodeTypeStore() - - useEffect(() => { - fetch().catch(() => void 0) - }, [fetch]) - - const nodeTypes = items.length ? items.map((n: any) => ({ - id: n.id, - displayName: n.displayName || n.name || n.id, - category: n.category || 'other', - icon: n.icon || 'ApiOutlined', - description: n.description || '', - })) : fallbackNodeTypes - - const grouped = nodeTypes.reduce((acc: Record, it) => { - acc[it.category] = acc[it.category] || [] - acc[it.category].push(it) - return acc - }, {}) - - const onDragStart = (event: React.DragEvent, nodeType: any) => { - event.dataTransfer.setData('application/reactflow', JSON.stringify(nodeType)) - event.dataTransfer.effectAllowed = 'move' - } - - if (loading && items.length === 0) { - return
- } - - if (Object.keys(grouped).length === 0) { - return - } - - return ( -
- 节点库 - - {Object.entries(grouped).map(([category, arr]) => ( - - - {arr.map((nt: any) => { - const IconComponent = (Icons as any)[nt.icon] || (Icons as any).ApiOutlined - return ( - onDragStart(e, nt)} - data-testid={`palette-card-${nt.id}`} - > - - -
- {nt.displayName} -
{nt.description}
-
-
-
- ) - })} -
-
- ))} -
-
- ) -} diff --git a/frontend/src/components/editor/NodePropertyPanel.tsx b/frontend/src/components/editor/NodePropertyPanel.tsx deleted file mode 100644 index da6da12..0000000 --- a/frontend/src/components/editor/NodePropertyPanel.tsx +++ /dev/null @@ -1,146 +0,0 @@ -import React, { useEffect, useMemo } from 'react' -import { Drawer, Form, Input, Select, InputNumber, Button, Space, Divider, Switch } from 'antd' -import { useEditorStore } from '@/store/editorStore' -import { useNodeTypeStore } from '@/store/nodeTypeStore' -import ExpressionInput from '@/components/common/ExpressionInput' -import FieldMappingSelector from '@/components/common/FieldMappingSelector' -import CodeEditor from '@/components/common/CodeEditor' -import KeyValueEditor from '@/components/common/KeyValueEditor' - -const fallbackFields: Record = { - http_request: [ - { name: 'url', label: 'URL', type: 'text', required: true }, - { name: 'method', label: 'Method', type: 'select', options: ['GET', 'POST', 'PUT', 'DELETE'], defaultValue: 'GET' }, - { name: 'timeout', label: 'Timeout(ms)', type: 'number', defaultValue: 30000 }, - ], - condition: [ - { name: 'expression', label: '表达式', type: 'text', required: true }, - ], - approval: [ - { name: 'assignee', label: '审批人', type: 'text', required: true }, - ], - send_mail: [ - { name: 'to', label: '收件人', type: 'text', required: true }, - { name: 'subject', label: '主题', type: 'text', required: true }, - ], -} - -export default function NodePropertyPanel() { - const { selectedNodeId, nodes, edges, updateNodeData, setSelectedNode } = useEditorStore() - const { items } = useNodeTypeStore() - const [form] = Form.useForm() - - const node = useMemo(() => nodes.find((n) => n.id === selectedNodeId), [nodes, selectedNodeId]) - const nodeTypeId = node?.data?.type as string | undefined - - const fields = useMemo(() => { - const meta = items.find((i: any) => i.id === nodeTypeId) - return meta?.fields || fallbackFields[nodeTypeId || ''] || [] - }, [items, nodeTypeId]) - - const upstreamNodes = useMemo(() => { - if (!node) return [] - const incoming = edges.filter((e) => e.target === node.id).map((e) => e.source) - return nodes.filter((n) => incoming.includes(n.id)) - }, [edges, nodes, node]) - - useEffect(() => { - if (node) { - form.setFieldsValue({ name: node.data?.name, ...node.data?.config }) - } else { - form.resetFields() - } - }, [node, form]) - - const onClose = () => setSelectedNode(undefined) - - const onSubmit = async () => { - const values = await form.validateFields() - updateNodeData(node!.id, { name: values.name, config: { ...values, name: undefined } }) - } - - return ( - - {!node ? null : ( -
- - - - - - {fields.map((f) => { - const rules: any[] = [] - if (f.required) rules.push({ required: true, message: `请输入${f.label}` }) - if (f.minLength) rules.push({ min: f.minLength, message: `${f.label} 最小长度 ${f.minLength}` }) - if (f.maxLength) rules.push({ max: f.maxLength, message: `${f.label} 最大长度 ${f.maxLength}` }) - if (f.pattern) rules.push({ pattern: new RegExp(f.pattern), message: `${f.label} 格式不正确` }) - - // 字段映射优先 - if (f.supportsFieldMapping) { - return ( - - [i.id, i])) as any} /> - - ) - } - // 表达式输入 - if (f.supportsExpression) { - return ( - - ) - } - // 代码编辑 - if (f.type === 'code') { - return ( - - - - ) - } - // 键值对 - if (f.type === 'key_value') { - return ( - - - - ) - } - // 选择/数字/布尔 - if (f.type === 'select') { - return ( - - setAssignee(e.target.value)} /> - - - - {loading ? ( - - ) : err ? ( - - ) : items.length === 0 ? ( - - ) : ( - r.id} - dataSource={items} - pagination={{ current: page, pageSize: size, total, onChange: (p, s) => load(p, s) }} - columns={[ - { title: '任务ID', dataIndex: 'id', width: 220 }, - { title: '名称', dataIndex: 'name', width: 200 }, - { title: '办理人', dataIndex: 'assignee', width: 160 }, - { title: '实例ID', dataIndex: 'processInstanceId', width: 220 }, - { title: '操作', key: 'op', width: 120, render: (_, r) => }, - ]} - scroll={{ x: 900 }} - /> - )} - - setModalOpen(false)} onOk={submit} destroyOnClose> - {!formDef ? null : ( - - {formDef.fields.map(renderField)} - - )} - - - ) -} diff --git a/frontend/src/pages/ExecutionHistoryPage.tsx b/frontend/src/pages/ExecutionHistoryPage.tsx deleted file mode 100644 index f9f5ff2..0000000 --- a/frontend/src/pages/ExecutionHistoryPage.tsx +++ /dev/null @@ -1,173 +0,0 @@ -import React, { useMemo, useState } from 'react' -import { Typography, Space, Input, Button, Table, Tag, Drawer, Descriptions, message, Timeline, Select } from 'antd' -import { workflowApi } from '@/api/workflow' -import type { WorkflowExecutionRecord, WorkflowExecutionDetail } from '@/types/workflow' -import Loading from '@/components/common/Loading' -import Empty from '@/components/common/Empty' -import ErrorBlock from '@/components/common/Error' -import JsonView from '@/components/common/JsonView' - -export default function ExecutionHistoryPage() { - const [workflowId, setWorkflowId] = useState('') - const [loading, setLoading] = useState(false) - const [records, setRecords] = useState([]) - const [total, setTotal] = useState(0) - const [page, setPage] = useState(1) - const [size, setSize] = useState(10) - - const [detailOpen, setDetailOpen] = useState(false) - const [detail, setDetail] = useState(null) - const [statusFilter, setStatusFilter] = useState(undefined) - const [detailLoading, setDetailLoading] = useState(false) - const [err, setErr] = useState(null) - - const load = async (p = page, s = size) => { - if (!workflowId?.trim()) { - return message.warning('请输入工作流ID') - } - setLoading(true) - setErr(null) - try { - const res = await workflowApi.getExecutions(workflowId.trim(), p, s) - setRecords(res.items) - setTotal(res.total) - setPage(res.page) - setSize(res.size) - } catch (e: any) { - setErr(e?.message || '加载失败') - message.error(`加载失败: ${e?.message || e}`) - } finally { - setLoading(false) - } - } - -const [nodeDetailOpen, setNodeDetailOpen] = useState(false) - const [nodeDetail, setNodeDetail] = useState(null) - - const viewDetail = async (executionId: string) => { - setDetailOpen(true) - setDetailLoading(true) - try { - const d = await workflowApi.getExecutionDetail(executionId) - setDetail(d) - // 自动定位第一个失败节点 - const entries = Object.entries(d.nodes || {}) - const failed = entries.find(([, v]) => v.status === 'failed') - if (failed) { - setNodeDetail(failed[1]) - setNodeDetailOpen(true) - } - } catch (e: any) { - message.error(`详情获取失败: ${e?.message || e}`) - } finally { - setDetailLoading(false) - } - } - - return ( -
- 执行历史 - - setWorkflowId(e.target.value)} /> -
r.id} - dataSource={statusFilter ? records.filter((r) => r.status === statusFilter) : records} - pagination={{ current: page, pageSize: size, total, onChange: (p, s) => load(p, s) }} - columns={[ - { title: '执行ID', dataIndex: 'id', width: 220 }, - { title: '流程实例ID', dataIndex: 'processInstanceId', width: 220 }, - { title: '状态', dataIndex: 'status', width: 100, render: (v) => {v} }, - { title: '开始时间', dataIndex: 'startedAt', width: 180 }, - { title: '结束时间', dataIndex: 'endedAt', width: 180 }, - { title: '操作', key: 'op', fixed: 'right', width: 120, render: (_, r) => }, - ]} - scroll={{ x: 1000 }} - /> - )} - - setDetailOpen(false)} width={720} destroyOnClose> - {detailLoading ? ( -
加载中...
- ) : !detail ? null : ( - <> - - {detail.workflowDefinitionId} - {detail.processInstanceId} - {detail.status} - {detail.startedAt} - {detail.endedAt} - -
- 节点 -
k as string} - size="small" - dataSource={Object.keys(detail.nodes || {})} - pagination={false} - columns={[ - { title: '节点ID', dataIndex: 'id', render: (_, id) => id }, - { title: '状态', render: (_, id) => {detail.nodes?.[id].status} }, - { title: '开始', render: (_, id) => detail.nodes?.[id].startTime }, - { title: '结束', render: (_, id) => detail.nodes?.[id].endTime }, - { title: '错误', render: (_, id) => detail.nodes?.[id].error ? {String(detail.nodes?.[id].error?.message || '错误')} : '-' }, - { title: '查看', render: (_, id) => }, - ]} - /> - - -
- 时间线 - ({ id, ...v })) - .sort((a, b) => (a.startTime || '').localeCompare(b.startTime || '')) - .map((n) => ({ - color: n.status === 'success' ? 'green' : n.status === 'failed' ? 'red' : 'blue', - children: ( -
-
{n.id} - {n.status}
-
开始:{n.startTime || '-'},结束:{n.endTime || '-'}
- {n.error ?
错误:{String(n.error?.message || '错误')}
: null} -
- ), - })))} /> -
- - )} - - - setNodeDetailOpen(false)} width={720}> - {nodeDetail ? ( - <> - 输入 - - 输出 - - - ) : null} - - - ) -} diff --git a/frontend/src/pages/WorkflowEditorPage.tsx b/frontend/src/pages/WorkflowEditorPage.tsx deleted file mode 100644 index 0058ae9..0000000 --- a/frontend/src/pages/WorkflowEditorPage.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import React, { useState, useEffect } from 'react' -import { Row, Col, Typography, Divider, Space, Input, Button, message } from 'antd' -import NodePalette from '@/components/editor/NodePalette' -import Canvas from '@/components/editor/Canvas' -import NodePropertyPanel from '@/components/editor/NodePropertyPanel' -import EdgePropertyPanel from '@/components/editor/EdgePropertyPanel' -import { useEditorStore } from '@/store/editorStore' -import { workflowApi } from '@/api/workflow' -import type { WorkflowDefinition } from '@/types/workflow' -import { validateWorkflowDefinition } from '@/utils/validate' -import { downloadJson } from '@/utils/download' -import { saveLocal, loadLocal } from '@/utils/storage' -import { useSearchParams } from 'react-router-dom' - -function rfToDefinition(name: string, id: string | undefined, nodes: any[], edges: any[]): WorkflowDefinition { - return { - id, - name, - schemaVersion: '1.0', - nodes: nodes.map((n) => ({ - id: n.id, - type: n.data?.type, - name: n.data?.name, - position: n.position, - config: n.data?.config || {}, - })), - edges: edges.map((e) => ({ source: e.source, target: e.target, condition: e.label ? `\${${e.label}}` : undefined })), - } -} - -function defToRf(def: WorkflowDefinition) { - const nodes = def.nodes.map((n) => ({ - id: n.id, - type: 'custom', - position: n.position, - data: { type: n.type, name: n.name, config: n.config, icon: 'ApiOutlined' }, - })) - const edges = def.edges.map((e, idx) => ({ - id: `e-${idx}`, - source: e.source, - target: e.target, - type: 'smoothstep', - label: (e as any).condition ? String((e as any).condition).replace(/^\$\{|\}$/g, '') : undefined, - })) - return { nodes, edges } -} - -export default function WorkflowEditorPage() { - const { nodes, edges, setNodes, setEdges } = useEditorStore() - const [wfId, setWfId] = useState(undefined) - const [wfName, setWfName] = useState('未命名工作流') - const [sp] = useSearchParams() - - const onSave = async () => { - if (!wfName?.trim()) return message.warning('请输入工作流名称') - const def = rfToDefinition(wfName.trim(), wfId, nodes, edges) - const { valid, errors } = validateWorkflowDefinition(def) - if (!valid) { - return message.error(`校验失败:${(errors || []).slice(0, 3).join('; ')}`) - } - try { - const saved = await workflowApi.save(def) - setWfId(saved.id) - message.success(`已保存,ID: ${saved.id || ''}`) - } catch (e: any) { - message.error(`保存失败: ${e?.message || e}`) - } - } - - const onLoad = async () => { - if (!wfId) return message.warning('请输入要加载的工作流 ID') - try { - const def = await workflowApi.getById(wfId) - const { nodes: nn, edges: ee } = defToRf(def) - setNodes(nn) - setEdges(ee) - setWfName(def.name) - message.success('加载完成') - } catch (e: any) { - message.error(`加载失败: ${e?.message || e}`) - } - } - - useEffect(() => { - const id = sp.get('id') - if (id) { - setWfId(id) - ;(async () => { try { const def = await workflowApi.getById(id); const { nodes: nn, edges: ee } = defToRf(def); setNodes(nn); setEdges(ee); setWfName(def.name) } catch { /* ignore */ } })() - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - // 快捷键:Ctrl/Cmd+S 保存,Ctrl/Cmd+L 加载 - useEffect(() => { - const handler = (e: KeyboardEvent) => { - const isMac = navigator.platform.toLowerCase().includes('mac') - const saveCombo = (isMac && e.metaKey && e.key.toLowerCase() === 's') || (!isMac && e.ctrlKey && e.key.toLowerCase() === 's') - const loadCombo = (isMac && e.metaKey && e.key.toLowerCase() === 'l') || (!isMac && e.ctrlKey && e.key.toLowerCase() === 'l') - if (saveCombo) { e.preventDefault(); onSave() } - if (loadCombo) { e.preventDefault(); onLoad() } - } - window.addEventListener('keydown', handler) - return () => window.removeEventListener('keydown', handler) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [wfId, wfName, nodes, edges]) - - // 自动保存到本地(简易) - useEffect(() => { - const def = rfToDefinition(wfName.trim() || '未命名工作流', wfId, nodes, edges) - saveLocal('editor-autosave', def) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [wfName, wfId, nodes, edges]) - - const restoreLocal = () => { - const def = loadLocal('editor-autosave') - if (!def) return message.warning('没有本地草稿') - try { - const { nodes: nn, edges: ee } = defToRf(def) - setNodes(nn) - setEdges(ee) - setWfName(def.name || wfName) - setWfId(def.id || wfId) - message.success('已恢复本地草稿') - } catch (e: any) { - message.error(`恢复失败: ${e?.message || e}`) - } - } - - const fileRef = React.useRef(null) - - const onExport = () => { - const def = rfToDefinition(wfName.trim() || '未命名工作流', wfId, nodes, edges) - downloadJson(`${wfName || 'workflow'}.json`, def) - } - - const onImport = async (file: File) => { - try { - const text = await file.text() - const def = JSON.parse(text) - const { nodes: nn, edges: ee } = defToRf(def) - setNodes(nn) - setEdges(ee) - setWfName(def.name || wfName) - setWfId(def.id || wfId) - message.success('已导入 JSON') - } catch (e: any) { - message.error(`导入失败: ${e?.message || e}`) - } - } - - return ( -
- 工作流编辑 - - setWfName(e.target.value)} style={{ width: 240 }} /> - setWfId(e.target.value)} style={{ width: 260 }} /> - - - - { const f = e.target.files?.[0]; if (f) onImport(f); if (fileRef.current) fileRef.current.value = '' }} /> - - - - - -
- - - - - - - - - - ) -} diff --git a/frontend/src/pages/WorkflowListPage.tsx b/frontend/src/pages/WorkflowListPage.tsx deleted file mode 100644 index 62df6b1..0000000 --- a/frontend/src/pages/WorkflowListPage.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { useEffect, useState } from 'react' -import { useWorkflowStore } from '@/store/workflowStore' -import { Typography, Table, Button, Space, Input, Tag } from 'antd' -import { Link } from 'react-router-dom' -import Loading from '@/components/common/Loading' - -export default function WorkflowListPage() { - const { items, loading, load, total } = useWorkflowStore() as any - const [page, setPage] = useState(1) - const [size, setSize] = useState(10) - const [status, setStatus] = useState(undefined) - - useEffect(() => { void load({ page, size, status }) }, [load, page, size, status]) - - return ( -
- 工作流列表 - - setStatus(e.target.value || undefined)} /> - - - {loading ? ( - - ) : ( -
{ setPage(p); setSize(s || 10) } }} - columns={[ - { title: 'ID', dataIndex: 'id', width: 220 }, - { title: '名称', dataIndex: 'name', width: 240 }, - { title: '状态', dataIndex: 'status', width: 120, render: (v: any) => {v} }, - { title: '操作', key: 'op', width: 200, render: (_, r: any) => ( - - - - ) }, - ]} - /> - )} - - ) -} diff --git a/frontend/src/store/editorStore.ts b/frontend/src/store/editorStore.ts deleted file mode 100644 index cdcc8ef..0000000 --- a/frontend/src/store/editorStore.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { create } from 'zustand' -import type { Node, Edge } from 'reactflow' - -interface EditorStore { - nodes: Node[] - edges: Edge[] - selectedNodeId?: string - selectedEdgeId?: string - setNodes: (nodes: Node[]) => void - setEdges: (edges: Edge[]) => void - addNode: (node: Node) => void - addEdge: (edge: Edge) => void - setSelectedNode: (id?: string) => void - setSelectedEdge: (id?: string) => void - updateNodeData: (id: string, data: Record) => void - updateEdgeData: (id: string, data: Record) => void - updateEdgeProps: (id: string, patch: Partial) => void -} - -export const useEditorStore = create((set) => ({ - nodes: [], - edges: [], - selectedNodeId: undefined, - selectedEdgeId: undefined, - setNodes: (nodes) => set({ nodes }), - setEdges: (edges) => set({ edges }), - addNode: (node) => set((s) => ({ nodes: [...s.nodes, node] })), - addEdge: (edge) => set((s) => ({ edges: [...s.edges, edge] })), - setSelectedNode: (id) => set({ selectedNodeId: id, selectedEdgeId: undefined }), - setSelectedEdge: (id) => set({ selectedEdgeId: id, selectedNodeId: undefined }), - updateNodeData: (id, data) => set((s) => ({ - nodes: s.nodes.map((n) => (n.id === id ? { ...n, data: { ...n.data, ...data } } : n)), - })), - updateEdgeData: (id, data) => set((s) => ({ - edges: s.edges.map((e) => (e.id === id ? { ...e, data: { ...(e as any).data, ...data } } : e)), - })), - updateEdgeProps: (id, patch) => set((s) => ({ - edges: s.edges.map((e) => (e.id === id ? { ...e, ...patch } : e)), - })), -})) diff --git a/frontend/src/store/nodeTypeStore.ts b/frontend/src/store/nodeTypeStore.ts deleted file mode 100644 index e7b3e08..0000000 --- a/frontend/src/store/nodeTypeStore.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { create } from 'zustand' -import type { NodeTypeMetadata } from '@/types/node' -import { nodeTypeApi } from '@/api/nodeType' - -interface NodeTypeStore { - items: NodeTypeMetadata[] - loading: boolean - fetch: () => Promise -} - -export const useNodeTypeStore = create((set) => ({ - items: [], - loading: false, - fetch: async () => { - set({ loading: true }) - try { - const res = await nodeTypeApi.list() - set({ items: res }) - } finally { - set({ loading: false }) - } - }, -})) diff --git a/frontend/src/store/workflowStore.ts b/frontend/src/store/workflowStore.ts deleted file mode 100644 index 91bdb07..0000000 --- a/frontend/src/store/workflowStore.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { create } from 'zustand' -import type { WorkflowDefinition } from '@/types/workflow' -import { workflowApi } from '@/api/workflow' - -interface WorkflowStore { - items: WorkflowDefinition[] - total: number - loading: boolean - load: (params?: { status?: string; page?: number; size?: number }) => Promise -} - -export const useWorkflowStore = create((set) => ({ - items: [], - total: 0, - loading: false, - load: async (params) => { - set({ loading: true }) - try { - const res = await workflowApi.list(params?.status, params?.page, params?.size) - set({ items: res.items, total: res.total }) - } finally { - set({ loading: false }) - } - }, -})) diff --git a/frontend/src/types/node.ts b/frontend/src/types/node.ts deleted file mode 100644 index 1e80c12..0000000 --- a/frontend/src/types/node.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface FieldDefinition { - name: string - label: string - type: 'text' | 'textarea' | 'number' | 'select' | 'code' | 'key_value' | 'boolean' - required?: boolean - supportsExpression?: boolean - supportsFieldMapping?: boolean - options?: string[] - defaultValue?: any - placeholder?: string - language?: string -} - -export interface NodeTypeMetadata { - id: string - name: string - displayName: string - category: 'api' | 'database' | 'logic' | 'notification' | 'transform' | 'other' - icon?: string - description?: string - fields: FieldDefinition[] - outputSchema: Record - implementationClass?: string - enabled?: boolean -} diff --git a/frontend/src/types/workflow.ts b/frontend/src/types/workflow.ts deleted file mode 100644 index 724de00..0000000 --- a/frontend/src/types/workflow.ts +++ /dev/null @@ -1,73 +0,0 @@ -export interface Position { - x: number - y: number -} - -export interface WorkflowNode { - id: string - type: string - name: string - position: Position - config: Record -} - -export interface WorkflowEdge { - id?: string - source: string - target: string - // 必须是完整 ${...} - condition?: string -} - -export interface WorkflowDefinition { - id?: string - name: string - description?: string - schemaVersion: '1.0' - nodes: WorkflowNode[] - edges: WorkflowEdge[] - variables?: Record - metadata?: Record -} - -export interface WorkflowExecutionRecord { - id: string - workflowDefinitionId: string - processInstanceId: string - status: 'running' | 'completed' | 'failed' | 'cancelled' - triggerType?: 'manual' | 'cron' | 'webhook' - triggeredBy?: string - startedAt?: string - endedAt?: string -} - -export interface NodeExecutionData { - status: 'success' | 'failed' | 'skipped' | 'running' - input?: any - output?: any - startTime?: string - endTime?: string - error?: any -} - -export interface WorkflowExecutionDetail { - id: string - workflowDefinitionId: string - processInstanceId: string - status: 'running' | 'completed' | 'failed' | 'cancelled' - input?: Record - nodes?: Record - startedAt?: string - endedAt?: string - error?: any -} - -export interface WorkflowExecutionResult { - workflowId: string - processInstanceId: string - status: 'running' | 'completed' | 'failed' - output?: any - nodes?: Record - startedAt?: string - endedAt?: string -} diff --git a/frontend/src/utils/download.ts b/frontend/src/utils/download.ts deleted file mode 100644 index 3f4574f..0000000 --- a/frontend/src/utils/download.ts +++ /dev/null @@ -1,11 +0,0 @@ -export function downloadJson(filename: string, data: any) { - const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }) - const url = URL.createObjectURL(blob) - const a = document.createElement('a') - a.href = url - a.download = filename - document.body.appendChild(a) - a.click() - document.body.removeChild(a) - URL.revokeObjectURL(url) -} \ No newline at end of file diff --git a/frontend/src/utils/schema.ts b/frontend/src/utils/schema.ts deleted file mode 100644 index 8b2314e..0000000 --- a/frontend/src/utils/schema.ts +++ /dev/null @@ -1,44 +0,0 @@ -export const workflowDefinitionSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - title: 'WorkflowDefinition', - type: 'object', - required: ['name', 'schemaVersion', 'nodes', 'edges'], - properties: { - id: { type: 'string' }, - name: { type: 'string', minLength: 1 }, - schemaVersion: { type: 'string' }, - nodes: { - type: 'array', - minItems: 1, - items: { - type: 'object', - required: ['id', 'type', 'name', 'position', 'config'], - properties: { - id: { type: 'string' }, - type: { type: 'string' }, - name: { type: 'string' }, - position: { - type: 'object', - required: ['x', 'y'], - properties: { x: { type: 'number' }, y: { type: 'number' } }, - }, - config: { type: 'object' }, - }, - }, - }, - edges: { - type: 'array', - items: { - type: 'object', - required: ['source', 'target'], - properties: { - source: { type: 'string' }, - target: { type: 'string' }, - condition: { type: 'string' }, - }, - }, - }, - variables: { type: 'object' }, - metadata: { type: 'object' }, - }, -} as const diff --git a/frontend/src/utils/storage.ts b/frontend/src/utils/storage.ts deleted file mode 100644 index 9542f85..0000000 --- a/frontend/src/utils/storage.ts +++ /dev/null @@ -1,16 +0,0 @@ -export function saveLocal(key: string, data: any) { - try { - localStorage.setItem(key, JSON.stringify(data)) - } catch { - // ignore - } -} - -export function loadLocal(key: string): T | null { - try { - const t = localStorage.getItem(key) - return t ? (JSON.parse(t) as T) : null - } catch { - return null - } -} \ No newline at end of file diff --git a/frontend/src/utils/validate.ts b/frontend/src/utils/validate.ts deleted file mode 100644 index 681ccc1..0000000 --- a/frontend/src/utils/validate.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Ajv from 'ajv' -import { workflowDefinitionSchema } from './schema' - -const ajv = new Ajv({ allErrors: true }) -const validateFn = ajv.compile(workflowDefinitionSchema as any) - -export function validateWorkflowDefinition(def: any): { valid: boolean; errors?: string[] } { - const ok = validateFn(def) - if (ok) return { valid: true } - const errors = (validateFn.errors || []).map((e) => `${e.instancePath} ${e.message}`) - return { valid: false, errors } -} diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts deleted file mode 100644 index 11f02fe..0000000 --- a/frontend/src/vite-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/frontend/stylelint.config.mjs b/frontend/stylelint.config.mjs new file mode 100644 index 0000000..e380674 --- /dev/null +++ b/frontend/stylelint.config.mjs @@ -0,0 +1,4 @@ +export default { + extends: ['@vben/stylelint-config'], + root: true, +}; diff --git a/frontend/tea.yaml b/frontend/tea.yaml new file mode 100644 index 0000000..6e56d6f --- /dev/null +++ b/frontend/tea.yaml @@ -0,0 +1,6 @@ +# https://tea.xyz/what-is-this-file +--- +version: 1.0.0 +codeOwners: + - '0xB33cc732DFc15Cd39eF50Fb165c876E24417E48f' +quorum: 1 diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json deleted file mode 100644 index 06a0f9f..0000000 --- a/frontend/tsconfig.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2020", - "useDefineForClassFields": true, - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "module": "ESNext", - "skipLibCheck": true, - "moduleResolution": "bundler", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": true, - "jsx": "react-jsx", - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - "strict": true - }, - "include": ["src"] -} diff --git a/frontend/tsconfig.tsbuildinfo b/frontend/tsconfig.tsbuildinfo deleted file mode 100644 index 4e4abb7..0000000 --- a/frontend/tsconfig.tsbuildinfo +++ /dev/null @@ -1 +0,0 @@ -{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/api/http.ts","./src/api/nodetype.ts","./src/api/task.ts","./src/api/workflow.ts","./src/components/common/codeeditor.tsx","./src/components/common/empty.tsx","./src/components/common/error.tsx","./src/components/common/expressioninput.tsx","./src/components/common/fieldmappingselector.tsx","./src/components/common/jsonview.tsx","./src/components/common/keyvalueeditor.tsx","./src/components/common/loading.tsx","./src/components/editor/canvas.tsx","./src/components/editor/conditionnode.tsx","./src/components/editor/customnode.tsx","./src/components/editor/edgepropertypanel.tsx","./src/components/editor/nodepalette.tsx","./src/components/editor/nodepropertypanel.tsx","./src/components/layout/applayout.tsx","./src/pages/approvalcenterpage.tsx","./src/pages/executionhistorypage.tsx","./src/pages/workfloweditorpage.tsx","./src/pages/workflowlistpage.tsx","./src/store/editorstore.ts","./src/store/nodetypestore.ts","./src/store/workflowstore.ts","./src/types/node.ts","./src/types/workflow.ts","./src/utils/download.ts","./src/utils/schema.ts","./src/utils/storage.ts","./src/utils/validate.ts"],"version":"5.9.3"} \ No newline at end of file diff --git a/frontend/turbo.json b/frontend/turbo.json new file mode 100644 index 0000000..3443e27 --- /dev/null +++ b/frontend/turbo.json @@ -0,0 +1,49 @@ +{ + "$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/**"] + }, + "@vben/backend-mock#build": { + "dependsOn": ["^build"], + "outputs": [".nitro/**", ".output/**"] + }, + "test:e2e": {}, + "dev": { + "dependsOn": [], + "outputs": [], + "cache": false, + "persistent": true + }, + "typecheck": { + "outputs": [] + } + } +} diff --git a/frontend/vben-admin.code-workspace b/frontend/vben-admin.code-workspace new file mode 100644 index 0000000..aa8205b --- /dev/null +++ b/frontend/vben-admin.code-workspace @@ -0,0 +1,172 @@ +{ + "folders": [ + { + "name": "@vben/backend-mock", + "path": "apps/backend-mock", + }, + { + "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/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/playground", + "path": "playground", + }, + { + "name": "@vben/turbo-run", + "path": "scripts/turbo-run", + }, + { + "name": "@vben/vsh", + "path": "scripts/vsh", + }, + ], +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts deleted file mode 100644 index f93d20f..0000000 --- a/frontend/vite.config.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' -import path from 'path' - -// https://vitejs.dev/config/ -export default defineConfig({ - plugins: [react()], - resolve: { - alias: { - '@': path.resolve(__dirname, './src'), - }, - }, - server: { - port: 3000, - proxy: { - '/api': { - target: process.env.VITE_API_BASE_URL || 'http://localhost:8080', - changeOrigin: true, - }, - }, - }, -}) diff --git a/frontend/vitest.config.ts b/frontend/vitest.config.ts new file mode 100644 index 0000000..a10b5fa --- /dev/null +++ b/frontend/vitest.config.ts @@ -0,0 +1,11 @@ +import Vue from '@vitejs/plugin-vue'; +import VueJsx from '@vitejs/plugin-vue-jsx'; +import { configDefaults, defineConfig } from 'vitest/config'; + +export default defineConfig({ + plugins: [Vue(), VueJsx()], + test: { + environment: 'happy-dom', + exclude: [...configDefaults.exclude, '**/e2e/**'], + }, +}); diff --git a/frontend/vitest.workspace.ts b/frontend/vitest.workspace.ts new file mode 100644 index 0000000..f00d6f6 --- /dev/null +++ b/frontend/vitest.workspace.ts @@ -0,0 +1,3 @@ +import { defineWorkspace } from 'vitest/config'; + +export default defineWorkspace(['vitest.config.ts']);