From a4736a49f81a3c4473d41c9288dc02dfbceadfce Mon Sep 17 00:00:00 2001 From: xingyu Date: Tue, 10 Mar 2026 05:08:45 +0800 Subject: [PATCH] feat: migrate to Tailwind CSS v4 (#7614) * chore: update deps * feat: use jsonc/x language * chore: update eslint 10.0 * fix: no-useless-assignment * feat: add CLAUDE.md * chore: ignore * feat: claude * fix: lint * chore: suppot eslint v10 * fix: lint * fix: lint * fix: type check * fix: unit test * fix: Suggested fix * fix: unit test * chore: update stylelint v17 * chore: update all major deps * fix: echarts console warn * chore: update vitest v4 * feat: add skills ignores * chore: update deps * chore: update deps * fix: cspell * chore: update deps * chore: update tailwindcss v4 * chore: remove postcss config * fix: no use catalog * chore: tailwind v4 config * fix: tailwindcss v4 sort * feat: use eslint-plugin-better-tailwindcss * fix: Interference between enforce-consistent-line-wrapping, jsx-curly-brace-presence and Prettier * fix: Interference between enforce-consistent-line-wrapping, jsx-curly-brace-presence and Prettier * fix(lint): resolve prettier and better-tailwindcss formatting conflicts * fix(tailwind): update theme references and lint sources * style(format): normalize apps docs and playground vue files * style(format): normalize core ui-kit components * style(format): normalize effects ui and layout components --- .gitignore | 7 + .npmrc | 1 - .prettierignore | 5 + .stylelintignore | 4 + .vscode/settings.json | 10 +- CLAUDE.md | 148 + apps/backend-mock/api/table/list.ts | 2 +- apps/web-antd/postcss.config.mjs | 1 - apps/web-antd/src/store/auth.ts | 3 +- .../src/views/dashboard/analytics/index.vue | 4 +- apps/web-antd/tailwind.config.mjs | 1 - apps/web-antdv-next/postcss.config.mjs | 1 - apps/web-antdv-next/src/store/auth.ts | 3 +- .../src/views/dashboard/analytics/index.vue | 4 +- apps/web-antdv-next/tailwind.config.mjs | 1 - apps/web-ele/postcss.config.mjs | 1 - apps/web-ele/src/store/auth.ts | 3 +- .../src/views/dashboard/analytics/index.vue | 4 +- .../web-ele/src/views/demos/element/index.vue | 4 +- apps/web-ele/tailwind.config.mjs | 1 - apps/web-naive/postcss.config.mjs | 1 - apps/web-naive/src/store/auth.ts | 3 +- .../src/views/dashboard/analytics/index.vue | 4 +- apps/web-naive/tailwind.config.mjs | 1 - apps/web-tdesign/postcss.config.mjs | 1 - apps/web-tdesign/src/store/auth.ts | 3 +- .../src/views/dashboard/analytics/index.vue | 4 +- .../src/views/demos/tdesign/index.vue | 6 +- apps/web-tdesign/tailwind.config.mjs | 1 - cspell.json | 3 + docs/.vitepress/components/demo-preview.vue | 2 +- docs/.vitepress/components/preview-group.vue | 8 +- docs/.vitepress/config/shared.mts | 8 +- .../theme/components/vben-contributors.vue | 2 +- docs/package.json | 1 + docs/tailwind.config.mjs | 11 - .../lint-configs/eslint-config/package.json | 7 +- .../eslint-config/src/configs/comments.ts | 3 +- .../eslint-config/src/configs/ignores.ts | 6 + .../eslint-config/src/configs/index.ts | 1 + .../eslint-config/src/configs/jsonc.ts | 9 +- .../eslint-config/src/configs/node.ts | 1 - .../src/configs/perfectionist.ts | 55 +- .../eslint-config/src/configs/pnpm.ts | 7 +- .../eslint-config/src/configs/tailwindcss.ts | 49 + .../eslint-config/src/configs/test.ts | 2 +- .../eslint-config/src/configs/yaml.ts | 2 +- .../eslint-config/src/custom-config.ts | 6 +- .../lint-configs/eslint-config/src/index.ts | 2 + .../lint-configs/prettier-config/index.mjs | 1 - .../lint-configs/prettier-config/package.json | 3 +- .../lint-configs/stylelint-config/index.mjs | 14 +- internal/tailwind-config/build.config.ts | 10 - internal/tailwind-config/package.json | 67 - internal/tailwind-config/src/index.ts | 266 - internal/tailwind-config/src/module.d.ts | 3 - internal/tailwind-config/src/plugins/entry.ts | 53 - .../tailwind-config/src/postcss.config.ts | 15 - internal/tailwind-config/tsconfig.json | 6 - internal/vite-config/package.json | 1 + internal/vite-config/src/plugins/index.ts | 4 + .../src/plugins/tailwind-reference.ts | 40 + package.json | 4 +- packages/@core/base/design/package.json | 9 + packages/@core/base/design/src/css/global.css | 511 +- .../@core/base/design/src/css/nprogress.css | 8 +- .../src/utils/__tests__/resources.test.ts | 54 +- packages/@core/preferences/src/config.ts | 2 +- .../@core/ui-kit/form-ui/postcss.config.mjs | 1 - packages/@core/ui-kit/form-ui/src/form-api.ts | 23 +- .../form-ui/src/form-render/form-field.vue | 4 +- .../ui-kit/form-ui/src/form-render/form.vue | 2 +- .../@core/ui-kit/form-ui/tailwind.config.mjs | 1 - .../@core/ui-kit/layout-ui/postcss.config.mjs | 1 - .../src/components/layout-sidebar.vue | 4 +- .../widgets/sidebar-collapse-button.vue | 2 +- .../widgets/sidebar-fixed-button.vue | 2 +- .../ui-kit/layout-ui/src/vben-layout.vue | 4 +- .../ui-kit/layout-ui/tailwind.config.mjs | 1 - packages/@core/ui-kit/menu-ui/package.json | 1 + .../@core/ui-kit/menu-ui/postcss.config.mjs | 1 - .../menu-ui/src/components/menu-badge-dot.vue | 2 +- .../components/normal-menu/normal-menu.vue | 4 +- .../@core/ui-kit/menu-ui/tailwind.config.mjs | 1 - .../@core/ui-kit/popup-ui/postcss.config.mjs | 1 - .../@core/ui-kit/popup-ui/src/alert/alert.vue | 2 +- .../src/drawer/__tests__/drawer-api.test.ts | 17 +- .../ui-kit/popup-ui/src/drawer/drawer-api.ts | 29 +- .../ui-kit/popup-ui/src/drawer/drawer.vue | 12 +- .../src/modal/__tests__/modal-api.test.ts | 17 +- .../ui-kit/popup-ui/src/modal/modal-api.ts | 32 +- .../@core/ui-kit/popup-ui/src/modal/modal.vue | 9 +- .../@core/ui-kit/popup-ui/tailwind.config.mjs | 1 - .../@core/ui-kit/shadcn-ui/components.json | 3 +- packages/@core/ui-kit/shadcn-ui/package.json | 1 + .../@core/ui-kit/shadcn-ui/postcss.config.mjs | 1 - .../src/components/avatar/avatar.vue | 4 +- .../src/components/back-top/back-top.vue | 2 +- .../breadcrumb/breadcrumb-background.vue | 12 +- .../src/components/button/button.vue | 2 +- .../dropdown-menu/dropdown-menu.vue | 2 +- .../dropdown-menu/dropdown-radio-menu.vue | 2 +- .../components/full-screen/full-screen.vue | 4 +- .../input-password/input-password.vue | 4 +- .../input-password/password-strength.vue | 2 +- .../shadcn-ui/src/components/logo/logo.vue | 2 +- .../src/components/pin-input/input.vue | 2 +- .../src/components/scrollbar/scrollbar.vue | 4 +- .../src/components/segmented/segmented.vue | 4 +- .../components/segmented/tabs-indicator.vue | 4 +- .../src/components/spinner/loading.vue | 6 +- .../src/components/spinner/spinner.vue | 4 +- .../src/components/tooltip/help-tooltip.vue | 2 +- .../src/components/tooltip/tooltip.vue | 2 +- .../src/ui/accordion/AccordionContent.vue | 4 +- .../src/ui/accordion/AccordionTrigger.vue | 2 +- .../ui/alert-dialog/AlertDialogContent.vue | 2 +- .../alert-dialog/AlertDialogDescription.vue | 2 +- .../ui/alert-dialog/AlertDialogOverlay.vue | 2 +- .../src/ui/alert-dialog/AlertDialogTitle.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/badge/badge.ts | 4 +- .../src/ui/breadcrumb/BreadcrumbItem.vue | 2 +- .../src/ui/breadcrumb/BreadcrumbLink.vue | 2 +- .../src/ui/breadcrumb/BreadcrumbList.vue | 2 +- .../src/ui/breadcrumb/BreadcrumbPage.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/card/Card.vue | 2 +- .../shadcn-ui/src/ui/card/CardDescription.vue | 2 +- .../shadcn-ui/src/ui/card/CardTitle.vue | 2 +- .../shadcn-ui/src/ui/checkbox/Checkbox.vue | 2 +- .../context-menu/ContextMenuCheckboxItem.vue | 2 +- .../ui/context-menu/ContextMenuContent.vue | 2 +- .../src/ui/context-menu/ContextMenuItem.vue | 2 +- .../src/ui/context-menu/ContextMenuLabel.vue | 2 +- .../ui/context-menu/ContextMenuRadioItem.vue | 2 +- .../ui/context-menu/ContextMenuSeparator.vue | 2 +- .../ui/context-menu/ContextMenuShortcut.vue | 2 +- .../ui/context-menu/ContextMenuSubContent.vue | 4 +- .../ui/context-menu/ContextMenuSubTrigger.vue | 2 +- .../shadcn-ui/src/ui/dialog/DialogContent.vue | 4 +- .../src/ui/dialog/DialogDescription.vue | 2 +- .../shadcn-ui/src/ui/dialog/DialogFooter.vue | 2 +- .../shadcn-ui/src/ui/dialog/DialogOverlay.vue | 2 +- .../src/ui/dialog/DialogScrollContent.vue | 6 +- .../shadcn-ui/src/ui/dialog/DialogTitle.vue | 2 +- .../DropdownMenuCheckboxItem.vue | 2 +- .../ui/dropdown-menu/DropdownMenuContent.vue | 2 +- .../src/ui/dropdown-menu/DropdownMenuItem.vue | 2 +- .../dropdown-menu/DropdownMenuRadioItem.vue | 2 +- .../dropdown-menu/DropdownMenuSeparator.vue | 2 +- .../dropdown-menu/DropdownMenuSubContent.vue | 4 +- .../dropdown-menu/DropdownMenuSubTrigger.vue | 2 +- .../ui/dropdown-menu/DropdownMenuTrigger.vue | 2 +- .../shadcn-ui/src/ui/form/FormDescription.vue | 2 +- .../shadcn-ui/src/ui/form/FormMessage.vue | 2 +- .../src/ui/hover-card/HoverCardContent.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/input/Input.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/label/Label.vue | 2 +- .../ui/number-field/NumberFieldDecrement.vue | 2 +- .../ui/number-field/NumberFieldIncrement.vue | 2 +- .../src/ui/number-field/NumberFieldInput.vue | 2 +- .../src/ui/pin-input/PinInputInput.vue | 2 +- .../src/ui/popover/PopoverContent.vue | 2 +- .../src/ui/radio-group/RadioGroupItem.vue | 2 +- .../src/ui/resizable/ResizableHandle.vue | 4 +- .../src/ui/scroll-area/ScrollArea.vue | 2 +- .../src/ui/scroll-area/ScrollBar.vue | 4 +- .../shadcn-ui/src/ui/select/SelectContent.vue | 4 +- .../shadcn-ui/src/ui/select/SelectItem.vue | 2 +- .../src/ui/select/SelectSeparator.vue | 2 +- .../shadcn-ui/src/ui/select/SelectTrigger.vue | 2 +- .../shadcn-ui/src/ui/separator/Separator.vue | 4 +- .../shadcn-ui/src/ui/sheet/SheetContent.vue | 2 +- .../src/ui/sheet/SheetDescription.vue | 2 +- .../shadcn-ui/src/ui/sheet/SheetFooter.vue | 2 +- .../shadcn-ui/src/ui/sheet/SheetOverlay.vue | 2 +- .../shadcn-ui/src/ui/sheet/SheetTitle.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/switch/Switch.vue | 4 +- .../shadcn-ui/src/ui/tabs/TabsContent.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/tabs/TabsList.vue | 2 +- .../shadcn-ui/src/ui/tabs/TabsTrigger.vue | 2 +- .../shadcn-ui/src/ui/textarea/Textarea.vue | 2 +- .../src/ui/tooltip/TooltipContent.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/tree/tree.vue | 10 +- .../ui-kit/shadcn-ui/tailwind.config.mjs | 1 - packages/@core/ui-kit/tabs-ui/package.json | 1 + .../@core/ui-kit/tabs-ui/postcss.config.mjs | 1 - .../src/components/tabs-chrome/tabs.vue | 28 +- .../tabs-ui/src/components/tabs/tabs.vue | 12 +- .../@core/ui-kit/tabs-ui/tailwind.config.mjs | 1 - packages/effects/common-ui/package.json | 1 + .../captcha/point-selection-captcha/index.vue | 6 +- .../point-selection-captcha-card.vue | 4 +- .../captcha/slider-captcha/index.vue | 2 +- .../slider-captcha/slider-captcha-action.vue | 6 +- .../slider-captcha/slider-captcha-bar.vue | 4 +- .../slider-captcha/slider-captcha-content.vue | 2 +- .../captcha/slider-rotate-captcha/index.vue | 2 +- .../slider-translate-captcha/index.vue | 4 +- .../src/components/cropper/cropper.vue | 34 +- .../ellipsis-text/ellipsis-text.vue | 2 +- .../components/icon-picker/icon-picker.vue | 4 +- .../common-ui/src/components/page/page.vue | 4 +- .../common-ui/src/components/tree/tree.vue | 2 +- .../effects/common-ui/src/ui/about/about.vue | 26 +- .../src/ui/authentication/auth-title.vue | 4 +- .../common-ui/src/ui/authentication/login.vue | 2 +- .../src/ui/authentication/qrcode-login.vue | 4 +- .../ui/authentication/third-party-login.vue | 6 +- .../analysis/analysis-charts-tabs.vue | 2 +- .../dashboard/analysis/analysis-overview.vue | 2 +- .../dashboard/workbench/workbench-header.vue | 4 +- .../dashboard/workbench/workbench-project.vue | 6 +- .../workbench/workbench-quick-nav.vue | 2 +- .../ui/dashboard/workbench/workbench-todo.vue | 10 +- .../dashboard/workbench/workbench-trends.vue | 8 +- .../common-ui/src/ui/fallback/fallback.vue | 6 +- .../common-ui/src/ui/profile/profile.vue | 10 +- packages/effects/layouts/package.json | 1 + .../src/authentication/authentication.vue | 26 +- .../layouts/src/authentication/form.vue | 4 +- .../layouts/src/authentication/toolbar.vue | 4 +- .../layouts/src/basic/copyright/copyright.vue | 4 +- .../layouts/src/basic/footer/footer.vue | 2 +- .../layouts/src/basic/header/header.vue | 6 +- packages/effects/layouts/src/basic/layout.vue | 2 +- .../layouts/src/widgets/color-toggle.vue | 8 +- .../widgets/global-search/global-search.vue | 12 +- .../widgets/global-search/search-panel.vue | 22 +- .../layouts/src/widgets/language-toggle.vue | 2 +- .../widgets/lock-screen/lock-screen-modal.vue | 2 +- .../src/widgets/lock-screen/lock-screen.vue | 14 +- .../src/widgets/notification/notification.vue | 28 +- .../src/widgets/preferences/blocks/block.vue | 2 +- .../preferences/blocks/general/animation.vue | 4 +- .../widgets/preferences/blocks/input-item.vue | 2 +- .../preferences/blocks/layout/content.vue | 2 +- .../preferences/blocks/layout/layout.vue | 2 +- .../preferences/blocks/switch-item.vue | 4 +- .../preferences/blocks/theme/builtin.vue | 8 +- .../preferences/blocks/theme/font-size.vue | 4 +- .../preferences/blocks/theme/radius.vue | 2 +- .../preferences/blocks/theme/theme.vue | 2 +- .../preferences/blocks/toggle-item.vue | 4 +- .../preferences/preferences-button.vue | 2 +- .../preferences/preferences-drawer.vue | 4 +- .../src/widgets/preferences/preferences.vue | 2 +- .../src/widgets/theme-toggle/theme-button.vue | 93 +- .../src/widgets/timezone/timezone-button.vue | 2 +- .../widgets/user-dropdown/user-dropdown.vue | 8 +- packages/effects/plugins/package.json | 1 + .../effects/plugins/src/echarts/echarts.ts | 7 +- .../plugins/src/echarts/use-echarts.ts | 2 +- packages/effects/plugins/src/vxe-table/api.ts | 11 +- .../effects/plugins/src/vxe-table/style.css | 2 + .../plugins/src/vxe-table/use-vxe-grid.vue | 10 +- .../request/src/request-client/modules/sse.ts | 4 +- .../src/request-client/preset-interceptors.ts | 2 +- packages/icons/src/svg/load.ts | 6 +- packages/styles/src/antd/index.css | 5 +- packages/styles/src/antdv-next/index.css | 5 +- playground/package.json | 1 + playground/postcss.config.mjs | 1 - playground/src/adapter/vxe-table.ts | 2 +- playground/src/store/auth.ts | 3 +- .../src/views/dashboard/analytics/index.vue | 4 +- .../demos/features/full-screen/index.vue | 2 +- .../views/examples/captcha/slider-captcha.vue | 12 +- .../captcha/slider-rotate-captcha.vue | 2 +- .../captcha/slider-translate-captcha.vue | 2 +- .../src/views/examples/count-to/index.vue | 4 +- .../examples/form/scroll-to-error-test.vue | 6 +- .../src/views/examples/json-viewer/data.ts | 2 +- .../src/views/examples/layout/col-page.vue | 8 +- .../src/views/examples/loading/index.vue | 17 +- .../src/views/examples/motion/index.vue | 12 +- .../src/views/examples/resize/basic.vue | 2 +- playground/src/views/system/menu/list.vue | 2 +- playground/tailwind.config.mjs | 1 - pnpm-lock.yaml | 8863 +++++++---------- pnpm-workspace.yaml | 174 +- scripts/turbo-run/src/index.ts | 8 +- scripts/vsh/env.d.ts | 1 + scripts/vsh/src/check-dep/index.ts | 2 - scripts/vsh/src/index.ts | 27 +- scripts/vsh/src/lint/index.ts | 4 +- scripts/vsh/tsconfig.json | 2 +- turbo.json | 1 - vben-admin.code-workspace | 4 - vitest.config.ts | 9 + 289 files changed, 5293 insertions(+), 6338 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 apps/web-antd/postcss.config.mjs delete mode 100644 apps/web-antd/tailwind.config.mjs delete mode 100644 apps/web-antdv-next/postcss.config.mjs delete mode 100644 apps/web-antdv-next/tailwind.config.mjs delete mode 100644 apps/web-ele/postcss.config.mjs delete mode 100644 apps/web-ele/tailwind.config.mjs delete mode 100644 apps/web-naive/postcss.config.mjs delete mode 100644 apps/web-naive/tailwind.config.mjs delete mode 100644 apps/web-tdesign/postcss.config.mjs delete mode 100644 apps/web-tdesign/tailwind.config.mjs delete mode 100644 docs/tailwind.config.mjs create mode 100644 internal/lint-configs/eslint-config/src/configs/tailwindcss.ts delete mode 100644 internal/tailwind-config/build.config.ts delete mode 100644 internal/tailwind-config/package.json delete mode 100644 internal/tailwind-config/src/index.ts delete mode 100644 internal/tailwind-config/src/module.d.ts delete mode 100644 internal/tailwind-config/src/plugins/entry.ts delete mode 100644 internal/tailwind-config/src/postcss.config.ts delete mode 100644 internal/tailwind-config/tsconfig.json create mode 100644 internal/vite-config/src/plugins/tailwind-reference.ts delete mode 100644 packages/@core/ui-kit/form-ui/postcss.config.mjs delete mode 100644 packages/@core/ui-kit/form-ui/tailwind.config.mjs delete mode 100644 packages/@core/ui-kit/layout-ui/postcss.config.mjs delete mode 100644 packages/@core/ui-kit/layout-ui/tailwind.config.mjs delete mode 100644 packages/@core/ui-kit/menu-ui/postcss.config.mjs delete mode 100644 packages/@core/ui-kit/menu-ui/tailwind.config.mjs delete mode 100644 packages/@core/ui-kit/popup-ui/postcss.config.mjs delete mode 100644 packages/@core/ui-kit/popup-ui/tailwind.config.mjs delete mode 100644 packages/@core/ui-kit/shadcn-ui/postcss.config.mjs delete mode 100644 packages/@core/ui-kit/shadcn-ui/tailwind.config.mjs delete mode 100644 packages/@core/ui-kit/tabs-ui/postcss.config.mjs delete mode 100644 packages/@core/ui-kit/tabs-ui/tailwind.config.mjs delete mode 100644 playground/postcss.config.mjs delete mode 100644 playground/tailwind.config.mjs create mode 100644 scripts/vsh/env.d.ts diff --git a/.gitignore b/.gitignore index 3399f39c0..df1f37a8a 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,10 @@ vite.config.ts.* *.sw? .history .cursor + +# AI +.agent +.agents +.claude +.codex +skills-lock.json diff --git a/.npmrc b/.npmrc index aeac1ae91..356b660d8 100644 --- a/.npmrc +++ b/.npmrc @@ -2,7 +2,6 @@ registry=https://registry.npmmirror.com public-hoist-pattern[]=lefthook public-hoist-pattern[]=eslint public-hoist-pattern[]=prettier -public-hoist-pattern[]=prettier-plugin-tailwindcss public-hoist-pattern[]=stylelint public-hoist-pattern[]=*postcss* public-hoist-pattern[]=@commitlint/* diff --git a/.prettierignore b/.prettierignore index d0b0ca133..d0cf12365 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,10 @@ dist dev-dist .local +.claude +.agent +.agents +.codex .output.js node_modules .nvmrc @@ -16,3 +20,4 @@ CODEOWNERS public .npmrc *-lock.yaml +skills-lock.json diff --git a/.stylelintignore b/.stylelintignore index f4b2db2c1..3adb33b22 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -2,3 +2,7 @@ dist public __tests__ coverage +.codex +.claude +.agent +.agents diff --git a/.vscode/settings.json b/.vscode/settings.json index 8da37dc96..984acf37b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { - "tailwindCSS.experimental.configFile": "internal/tailwind-config/src/index.ts", + "tailwindCSS.experimental.configFile": "packages/@core/base/design/src/css/global.css", + "tailwindCSS.lint.suggestCanonicalClasses": "ignore", // workbench "workbench.list.smoothScrolling": true, "workbench.startupEditor": "newUntitledFile", @@ -31,6 +32,9 @@ "editor.autoClosingOvertype": "always", "editor.autoClosingQuotes": "beforeWhitespace", "editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?", + "editor.quickSuggestions": { + "strings": "on" + }, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", "source.fixAll.stylelint": "explicit", @@ -79,6 +83,7 @@ "files.insertFinalNewline": true, "files.simpleDialog.enable": true, "files.associations": { + "*.css": "tailwindcss", "*.ejs": "html", "*.art": "html", "**/tsconfig.json": "jsonc", @@ -220,8 +225,7 @@ "*.env": "$(capture).env.*", "README.md": "README*,CHANGELOG*,LICENSE,CNAME", "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,.gitattributes,.gitignore,.gitpod.yml,.npmrc,.browserslistrc,.node-version,.git*,.tazerc.json", - "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml", - "tailwind.config.mjs": "postcss.*" + "eslint.config.mjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,stylelint.config.*,.lintstagedrc.mjs,cspell.json,lefthook.yml" }, "commentTranslate.hover.enabled": false, "commentTranslate.multiLineMerge": true, diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..5d677b0bd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,148 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## 技术栈 + +1. 基于 **pnpm workspaces** + **Turborepo** 的 Vue 3 + TypeScript + Vite monorepo 项目。 +2. 提供多个 UI 组件库版本(Ant Design Vue、Element Plus、Naive UI、TDesign),共享同一套使用tailwindcss+shadcn-vue的UI组件库核心框架。 +3. 要求 Node ≥ 20.19.0,pnpm ≥ 10。 +4. 使用 **prettier** + **eslint** + **stylelint** 进行代码检查和格式化。 +5. 使用 **vitest** 进行单元测试。 +6. 使用 **commitlint** 进行提交规范。 +7. 使用 **czg** 进行提交规范。 +8. 使用 **lefthook** 进行提交规范。 +9. 使用 **vsh** 进行代码检查和格式化。 +10. 使用 **turbo** 进行构建。 +11. 使用 **vite** 进行开发。 +12. 使用 **vue-tsc** 进行类型检查。 + +```bash +# 其他检查 +pnpm check:circular # 循环依赖扫描 +pnpm check:dep # depcheck 依赖检查 +pnpm check:cspell # 拼写检查 + +# 清理 +pnpm clean # 删除 dist、node_modules 等产物 +pnpm reinstall # clean + 重新安装 + +# 交互式规范提交 +pnpm commit # czg 提交向导 +``` + +Turbo 任务通过 `dependsOn: ["^build"]` 级联,构建某个应用时会自动先构建其所有依赖包。 + +## Monorepo 目录结构 + +```text +apps/ + backend-mock/ # 基于 Nitro 的 mock API 服务(h3 路由 + faker.js 数据) + web-antd/ # Ant Design Vue 应用 + web-ele/ # Element Plus 应用 + web-naive/ # Naive UI 应用 + web-tdesign/ # TDesign Vue 应用 + +packages/ + @core/ # 框架核心(不依赖具体 UI 库) + base/ # 共享工具、缓存、颜色处理、类型定义 + composables/ # 核心 Vue composable + preferences/ # PreferenceManager 类(响应式、持久化配置) + ui-kit/ # UI 组件片段:form-ui、layout-ui、menu-ui、popup-ui、shadcn-ui、tabs-ui + effects/ # 高层模块,可依赖 @core 和 UI 库 + access/ # 路由/菜单生成与权限指令 + common-ui/ # 通用 UI 组件(ApiComponent、IconPicker、VCropper、Tippy 等) + hooks/ # useAppConfig 等 + layouts/ # BasicLayout、登录页、各类 widgets + plugins/ # Motion 等插件 + request/ # RequestClient(axios 封装 + 拦截器体系) + constants/ # 全局常量(LOGIN_PATH 等) + icons/ # Iconify 图标封装 + locales/ # vue-i18n 初始化、loadLocalesMap 工具 + preferences/ # 对外暴露 @core/preferences 的公共 API + stores/ # Pinia 全局 store:useAccessStore、useUserStore、useTabbarStore + styles/ # 全局 CSS / TailwindCSS 基础样式 + types/ # 共享 TypeScript 类型 + utils/ # 共享工具函数(mergeRouteModules、mapTree 等) + +internal/ + lint-configs/ # ESLint、Prettier、Stylelint、commitlint 配置包 + node-utils/ # 构建时 Node 工具 + tailwind-config/ # 共享 Tailwind 配置 + tsconfig/ # 基础 tsconfig + vite-config/ # 共享 Vite 配置工厂 + 插件集合 + +scripts/ + vsh/ # CLI 工具(lint、check-dep、check-circular、publint) + turbo-run/ # 交互式 turbo 运行器 + +playground/ # 组件演示场 +docs/ # VitePress 文档 +``` + +## 核心架构说明 + +### 应用启动流程 + +每个应用的 `src/main.ts` 调用 `bootstrap(namespace)`(位于 `src/bootstrap.ts`),依次执行: + +1. 初始化**组件适配器**(`src/adapter/component/index.ts`)——将通用表单组件名映射到具体 UI 库的组件。 +2. 调用 `initSetupVbenForm()`(`src/adapter/form.ts`)配置通用表单系统。 +3. 依次初始化 i18n、Pinia stores、权限指令、Tippy、路由、MotionPlugin,最后挂载到 `#app`。 + +### 偏好设置系统 + +`@vben/preferences` 导出单例 `preferences`(`PreferenceManager`)。它是响应式的,自动持久化到 localStorage(以应用 namespace 为前缀),并驱动主题 CSS 变量的更新。各应用在 `src/preferences.ts` 中调用 `defineOverridesPreferences()` 覆盖默认值,无需修改核心代码。 + +### 权限/访问系统 + +`@vben/access`(`packages/effects/access`)支持三种访问模式: + +- **frontend**:根据用户角色过滤静态路由。 +- **backend**:从接口(`getAllMenusApi`)获取菜单并动态注册路由。 +- **mixed**:同时使用以上两种方式。 + +路由守卫(`src/router/guard.ts`)在登录后首次导航时调用 `generateAccess()`,将结果存入 `useAccessStore`,再重定向到目标页。`v-access` 指令和 `` 组件用于按权限码或角色控制 UI 元素显示。 + +### 请求客户端 + +`@vben/request` 将 Axios 封装为 `RequestClient`。每个应用在 `src/api/request.ts` 中创建自己的实例,挂载以下拦截器: + +- **请求拦截**:自动附加 Bearer Token 和 Accept-Language 头。 +- **`defaultResponseInterceptor`**:解包 `{ code, data, message }` 响应格式。 +- **`authenticateResponseInterceptor`**:处理 401,自动刷新 token 或跳转登录。 +- **`errorMessageResponseInterceptor`**:调用 `message.error()` 显示错误。 + +在 API 文件中从 `#/api/request` 引入 `requestClient`(自动解包响应)或 `baseRequestClient`(原始响应)。 + +### 路由组织 + +- `src/router/routes/modules/*.ts`:需要权限验证的动态路由。 +- `src/router/routes/core/`:始终可访问的路由(登录页、404 等)。 +- `mergeRouteModules(import.meta.glob(...))` 用于聚合路由模块文件。 +- 动态路由在运行时由权限系统注册。 + +### 适配器模式 + +每个 UI 库应用在 `src/adapter/` 下提供适配器,将 `@vben/common-ui` 的通用 form/modal/drawer 组件桥接到具体组件库。这是 `web-antd`、`web-ele` 等应用之间的主要差异所在。 + +### 全局 Pinia Store + +- `useAccessStore`:token、路由、菜单、锁屏、登录过期状态。 +- `useUserStore`:用户信息、角色、homePath。 +- `useTabbarStore`:已打开标签页管理。 + +所有 store 通过 `@vben/stores` 的 `initStores(app, { namespace })` 统一初始化。 + +### Mock 后端 + +`apps/backend-mock` 是一个 Nitro 服务器,可单独启动:`pnpm -F @vben/backend-mock start`。Vite 开发服务器通过 `vite.config.ts`(位于 `internal/vite-config`)将 API 请求代理到该服务。 + +## 开发约定 + +- **路径别名**:`#/*` 指向各应用的 `./src/*`(在 `package.json#imports` 中定义)。 +- **依赖版本管理**:内部包使用 `workspace:*`,第三方包使用 `catalog:`(版本集中在 `pnpm-workspace.yaml#catalog` 中管理)。 +- **提交规范**:遵循 Conventional Commits(`feat`、`fix`、`chore`、`docs`、`refactor`、`perf`、`test`、`ci`、`style`、`types`、`revert`),由 lefthook + commitlint 强制执行。 +- **pre-commit 钩子**(lefthook):自动对暂存文件执行 prettier + eslint + stylelint,推荐使用 `pnpm commit`(czg)提交。 +- **新增页面**:在 `src/views/` 下创建 `.vue` 文件,在 `src/router/routes/modules/` 下添加路由模块;若使用 backend 模式,还需确保后端接口返回对应菜单数据。 +- **国际化**:统一使用 `$t('key')`,locale 文件位于 `packages/locales/`,项目级国际化文件位于 `src/locales/langs`。 diff --git a/apps/backend-mock/api/table/list.ts b/apps/backend-mock/api/table/list.ts index 6664b583e..f81571e3b 100644 --- a/apps/backend-mock/api/table/list.ts +++ b/apps/backend-mock/api/table/list.ts @@ -79,7 +79,7 @@ export default eventHandler(async (event) => { const aValue = a[sortKey] as unknown; const bValue = b[sortKey] as unknown; - let result = 0; + let result: number; if (typeof aValue === 'number' && typeof bValue === 'number') { result = aValue - bValue; diff --git a/apps/web-antd/postcss.config.mjs b/apps/web-antd/postcss.config.mjs deleted file mode 100644 index 3d8070455..000000000 --- a/apps/web-antd/postcss.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-antd/src/store/auth.ts b/apps/web-antd/src/store/auth.ts index bd496d1ee..4b93784c8 100644 --- a/apps/web-antd/src/store/auth.ts +++ b/apps/web-antd/src/store/auth.ts @@ -98,8 +98,7 @@ export const useAuthStore = defineStore('auth', () => { } async function fetchUserInfo() { - let userInfo: null | UserInfo = null; - userInfo = await getUserInfoApi(); + const userInfo = await getUserInfoApi(); userStore.setUserInfo(userInfo); return userInfo; } diff --git a/apps/web-antd/src/views/dashboard/analytics/index.vue b/apps/web-antd/src/views/dashboard/analytics/index.vue index 5e3d6d285..e794c99a9 100644 --- a/apps/web-antd/src/views/dashboard/analytics/index.vue +++ b/apps/web-antd/src/views/dashboard/analytics/index.vue @@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
- + - + diff --git a/apps/web-antd/tailwind.config.mjs b/apps/web-antd/tailwind.config.mjs deleted file mode 100644 index f17f556fa..000000000 --- a/apps/web-antd/tailwind.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config'; diff --git a/apps/web-antdv-next/postcss.config.mjs b/apps/web-antdv-next/postcss.config.mjs deleted file mode 100644 index 3d8070455..000000000 --- a/apps/web-antdv-next/postcss.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-antdv-next/src/store/auth.ts b/apps/web-antdv-next/src/store/auth.ts index 6f7a3750c..90af35c8d 100644 --- a/apps/web-antdv-next/src/store/auth.ts +++ b/apps/web-antdv-next/src/store/auth.ts @@ -98,8 +98,7 @@ export const useAuthStore = defineStore('auth', () => { } async function fetchUserInfo() { - let userInfo: null | UserInfo = null; - userInfo = await getUserInfoApi(); + const userInfo = await getUserInfoApi(); userStore.setUserInfo(userInfo); return userInfo; } diff --git a/apps/web-antdv-next/src/views/dashboard/analytics/index.vue b/apps/web-antdv-next/src/views/dashboard/analytics/index.vue index 5e3d6d285..e794c99a9 100644 --- a/apps/web-antdv-next/src/views/dashboard/analytics/index.vue +++ b/apps/web-antdv-next/src/views/dashboard/analytics/index.vue @@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
- + - + diff --git a/apps/web-antdv-next/tailwind.config.mjs b/apps/web-antdv-next/tailwind.config.mjs deleted file mode 100644 index f17f556fa..000000000 --- a/apps/web-antdv-next/tailwind.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config'; diff --git a/apps/web-ele/postcss.config.mjs b/apps/web-ele/postcss.config.mjs deleted file mode 100644 index 3d8070455..000000000 --- a/apps/web-ele/postcss.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-ele/src/store/auth.ts b/apps/web-ele/src/store/auth.ts index 74fadfe24..bb37051ad 100644 --- a/apps/web-ele/src/store/auth.ts +++ b/apps/web-ele/src/store/auth.ts @@ -99,8 +99,7 @@ export const useAuthStore = defineStore('auth', () => { } async function fetchUserInfo() { - let userInfo: null | UserInfo = null; - userInfo = await getUserInfoApi(); + const userInfo = await getUserInfoApi(); userStore.setUserInfo(userInfo); return userInfo; } diff --git a/apps/web-ele/src/views/dashboard/analytics/index.vue b/apps/web-ele/src/views/dashboard/analytics/index.vue index 5e3d6d285..e794c99a9 100644 --- a/apps/web-ele/src/views/dashboard/analytics/index.vue +++ b/apps/web-ele/src/views/dashboard/analytics/index.vue @@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
- + - + diff --git a/apps/web-ele/src/views/demos/element/index.vue b/apps/web-ele/src/views/demos/element/index.vue index 0a7012d63..8d391700f 100644 --- a/apps/web-ele/src/views/demos/element/index.vue +++ b/apps/web-ele/src/views/demos/element/index.vue @@ -102,9 +102,7 @@ const segmentedOptions = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; -
- 一些演示的内容 -
+
一些演示的内容
diff --git a/apps/web-ele/tailwind.config.mjs b/apps/web-ele/tailwind.config.mjs deleted file mode 100644 index f17f556fa..000000000 --- a/apps/web-ele/tailwind.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config'; diff --git a/apps/web-naive/postcss.config.mjs b/apps/web-naive/postcss.config.mjs deleted file mode 100644 index 3d8070455..000000000 --- a/apps/web-naive/postcss.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-naive/src/store/auth.ts b/apps/web-naive/src/store/auth.ts index 0ff050b3b..5e5cf655b 100644 --- a/apps/web-naive/src/store/auth.ts +++ b/apps/web-naive/src/store/auth.ts @@ -99,8 +99,7 @@ export const useAuthStore = defineStore('auth', () => { } async function fetchUserInfo() { - let userInfo: null | UserInfo = null; - userInfo = await getUserInfoApi(); + const userInfo = await getUserInfoApi(); userStore.setUserInfo(userInfo); return userInfo; } diff --git a/apps/web-naive/src/views/dashboard/analytics/index.vue b/apps/web-naive/src/views/dashboard/analytics/index.vue index 5e3d6d285..e794c99a9 100644 --- a/apps/web-naive/src/views/dashboard/analytics/index.vue +++ b/apps/web-naive/src/views/dashboard/analytics/index.vue @@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
- + - + diff --git a/apps/web-naive/tailwind.config.mjs b/apps/web-naive/tailwind.config.mjs deleted file mode 100644 index f17f556fa..000000000 --- a/apps/web-naive/tailwind.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config'; diff --git a/apps/web-tdesign/postcss.config.mjs b/apps/web-tdesign/postcss.config.mjs deleted file mode 100644 index 3d8070455..000000000 --- a/apps/web-tdesign/postcss.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config/postcss'; diff --git a/apps/web-tdesign/src/store/auth.ts b/apps/web-tdesign/src/store/auth.ts index b3b4b7494..7f8d8f9b2 100644 --- a/apps/web-tdesign/src/store/auth.ts +++ b/apps/web-tdesign/src/store/auth.ts @@ -97,8 +97,7 @@ export const useAuthStore = defineStore('auth', () => { } async function fetchUserInfo() { - let userInfo: null | UserInfo = null; - userInfo = await getUserInfoApi(); + const userInfo = await getUserInfoApi(); userStore.setUserInfo(userInfo); return userInfo; } diff --git a/apps/web-tdesign/src/views/dashboard/analytics/index.vue b/apps/web-tdesign/src/views/dashboard/analytics/index.vue index 5e3d6d285..e794c99a9 100644 --- a/apps/web-tdesign/src/views/dashboard/analytics/index.vue +++ b/apps/web-tdesign/src/views/dashboard/analytics/index.vue @@ -76,10 +76,10 @@ const chartTabs: TabOption[] = [
- + - + diff --git a/apps/web-tdesign/src/views/demos/tdesign/index.vue b/apps/web-tdesign/src/views/demos/tdesign/index.vue index 27299dea5..f6342ae32 100644 --- a/apps/web-tdesign/src/views/demos/tdesign/index.vue +++ b/apps/web-tdesign/src/views/demos/tdesign/index.vue @@ -38,7 +38,7 @@ function notify(type: NotificationType) { description="支持多语言,主题功能集成切换等" title="TDesign Vue组件使用演示" > - + @@ -46,7 +46,7 @@ function notify(type: NotificationType) { - + @@ -55,7 +55,7 @@ function notify(type: NotificationType) { - + diff --git a/apps/web-tdesign/tailwind.config.mjs b/apps/web-tdesign/tailwind.config.mjs deleted file mode 100644 index f17f556fa..000000000 --- a/apps/web-tdesign/tailwind.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default } from '@vben/tailwind-config'; diff --git a/cspell.json b/cspell.json index 0c05c512d..08b165070 100644 --- a/cspell.json +++ b/cspell.json @@ -13,6 +13,7 @@ "brotli", "cascader", "clsx", + "dedup", "defu", "demi", "dotenv", @@ -41,6 +42,7 @@ "noreferrer", "nprogress", "nuxt", + "organisation", "pinia", "prefixs", "publint", @@ -51,6 +53,7 @@ "sonner", "sortablejs", "styl", + "tabler", "taze", "tdesign", "ui-kit", diff --git a/docs/.vitepress/components/demo-preview.vue b/docs/.vitepress/components/demo-preview.vue index 983a95062..cca9c749d 100644 --- a/docs/.vitepress/components/demo-preview.vue +++ b/docs/.vitepress/components/demo-preview.vue @@ -27,7 +27,7 @@ const parsedFiles = computed(() => {
- + ERROR: The preview directory does not exist. Please check the 'dir' diff --git a/docs/.vitepress/components/preview-group.vue b/docs/.vitepress/components/preview-group.vue index e08e921aa..ccf1f7e87 100644 --- a/docs/.vitepress/components/preview-group.vue +++ b/docs/.vitepress/components/preview-group.vue @@ -56,15 +56,15 @@ const toggleOpen = () => {