From 04fbb7a5566e8c5c65e4387780e204c299787a42 Mon Sep 17 00:00:00 2001 From: xingyu Date: Sun, 31 May 2026 15:18:46 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=8D=87=E7=BA=A7=20shadcn-vue=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=88=B0v4=E6=9C=80=E6=96=B0=E7=89=88=20(#79?= =?UTF-8?q?72)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: useStore is deprecated * chore: update deps * feat: 升级shadcn-ui v4 * fix: workbench style * feat: 升级shadcn-ui v4 step2 * feat: 升级shadcn-ui v4 step3 * chore: 升级shadcn v4 * fix: pagination * fix: dark style * fix: doc import * feat: 增加详情组件,参考 antdv-next * docs: descriptions docs * docs: Browser Support * feat: add table action * feat: icon use vbenIcon * fix: type error * fix: dropdown popConfirm * feat: 使用默认的文字交互 * feat: 优化渲染性能 --- README.ja-JP.md | 2 +- README.md | 2 +- README.zh-CN.md | 2 +- .../src/views/dashboard/workspace/index.vue | 4 +- docs/.vitepress/components/preview-group.vue | 2 +- docs/.vitepress/config/en.mts | 8 + docs/.vitepress/config/zh.mts | 8 + docs/package.json | 2 +- .../components/common-ui/vben-descriptions.md | 102 + .../components/common-ui/vben-table-action.md | 165 + docs/src/demos/vben-alert/prompt/index.vue | 2 +- .../demos/vben-descriptions/basic/index.vue | 18 + .../vben-descriptions/bordered/index.vue | 22 + .../demos/vben-descriptions/custom/index.vue | 17 + .../demos/vben-descriptions/size/index.vue | 35 + .../demos/vben-descriptions/span/index.vue | 15 + .../vben-descriptions/vertical/index.vue | 13 + .../demos/vben-table-action/basic/index.vue | 28 + .../vben-table-action/dropdown/index.vue | 44 + .../vben-table-action/permission/index.vue | 28 + .../vben-table-action/popconfirm/index.vue | 32 + .../demos/vben-table-action/tooltip/index.vue | 17 + .../components/common-ui/vben-descriptions.md | 102 + .../components/common-ui/vben-table-action.md | 165 + docs/src/en/guide/introduction/vben.md | 2 +- docs/src/guide/introduction/vben.md | 2 +- package.json | 2 +- packages/@core/base/icons/package.json | 2 +- packages/@core/base/icons/src/lucide.ts | 3 +- .../src/use-simple-locale/messages.ts | 2 + .../@core/ui-kit/form-ui/src/use-vben-form.ts | 4 +- .../ui-kit/popup-ui/src/drawer/drawer.vue | 5 +- .../ui-kit/popup-ui/src/drawer/use-drawer.ts | 4 +- .../ui-kit/popup-ui/src/modal/use-modal.ts | 4 +- packages/@core/ui-kit/shadcn-ui/package.json | 2 +- .../src/components/back-top/back-top.vue | 2 +- .../shadcn-ui/src/components/button/button.ts | 6 +- .../src/components/button/icon-button.vue | 4 +- .../collapsible/collapsible-params.vue | 2 +- .../components/collapsible/collapsible.vue | 2 +- .../descriptions/descriptions-cell.vue | 129 + .../descriptions/descriptions-item.vue | 57 + .../descriptions/descriptions-row.vue | 112 + .../components/descriptions/descriptions.vue | 91 + .../src/components/descriptions/index.ts | 4 + .../src/components/descriptions/types.ts | 93 + .../descriptions/use-descriptions.ts | 216 + .../ui-kit/shadcn-ui/src/components/index.ts | 2 + .../src/components/pin-input/input.vue | 4 +- .../src/components/scrollbar/scrollbar.vue | 2 +- .../src/components/segmented/segmented.vue | 2 +- .../table-action/action-dropdown-item.vue | 116 + .../components/table-action/action-item.vue | 97 + .../src/components/table-action/index.ts | 3 + .../components/table-action/table-action.vue | 216 + .../src/components/table-action/types.ts | 75 + .../src/components/tooltip/help-tooltip.vue | 2 +- .../shadcn-ui/src/ui/accordion/Accordion.vue | 4 +- .../src/ui/accordion/AccordionContent.vue | 14 +- .../src/ui/accordion/AccordionItem.vue | 22 +- .../src/ui/accordion/AccordionTrigger.vue | 20 +- .../src/ui/alert-dialog/AlertDialog.vue | 8 +- .../src/ui/alert-dialog/AlertDialogAction.vue | 18 +- .../src/ui/alert-dialog/AlertDialogCancel.vue | 20 +- .../ui/alert-dialog/AlertDialogContent.vue | 23 +- .../alert-dialog/AlertDialogDescription.vue | 16 +- .../src/ui/alert-dialog/AlertDialogFooter.vue | 20 + .../src/ui/alert-dialog/AlertDialogHeader.vue | 18 + .../ui/alert-dialog/AlertDialogOverlay.vue | 8 - .../src/ui/alert-dialog/AlertDialogTitle.vue | 24 +- .../ui/alert-dialog/AlertDialogTrigger.vue | 13 + .../shadcn-ui/src/ui/alert-dialog/index.ts | 3 + .../ui-kit/shadcn-ui/src/ui/avatar/Avatar.vue | 28 +- .../src/ui/avatar/AvatarFallback.vue | 22 +- .../shadcn-ui/src/ui/avatar/AvatarImage.vue | 8 +- .../ui-kit/shadcn-ui/src/ui/avatar/avatar.ts | 22 - .../ui-kit/shadcn-ui/src/ui/avatar/index.ts | 1 - .../ui-kit/shadcn-ui/src/ui/badge/Badge.vue | 31 +- .../ui-kit/shadcn-ui/src/ui/badge/badge.ts | 20 +- .../src/ui/breadcrumb/Breadcrumb.vue | 6 +- .../src/ui/breadcrumb/BreadcrumbEllipsis.vue | 13 +- .../src/ui/breadcrumb/BreadcrumbItem.vue | 9 +- .../src/ui/breadcrumb/BreadcrumbLink.vue | 12 +- .../src/ui/breadcrumb/BreadcrumbList.vue | 5 +- .../src/ui/breadcrumb/BreadcrumbPage.vue | 11 +- .../src/ui/breadcrumb/BreadcrumbSeparator.vue | 11 +- .../ui-kit/shadcn-ui/src/ui/button/Button.vue | 14 +- .../ui-kit/shadcn-ui/src/ui/button/button.ts | 3 +- .../ui-kit/shadcn-ui/src/ui/button/index.ts | 2 - .../ui-kit/shadcn-ui/src/ui/button/types.ts | 20 - .../ui-kit/shadcn-ui/src/ui/card/Card.vue | 7 +- .../shadcn-ui/src/ui/card/CardAction.vue | 23 + .../shadcn-ui/src/ui/card/CardContent.vue | 6 +- .../shadcn-ui/src/ui/card/CardDescription.vue | 9 +- .../shadcn-ui/src/ui/card/CardFooter.vue | 9 +- .../shadcn-ui/src/ui/card/CardHeader.vue | 14 +- .../shadcn-ui/src/ui/card/CardTitle.vue | 9 +- .../ui-kit/shadcn-ui/src/ui/card/index.ts | 1 + .../shadcn-ui/src/ui/checkbox/Checkbox.vue | 24 +- .../src/ui/context-menu/ContextMenu.vue | 2 +- .../context-menu/ContextMenuCheckboxItem.vue | 26 +- .../ui/context-menu/ContextMenuContent.vue | 22 +- .../src/ui/context-menu/ContextMenuGroup.vue | 2 +- .../src/ui/context-menu/ContextMenuItem.vue | 30 +- .../src/ui/context-menu/ContextMenuLabel.vue | 16 +- .../src/ui/context-menu/ContextMenuPortal.vue | 2 +- .../ui/context-menu/ContextMenuRadioGroup.vue | 5 +- .../ui/context-menu/ContextMenuRadioItem.vue | 26 +- .../ui/context-menu/ContextMenuSeparator.vue | 14 +- .../ui/context-menu/ContextMenuShortcut.vue | 5 +- .../src/ui/context-menu/ContextMenuSub.vue | 2 +- .../ui/context-menu/ContextMenuSubContent.vue | 23 +- .../ui/context-menu/ContextMenuSubTrigger.vue | 20 +- .../ui/context-menu/ContextMenuTrigger.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/dialog/Dialog.vue | 4 +- .../shadcn-ui/src/ui/dialog/DialogClose.vue | 2 +- .../shadcn-ui/src/ui/dialog/DialogContent.vue | 2 +- .../src/ui/dialog/DialogDescription.vue | 14 +- .../shadcn-ui/src/ui/dialog/DialogFooter.vue | 22 +- .../shadcn-ui/src/ui/dialog/DialogHeader.vue | 7 +- .../shadcn-ui/src/ui/dialog/DialogOverlay.vue | 30 +- .../src/ui/dialog/DialogScrollContent.vue | 26 +- .../shadcn-ui/src/ui/dialog/DialogTitle.vue | 18 +- .../shadcn-ui/src/ui/dialog/DialogTrigger.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/dialog/index.ts | 1 + .../src/ui/dropdown-menu/DropdownMenu.vue | 12 +- .../DropdownMenuCheckboxItem.vue | 26 +- .../ui/dropdown-menu/DropdownMenuContent.vue | 20 +- .../ui/dropdown-menu/DropdownMenuGroup.vue | 2 +- .../src/ui/dropdown-menu/DropdownMenuItem.vue | 30 +- .../ui/dropdown-menu/DropdownMenuLabel.vue | 16 +- .../dropdown-menu/DropdownMenuRadioGroup.vue | 5 +- .../dropdown-menu/DropdownMenuRadioItem.vue | 26 +- .../dropdown-menu/DropdownMenuSeparator.vue | 12 +- .../ui/dropdown-menu/DropdownMenuShortcut.vue | 11 +- .../src/ui/dropdown-menu/DropdownMenuSub.vue | 8 +- .../dropdown-menu/DropdownMenuSubContent.vue | 23 +- .../dropdown-menu/DropdownMenuSubTrigger.vue | 25 +- .../ui/dropdown-menu/DropdownMenuTrigger.vue | 5 +- .../shadcn-ui/src/ui/form/FormControl.vue | 3 +- .../shadcn-ui/src/ui/form/FormDescription.vue | 5 +- .../ui-kit/shadcn-ui/src/ui/form/FormItem.vue | 12 +- .../shadcn-ui/src/ui/form/FormLabel.vue | 13 +- .../shadcn-ui/src/ui/form/FormMessage.vue | 15 +- .../shadcn-ui/src/ui/form/useFormField.ts | 26 +- .../shadcn-ui/src/ui/hover-card/HoverCard.vue | 4 +- .../src/ui/hover-card/HoverCardContent.vue | 20 +- .../src/ui/hover-card/HoverCardTrigger.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/input/Input.vue | 4 +- .../ui-kit/shadcn-ui/src/ui/label/Label.vue | 14 +- .../src/ui/number-field/NumberField.vue | 21 +- .../ui/number-field/NumberFieldContent.vue | 6 +- .../ui/number-field/NumberFieldDecrement.vue | 19 +- .../ui/number-field/NumberFieldIncrement.vue | 17 +- .../src/ui/number-field/NumberFieldInput.vue | 11 +- .../src/ui/pagination/Pagination.vue | 31 + .../src/ui/pagination/PaginationContent.vue | 27 + .../src/ui/pagination/PaginationEllipsis.vue | 16 +- .../src/ui/pagination/PaginationFirst.vue | 39 +- .../src/ui/pagination/PaginationItem.vue | 47 + .../src/ui/pagination/PaginationLast.vue | 39 +- .../src/ui/pagination/PaginationNext.vue | 41 +- .../src/ui/pagination/PaginationPrev.vue | 35 - .../src/ui/pagination/PaginationPrevious.vue | 50 + .../shadcn-ui/src/ui/pagination/index.ts | 10 +- .../shadcn-ui/src/ui/pin-input/PinInput.vue | 28 +- .../src/ui/pin-input/PinInputGroup.vue | 13 +- .../src/ui/pin-input/PinInputInput.vue | 30 - .../src/ui/pin-input/PinInputSeparator.vue | 6 +- .../src/ui/pin-input/PinInputSlot.vue | 31 + .../shadcn-ui/src/ui/pin-input/index.ts | 2 +- .../shadcn-ui/src/ui/popover/Popover.vue | 4 +- .../src/ui/popover/PopoverAnchor.vue | 13 + .../src/ui/popover/PopoverContent.vue | 16 +- .../src/ui/popover/PopoverTrigger.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/popover/index.ts | 2 +- .../src/ui/radio-group/RadioGroup.vue | 22 +- .../src/ui/radio-group/RadioGroupItem.vue | 29 +- .../src/ui/resizable/ResizableHandle.vue | 20 +- .../src/ui/resizable/ResizablePanel.vue | 32 + .../src/ui/resizable/ResizablePanelGroup.vue | 17 +- .../shadcn-ui/src/ui/resizable/index.ts | 2 +- .../src/ui/scroll-area/ScrollArea.vue | 26 +- .../src/ui/scroll-area/ScrollBar.vue | 23 +- .../ui-kit/shadcn-ui/src/ui/select/Select.vue | 4 +- .../shadcn-ui/src/ui/select/SelectContent.vue | 21 +- .../shadcn-ui/src/ui/select/SelectGroup.vue | 14 +- .../shadcn-ui/src/ui/select/SelectItem.vue | 24 +- .../src/ui/select/SelectItemText.vue | 2 +- .../shadcn-ui/src/ui/select/SelectLabel.vue | 11 +- .../src/ui/select/SelectScrollDownButton.vue | 18 +- .../src/ui/select/SelectScrollUpButton.vue | 18 +- .../src/ui/select/SelectSeparator.vue | 16 +- .../shadcn-ui/src/ui/select/SelectTrigger.vue | 28 +- .../shadcn-ui/src/ui/select/SelectValue.vue | 2 +- .../shadcn-ui/src/ui/separator/Separator.vue | 37 +- .../ui-kit/shadcn-ui/src/ui/sheet/Sheet.vue | 4 +- .../shadcn-ui/src/ui/sheet/SheetClose.vue | 2 +- .../src/ui/sheet/SheetDescription.vue | 14 +- .../shadcn-ui/src/ui/sheet/SheetFooter.vue | 9 +- .../shadcn-ui/src/ui/sheet/SheetHeader.vue | 9 +- .../shadcn-ui/src/ui/sheet/SheetTitle.vue | 14 +- .../shadcn-ui/src/ui/sheet/SheetTrigger.vue | 2 +- .../ui-kit/shadcn-ui/src/ui/switch/Switch.vue | 24 +- .../ui-kit/shadcn-ui/src/ui/tabs/Tabs.vue | 21 +- .../shadcn-ui/src/ui/tabs/TabsContent.vue | 21 +- .../ui-kit/shadcn-ui/src/ui/tabs/TabsList.vue | 16 +- .../shadcn-ui/src/ui/tabs/TabsTrigger.vue | 18 +- .../shadcn-ui/src/ui/textarea/Textarea.vue | 9 +- .../src/ui/toggle-group/ToggleGroup.vue | 52 +- .../src/ui/toggle-group/ToggleGroupItem.vue | 26 +- .../ui-kit/shadcn-ui/src/ui/toggle/Toggle.vue | 24 +- .../ui-kit/shadcn-ui/src/ui/toggle/toggle.ts | 22 +- .../shadcn-ui/src/ui/tooltip/Tooltip.vue | 4 +- .../src/ui/tooltip/TooltipContent.vue | 3 +- .../src/ui/tooltip/TooltipProvider.vue | 2 +- .../src/ui/tooltip/TooltipTrigger.vue | 2 +- .../tabs-ui/src/components/tabs/tabs.vue | 2 +- .../components/icon-picker/icon-picker.vue | 16 +- .../effects/common-ui/src/components/index.ts | 9 + .../dashboard/workbench/workbench-project.vue | 2 +- .../workbench/workbench-quick-nav.vue | 2 +- .../ui/dashboard/workbench/workbench-todo.vue | 2 +- .../dashboard/workbench/workbench-trends.vue | 2 +- packages/locales/src/langs/en-US/common.json | 1 + packages/locales/src/langs/zh-CN/common.json | 1 + packages/styles/src/naive/index.css | 1 - playground/src/adapter/vxe-table.ts | 33 +- playground/src/locales/langs/en-US/demos.json | 2 +- playground/src/locales/langs/zh-CN/demos.json | 2 +- .../src/views/dashboard/workspace/index.vue | 2 +- .../examples/form/scroll-to-error-test.vue | 2 +- .../src/views/examples/layout/col-page.vue | 10 +- playground/src/views/system/user/data.ts | 51 +- playground/src/views/system/user/list.vue | 62 +- .../src/views/system/user/modules/detail.vue | 28 + .../src/views/system/user/modules/form.vue | 24 +- pnpm-lock.yaml | 4365 +++++++++-------- pnpm-workspace.yaml | 42 +- 239 files changed, 6015 insertions(+), 3205 deletions(-) create mode 100644 docs/src/components/common-ui/vben-descriptions.md create mode 100644 docs/src/components/common-ui/vben-table-action.md create mode 100644 docs/src/demos/vben-descriptions/basic/index.vue create mode 100644 docs/src/demos/vben-descriptions/bordered/index.vue create mode 100644 docs/src/demos/vben-descriptions/custom/index.vue create mode 100644 docs/src/demos/vben-descriptions/size/index.vue create mode 100644 docs/src/demos/vben-descriptions/span/index.vue create mode 100644 docs/src/demos/vben-descriptions/vertical/index.vue create mode 100644 docs/src/demos/vben-table-action/basic/index.vue create mode 100644 docs/src/demos/vben-table-action/dropdown/index.vue create mode 100644 docs/src/demos/vben-table-action/permission/index.vue create mode 100644 docs/src/demos/vben-table-action/popconfirm/index.vue create mode 100644 docs/src/demos/vben-table-action/tooltip/index.vue create mode 100644 docs/src/en/components/common-ui/vben-descriptions.md create mode 100644 docs/src/en/components/common-ui/vben-table-action.md create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-cell.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-item.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-row.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/descriptions/index.ts create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/descriptions/types.ts create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/descriptions/use-descriptions.ts create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/table-action/action-dropdown-item.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/table-action/action-item.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/table-action/index.ts create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/table-action/table-action.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/components/table-action/types.ts create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogFooter.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogHeader.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogOverlay.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/alert-dialog/AlertDialogTrigger.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/avatar/avatar.ts delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/button/types.ts create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/card/CardAction.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/pagination/Pagination.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationContent.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationItem.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrev.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/pagination/PaginationPrevious.vue delete mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputInput.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/pin-input/PinInputSlot.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/popover/PopoverAnchor.vue create mode 100644 packages/@core/ui-kit/shadcn-ui/src/ui/resizable/ResizablePanel.vue create mode 100644 playground/src/views/system/user/modules/detail.vue diff --git a/README.ja-JP.md b/README.ja-JP.md index 4ce285a74..ca0a9775d 100644 --- a/README.ja-JP.md +++ b/README.ja-JP.md @@ -114,7 +114,7 @@ pnpm build ## ブラウザサポート -ローカル開発には `Chrome 80+` ブラウザを推奨します +Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+ モダンブラウザをサポートし、IEはサポートしません diff --git a/README.md b/README.md index ce8e89758..e1c9f29cd 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ Reference [vue](https://github.com/vuejs/vue/blob/dev/.github/COMMIT_CONVENTION. ## Browser Support -The `Chrome 80+` browser is recommended for local development +Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+ Support modern browsers, not IE diff --git a/README.zh-CN.md b/README.zh-CN.md index d3193ef65..da7ec5fa8 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -114,7 +114,7 @@ pnpm build ## 浏览器支持 -本地开发推荐使用 `Chrome 80+` 浏览器 +Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+ 支持现代浏览器,不支持 IE diff --git a/apps/web-antd/src/views/dashboard/workspace/index.vue b/apps/web-antd/src/views/dashboard/workspace/index.vue index b95d61381..e99a37cdf 100644 --- a/apps/web-antd/src/views/dashboard/workspace/index.vue +++ b/apps/web-antd/src/views/dashboard/workspace/index.vue @@ -244,7 +244,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) { -
+
@@ -252,7 +252,7 @@ function navTo(nav: WorkbenchProjectItem | WorkbenchQuickNavItem) {
diff --git a/docs/.vitepress/components/preview-group.vue b/docs/.vitepress/components/preview-group.vue index ccf1f7e87..f816dd348 100644 --- a/docs/.vitepress/components/preview-group.vue +++ b/docs/.vitepress/components/preview-group.vue @@ -5,7 +5,7 @@ import { computed, ref, useSlots } from 'vue'; import { VbenTooltip } from '@vben-core/shadcn-ui'; -import { Code } from 'lucide-vue-next'; +import { Code } from '@lucide/vue'; import { TabsContent, TabsIndicator, diff --git a/docs/.vitepress/config/en.mts b/docs/.vitepress/config/en.mts index fcbe50edf..9154d2fa4 100644 --- a/docs/.vitepress/config/en.mts +++ b/docs/.vitepress/config/en.mts @@ -198,6 +198,14 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] { link: 'common-ui/vben-ellipsis-text', text: 'EllipsisText', }, + { + link: 'common-ui/vben-descriptions', + text: 'Descriptions', + }, + { + link: 'common-ui/vben-table-action', + text: 'TableAction', + }, { link: 'common-ui/vben-cropper', text: 'Cropper', diff --git a/docs/.vitepress/config/zh.mts b/docs/.vitepress/config/zh.mts index c90418b8d..fa97f00cb 100644 --- a/docs/.vitepress/config/zh.mts +++ b/docs/.vitepress/config/zh.mts @@ -196,6 +196,14 @@ function sidebarComponents(): DefaultTheme.SidebarItem[] { link: 'common-ui/vben-ellipsis-text', text: 'EllipsisText 省略文本', }, + { + link: 'common-ui/vben-descriptions', + text: 'Descriptions 描述列表', + }, + { + link: 'common-ui/vben-table-action', + text: 'TableAction 表格操作', + }, { link: 'common-ui/vben-cropper', text: 'Cropper 图片裁剪', diff --git a/docs/package.json b/docs/package.json index 400fb2a6b..674e8ea33 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,13 +15,13 @@ } }, "dependencies": { + "@lucide/vue": "catalog:", "@vben-core/shadcn-ui": "workspace:*", "@vben/common-ui": "workspace:*", "@vben/locales": "workspace:*", "@vben/plugins": "workspace:*", "@vben/styles": "workspace:*", "antdv-next": "catalog:", - "lucide-vue-next": "catalog:", "medium-zoom": "catalog:", "reka-ui": "catalog:", "vitepress-plugin-group-icons": "catalog:" diff --git a/docs/src/components/common-ui/vben-descriptions.md b/docs/src/components/common-ui/vben-descriptions.md new file mode 100644 index 000000000..fa48c7257 --- /dev/null +++ b/docs/src/components/common-ui/vben-descriptions.md @@ -0,0 +1,102 @@ +--- +outline: deep +--- + +# Vben Descriptions 描述列表 + +`Descriptions` 用于成组展示只读的字段信息,常用于详情页、信息预览等场景。组件基于 shadcn-ui 构建,API 参考 Ant Design Vue 的 Descriptions,支持响应式列数、跨列、边框、垂直布局等能力。 + +> 如果文档内没有覆盖到你需要的细节,可以结合在线示例一起查看。 + +::: info 写在前面 + +组件提供两种使用方式:通过 `items` 数据驱动(推荐),或通过子组件 `VbenDescriptionsItem` 声明列表项。两者可按需选择,`items` 优先级更高。::: + +## 基础用法 + +通过 `items` 传入字段数组,每项包含 `label` 与 `content`。默认按断点自适应列数(`xs` 1 列、`sm` 2 列、`md` 及以上 3 列)。 + + + +## 带边框 + +设置 `bordered` 展示边框样式,配合 `title` 标题与 `#extra` 插槽(位于标题右侧的操作区域)。 + + + +## 垂直布局 + +通过 `layout="vertical"` 让标签位于内容上方。 + + + +## 不同尺寸 + +通过 `size` 设置 `small`、`middle`、`large` 三种尺寸。 + + + +## 跨列与响应式 + +单项通过 `span` 设置跨列数,`'filled'` 表示占满当前行剩余空间;`column` 支持传入按断点配置的对象实现响应式列数。 + + + +## 子组件用法 + +不传 `items` 时,可在默认插槽中使用 `VbenDescriptionsItem` 声明列表项,内容支持默认插槽或 `#content` 插槽自定义。 + + + +## API + +### Descriptions Props + +| 属性名 | 描述 | 类型 | 默认值 | +| --- | --- | --- | --- | +| items | 数据驱动的列表项;不传则读取默认插槽 | `DescriptionsItemType[]` | - | +| bordered | 是否展示边框 | `boolean` | `false` | +| column | 一行的列数,支持按断点配置 | `number \| Partial>` | `{ xs: 1, sm: 2, md: 3, xxxl: 4 }` | +| layout | 布局方式 | `'horizontal' \| 'vertical'` | `'horizontal'` | +| size | 尺寸 | `'small' \| 'middle' \| 'large'` | `'middle'` | +| colon | 是否显示冒号(仅非边框的水平布局生效) | `boolean` | `true` | +| title | 标题 | `string` | - | +| extra | 标题右侧的操作区域 | `string` | - | +| labelStyle | 统一的标签样式 | `CSSProperties` | - | +| contentStyle | 统一的内容样式 | `CSSProperties` | - | +| class | 根节点自定义类名 | `string` | - | + +### Descriptions Slots + +| 插槽名 | 描述 | +| ------- | ---------------------------------- | +| title | 自定义标题 | +| extra | 自定义标题右侧操作区域 | +| default | 放置 `VbenDescriptionsItem` 子组件 | + +### DescriptionsItem + +`items` 数组中的每一项,或子组件 `VbenDescriptionsItem` 的属性。 + +| 属性名 | 描述 | 类型 | 默认值 | +| --- | --- | --- | --- | +| label | 标签 | `string \| number \| (() => VNode) \| Component` | - | +| content | 内容 | `string \| number \| (() => VNode) \| Component` | - | +| span | 跨列数,`'filled'` 占满当前行剩余 | `number \| 'filled' \| Partial>` | `1` | +| labelStyle | 标签样式 | `CSSProperties` | - | +| contentStyle | 内容样式 | `CSSProperties` | - | +| key | 唯一标识 | `string \| number` | - | + +### DescriptionsItem Slots + +仅子组件用法可用。 + +| 插槽名 | 描述 | +| ------- | ------------------------ | +| default | 内容(等价于 `content`) | +| content | 自定义内容 | +| label | 自定义标签 | + +::: tip Breakpoint + +响应式断点 `Breakpoint` 取值为 `'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'xxxl'`,断点像素与 Ant Design 一致(`sm` 576、`md` 768、`lg` 992、`xl` 1200、`xxl` 1600、`xxxl` 2000)。::: diff --git a/docs/src/components/common-ui/vben-table-action.md b/docs/src/components/common-ui/vben-table-action.md new file mode 100644 index 000000000..6ffe34dc7 --- /dev/null +++ b/docs/src/components/common-ui/vben-table-action.md @@ -0,0 +1,165 @@ +--- +outline: deep +--- + +# Vben TableAction 表格操作 + +`TableAction` 用于在表格操作列中渲染一组操作按钮,参考 vben2 的 TableAction 设计。基于 shadcn-ui 构建,支持权限控制、气泡确认、提示、下拉「更多」、分割线等能力,可在表格内外任意场景复用。 + +> 如果文档内没有覆盖到你需要的细节,可以结合在线示例一起查看。 + +::: info 写在前面 + +组件本身不依赖任何业务逻辑(不直接读取权限 store),权限通过注入 `hasPermission` 实现,从而保持核心层零耦合、可跨框架复用。在 vxe-table 中推荐通过列插槽(`slots: { default: 'action' }`)在页面里渲染,不改变表格原有的渲染机制。::: + +## 基础用法 + +通过 `actions` 传入操作项数组,每项包含 `text`、`onClick` 等;`danger` 标记危险操作,`divider` 显示按钮间分割线。 + + + +## 提示 + +通过 `tooltip` 为操作项添加提示,支持字符串或 `{ content, side }` 配置。 + + + +## 气泡确认 + +通过 `popConfirm` 开启点击前的气泡确认,常用于删除等危险操作。 + + + +## 更多下拉 + +通过 `dropdownActions` 将次要操作收纳到「更多」下拉中,`moreText` 可自定义按钮文案。 + + + +## 权限控制 + +为操作项设置 `auth` 权限码,并注入 `hasPermission` 判断函数,无权限的操作会被隐藏。 + + + +## 在 vxe-table 中使用 + +不改变 vxe-table 原有渲染方式,推荐在列配置中声明插槽,在页面通过插槽渲染。 + +::: tip 推荐:使用适配器封装的版本项目的 `#/adapter/vxe-table` 已对 `VbenTableAction` 做了二次封装,内部统一注入了 `hasPermission`(基于 `useAccess().hasAccessByCodes`)。因此从适配器引入时**无需再传入 `:has-permission`**,只需通过操作项的 `auth` 字段声明权限码即可。::: + +```ts +// data.ts —— 列配置声明插槽 +{ + align: 'center', + field: 'operation', + fixed: 'right', + slots: { default: 'action' }, + title: $t('system.user.operation'), + width: 180, +} +``` + +```vue + + + + +``` + +若直接从 `@vben/common-ui` 引入核心组件(不经过适配器),组件不依赖任何业务逻辑,需自行注入 `hasPermission`: + +```vue + + + +``` + +## API + +### TableAction Props + +| 属性名 | 描述 | 类型 | 默认值 | +| --- | --- | --- | --- | +| actions | 主操作按钮 | `ActionItem[]` | `[]` | +| dropdownActions | 「更多」下拉中的操作 | `ActionItem[]` | `[]` | +| align | 对齐方式 | `'start' \| 'center' \| 'end'` | `'end'` | +| divider | 按钮之间是否显示分割线 | `boolean` | `false` | +| moreText | 「更多」按钮文案(提供时显示在图标右侧) | `string` | - | +| hasPermission | 权限判断函数,返回 `false` 则隐藏对应 `auth` 的操作(从 `#/adapter/vxe-table` 引入时已自动注入,无需手动传入) | `(auth?: string \| string[]) => boolean` | - | +| class | 根节点自定义类名 | `string` | - | + +### ActionItem + +| 属性名 | 描述 | 类型 | 默认值 | +| --- | --- | --- | --- | +| text | 按钮文本 | `string` | - | +| icon | 图标组件 | `string`\| `VbenIcon` | - | +| onClick | 点击回调 | `() => void` | - | +| auth | 权限码,配合 `hasPermission` 过滤 | `string \| string[]` | - | +| ifShow | 是否显示 | `boolean \| (() => boolean)` | `true` | +| disabled | 是否禁用 | `boolean` | `false` | +| loading | 加载状态 | `boolean` | `false` | +| danger | 危险操作(红色文字) | `boolean` | `false` | +| tooltip | 提示 | `string \| { content: string; side?: 'top' \| 'bottom' \| 'left' \| 'right' }` | - | +| popConfirm | 气泡确认 | `TableActionPopConfirm` | - | +| variant | 按钮样式变体 | `ButtonVariants['variant']` | `'link'` | +| size | 按钮尺寸 | `ButtonVariants['size']` | `'sm'` | +| key | 唯一标识 | `string \| number` | - | + +### TableActionPopConfirm + +| 属性名 | 描述 | 类型 | 默认值 | +| --- | --- | --- | --- | +| title | 提示标题 | `string` | `'Are you sure?'` | +| okText | 确认按钮文案 | `string` | `'OK'` | +| cancelText | 取消按钮文案 | `string` | `'Cancel'` | +| confirm | 确认回调;未提供时回退到 `action.onClick` | `() => void` | - | diff --git a/docs/src/demos/vben-alert/prompt/index.vue b/docs/src/demos/vben-alert/prompt/index.vue index 5109c4791..7e807d065 100644 --- a/docs/src/demos/vben-alert/prompt/index.vue +++ b/docs/src/demos/vben-alert/prompt/index.vue @@ -3,8 +3,8 @@ import { h } from 'vue'; import { alert, prompt, useAlertContext, VbenButton } from '@vben/common-ui'; +import { BadgeJapaneseYen } from '@lucide/vue'; import { Input, RadioGroup, Select } from 'antdv-next'; -import { BadgeJapaneseYen } from 'lucide-vue-next'; function showPrompt() { prompt({ diff --git a/docs/src/demos/vben-descriptions/basic/index.vue b/docs/src/demos/vben-descriptions/basic/index.vue new file mode 100644 index 000000000..8b73fe637 --- /dev/null +++ b/docs/src/demos/vben-descriptions/basic/index.vue @@ -0,0 +1,18 @@ + + diff --git a/docs/src/demos/vben-descriptions/bordered/index.vue b/docs/src/demos/vben-descriptions/bordered/index.vue new file mode 100644 index 000000000..082157297 --- /dev/null +++ b/docs/src/demos/vben-descriptions/bordered/index.vue @@ -0,0 +1,22 @@ + + diff --git a/docs/src/demos/vben-descriptions/custom/index.vue b/docs/src/demos/vben-descriptions/custom/index.vue new file mode 100644 index 000000000..cd93ea0fc --- /dev/null +++ b/docs/src/demos/vben-descriptions/custom/index.vue @@ -0,0 +1,17 @@ + + diff --git a/docs/src/demos/vben-descriptions/size/index.vue b/docs/src/demos/vben-descriptions/size/index.vue new file mode 100644 index 000000000..d75a7e6b2 --- /dev/null +++ b/docs/src/demos/vben-descriptions/size/index.vue @@ -0,0 +1,35 @@ + + diff --git a/docs/src/demos/vben-descriptions/span/index.vue b/docs/src/demos/vben-descriptions/span/index.vue new file mode 100644 index 000000000..1cde5fea0 --- /dev/null +++ b/docs/src/demos/vben-descriptions/span/index.vue @@ -0,0 +1,15 @@ + + diff --git a/docs/src/demos/vben-descriptions/vertical/index.vue b/docs/src/demos/vben-descriptions/vertical/index.vue new file mode 100644 index 000000000..8c480835d --- /dev/null +++ b/docs/src/demos/vben-descriptions/vertical/index.vue @@ -0,0 +1,13 @@ + + diff --git a/docs/src/demos/vben-table-action/basic/index.vue b/docs/src/demos/vben-table-action/basic/index.vue new file mode 100644 index 000000000..b88cd4a19 --- /dev/null +++ b/docs/src/demos/vben-table-action/basic/index.vue @@ -0,0 +1,28 @@ + + diff --git a/docs/src/demos/vben-table-action/dropdown/index.vue b/docs/src/demos/vben-table-action/dropdown/index.vue new file mode 100644 index 000000000..b7664e51e --- /dev/null +++ b/docs/src/demos/vben-table-action/dropdown/index.vue @@ -0,0 +1,44 @@ + + diff --git a/docs/src/demos/vben-table-action/permission/index.vue b/docs/src/demos/vben-table-action/permission/index.vue new file mode 100644 index 000000000..bce245ca8 --- /dev/null +++ b/docs/src/demos/vben-table-action/permission/index.vue @@ -0,0 +1,28 @@ + + diff --git a/docs/src/demos/vben-table-action/popconfirm/index.vue b/docs/src/demos/vben-table-action/popconfirm/index.vue new file mode 100644 index 000000000..513430ced --- /dev/null +++ b/docs/src/demos/vben-table-action/popconfirm/index.vue @@ -0,0 +1,32 @@ + + diff --git a/docs/src/demos/vben-table-action/tooltip/index.vue b/docs/src/demos/vben-table-action/tooltip/index.vue new file mode 100644 index 000000000..2bd75e28b --- /dev/null +++ b/docs/src/demos/vben-table-action/tooltip/index.vue @@ -0,0 +1,17 @@ + + diff --git a/docs/src/en/components/common-ui/vben-descriptions.md b/docs/src/en/components/common-ui/vben-descriptions.md new file mode 100644 index 000000000..4685b1f5b --- /dev/null +++ b/docs/src/en/components/common-ui/vben-descriptions.md @@ -0,0 +1,102 @@ +--- +outline: deep +--- + +# Vben Descriptions + +`Descriptions` displays a group of read-only fields, commonly used on detail pages and information previews. It is built on shadcn-ui with an API modeled after Ant Design Vue's Descriptions, supporting responsive columns, column spanning, borders, and vertical layout. + +> If the documentation does not cover the details you need, please refer to the online examples. + +::: info Before you start + +The component supports two usages: data-driven via `items` (recommended), or declaring entries with the `VbenDescriptionsItem` child component. `items` takes precedence when both are provided. ::: + +## Basic Usage + +Pass an array of fields via `items`, each with a `label` and `content`. Columns adapt to breakpoints by default (1 column on `xs`, 2 on `sm`, 3 on `md` and above). + + + +## Bordered + +Set `bordered` for a bordered style, combined with the `title` prop and the `#extra` slot (an action area on the right of the title). + + + +## Vertical Layout + +Use `layout="vertical"` to place labels above their content. + + + +## Sizes + +Use `size` to switch between `small`, `middle`, and `large`. + + + +## Span & Responsive + +Set `span` on an item to span multiple columns; `'filled'` fills the remaining space of the current row. `column` accepts a breakpoint-keyed object for responsive columns. + + + +## Child Component Usage + +When `items` is omitted, declare entries with `VbenDescriptionsItem` in the default slot. Content can be customized via the default slot or the `#content` slot. + + + +## API + +### Descriptions Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| items | Data-driven entries; reads the default slot when omitted | `DescriptionsItemType[]` | - | +| bordered | Whether to show borders | `boolean` | `false` | +| column | Columns per row, supports breakpoint config | `number \| Partial>` | `{ xs: 1, sm: 2, md: 3, xxxl: 4 }` | +| layout | Layout direction | `'horizontal' \| 'vertical'` | `'horizontal'` | +| size | Size | `'small' \| 'middle' \| 'large'` | `'middle'` | +| colon | Show colon (only for non-bordered horizontal layout) | `boolean` | `true` | +| title | Title | `string` | - | +| extra | Action area on the right of the title | `string` | - | +| labelStyle | Shared label style | `CSSProperties` | - | +| contentStyle | Shared content style | `CSSProperties` | - | +| class | Custom class for the root node | `string` | - | + +### Descriptions Slots + +| Slot | Description | +| ------- | ------------------------------------- | +| title | Custom title | +| extra | Custom action area beside the title | +| default | Place `VbenDescriptionsItem` children | + +### DescriptionsItem + +Each entry in `items`, or the props of the `VbenDescriptionsItem` child component. + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| label | Label | `string \| number \| (() => VNode) \| Component` | - | +| content | Content | `string \| number \| (() => VNode) \| Component` | - | +| span | Columns to span, `'filled'` fills the rest of the row | `number \| 'filled' \| Partial>` | `1` | +| labelStyle | Label style | `CSSProperties` | - | +| contentStyle | Content style | `CSSProperties` | - | +| key | Unique key | `string \| number` | - | + +### DescriptionsItem Slots + +Available only for the child component usage. + +| Slot | Description | +| ------- | --------------------------------- | +| default | Content (equivalent to `content`) | +| content | Custom content | +| label | Custom label | + +::: tip Breakpoint + +The responsive `Breakpoint` is one of `'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'xxxl'`, with pixel values aligned with Ant Design (`sm` 576, `md` 768, `lg` 992, `xl` 1200, `xxl` 1600, `xxxl` 2000). ::: diff --git a/docs/src/en/components/common-ui/vben-table-action.md b/docs/src/en/components/common-ui/vben-table-action.md new file mode 100644 index 000000000..7c1e76553 --- /dev/null +++ b/docs/src/en/components/common-ui/vben-table-action.md @@ -0,0 +1,165 @@ +--- +outline: deep +--- + +# Vben TableAction + +`TableAction` renders a group of action buttons for table operation columns, inspired by the TableAction component from vben2. Built on shadcn-ui, it supports permission control, popconfirm, tooltips, a "more" dropdown, and dividers, and can be reused inside or outside tables. + +> If the documentation does not cover the details you need, please refer to the online examples. + +::: info Before you start + +The component carries no business logic (it does not read the permission store directly); permissions are handled by injecting `hasPermission`, keeping the core layer decoupled and reusable across frameworks. Inside vxe-table, the recommended approach is to render it via a column slot (`slots: { default: 'action' }`) on the page, without changing the table's original rendering mechanism. ::: + +## Basic Usage + +Pass an array of action items via `actions`, each with `text`, `onClick`, etc. `danger` marks destructive actions, and `divider` shows separators between buttons. + + + +## Tooltip + +Add a tooltip to an action via `tooltip`, accepting a string or a `{ content, side }` object. + + + +## PopConfirm + +Use `popConfirm` to require confirmation before the action runs, commonly used for destructive actions like delete. + + + +## More Dropdown + +Use `dropdownActions` to collapse secondary actions into a "more" dropdown. `moreText` customizes the button label. + + + +## Permission Control + +Set an `auth` code on an action and inject a `hasPermission` resolver; actions without permission are hidden. + + + +## Usage with vxe-table + +Without changing vxe-table's rendering mechanism, declare a slot in the column config and render it on the page. + +::: tip Recommended: use the adapter-wrapped version The project's `#/adapter/vxe-table` re-wraps `VbenTableAction` and injects `hasPermission` internally (based on `useAccess().hasAccessByCodes`). So when you import it from the adapter, **you no longer need to pass `:has-permission`** — just declare permission codes via the `auth` field of each action. ::: + +```ts +// data.ts — declare a slot in the column config +{ + align: 'center', + field: 'operation', + fixed: 'right', + slots: { default: 'action' }, + title: $t('system.user.operation'), + width: 180, +} +``` + +```vue + + + + +``` + +If you import the core component directly from `@vben/common-ui` (without going through the adapter), the component carries no business logic and you need to inject `hasPermission` yourself: + +```vue + + + +``` + +## API + +### TableAction Props + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| actions | Main action buttons | `ActionItem[]` | `[]` | +| dropdownActions | Actions inside the "more" dropdown | `ActionItem[]` | `[]` | +| align | Alignment | `'start' \| 'center' \| 'end'` | `'end'` | +| divider | Whether to show separators between buttons | `boolean` | `false` | +| moreText | Label for the "more" button (shown beside the icon) | `string` | - | +| hasPermission | Permission resolver; returning `false` hides the action with that `auth` (auto-injected when imported from `#/adapter/vxe-table`, no need to pass manually) | `(auth?: string \| string[]) => boolean` | - | +| class | Custom class for the root node | `string` | - | + +### ActionItem + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| text | Button text | `string` | - | +| icon | Icon component | `string` \| `VbenIcon` | - | +| onClick | Click callback | `() => void` | - | +| auth | Permission code, filtered by `hasPermission` | `string \| string[]` | - | +| ifShow | Whether to show | `boolean \| (() => boolean)` | `true` | +| disabled | Whether disabled | `boolean` | `false` | +| loading | Loading state | `boolean` | `false` | +| danger | Destructive action (red text) | `boolean` | `false` | +| tooltip | Tooltip | `string \| { content: string; side?: 'top' \| 'bottom' \| 'left' \| 'right' }` | - | +| popConfirm | PopConfirm | `TableActionPopConfirm` | - | +| variant | Button variant | `ButtonVariants['variant']` | `'link'` | +| size | Button size | `ButtonVariants['size']` | `'sm'` | +| key | Unique key | `string \| number` | - | + +### TableActionPopConfirm + +| Prop | Description | Type | Default | +| --- | --- | --- | --- | +| title | Confirm title | `string` | `'Are you sure?'` | +| okText | Confirm button text | `string` | `'OK'` | +| cancelText | Cancel button text | `string` | `'Cancel'` | +| confirm | Confirm callback; falls back to `action.onClick` if omitted | `() => void` | - | diff --git a/docs/src/en/guide/introduction/vben.md b/docs/src/en/guide/introduction/vben.md index 623a7ffe0..2e32d7787 100644 --- a/docs/src/en/guide/introduction/vben.md +++ b/docs/src/en/guide/introduction/vben.md @@ -26,7 +26,7 @@ ## Browser Support -- **Local development** is recommended using the **latest version of Chrome**. **Versions below Chrome 80 are not supported**. +- **Local development** is recommended using the **latest version of Chrome**. **Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+**. - **Production environment** supports modern browsers, IE is not supported. diff --git a/docs/src/guide/introduction/vben.md b/docs/src/guide/introduction/vben.md index 4f0f25978..513956d96 100644 --- a/docs/src/guide/introduction/vben.md +++ b/docs/src/guide/introduction/vben.md @@ -26,7 +26,7 @@ ## 浏览器支持 -- **本地开发**推荐使用`Chrome 最新版`浏览器,**不支持**`Chrome 80`以下版本。 +- **本地开发**推荐使用`Chrome 最新版`浏览器,**不支持**` Tailwind CSS v4.0 is designed for Safari 16.4+, Chrome 111+, and Firefox 128+。 - **生产环境**支持现代浏览器,不支持 IE。 diff --git a/package.json b/package.json index 29916365d..f8c4d52c7 100644 --- a/package.json +++ b/package.json @@ -106,5 +106,5 @@ "node": "^22.18.0 || ^24.0.0", "pnpm": ">=11.0.0" }, - "packageManager": "pnpm@11.2.2" + "packageManager": "pnpm@11.4.0" } diff --git a/packages/@core/base/icons/package.json b/packages/@core/base/icons/package.json index 558a5b7ca..23504aca4 100644 --- a/packages/@core/base/icons/package.json +++ b/packages/@core/base/icons/package.json @@ -36,7 +36,7 @@ }, "dependencies": { "@iconify/vue": "catalog:", - "lucide-vue-next": "catalog:", + "@lucide/vue": "catalog:", "vue": "catalog:" } } diff --git a/packages/@core/base/icons/src/lucide.ts b/packages/@core/base/icons/src/lucide.ts index acaecd92e..7edabcb63 100644 --- a/packages/@core/base/icons/src/lucide.ts +++ b/packages/@core/base/icons/src/lucide.ts @@ -35,7 +35,6 @@ export { EyeOff, FoldHorizontal, Fullscreen, - Github, Grid, Grip, GripVertical, @@ -93,4 +92,4 @@ export { Unlink2, UserRoundPen, X, -} from 'lucide-vue-next'; +} from '@lucide/vue'; diff --git a/packages/@core/composables/src/use-simple-locale/messages.ts b/packages/@core/composables/src/use-simple-locale/messages.ts index efc5c3cc7..671ae33c2 100644 --- a/packages/@core/composables/src/use-simple-locale/messages.ts +++ b/packages/@core/composables/src/use-simple-locale/messages.ts @@ -9,6 +9,7 @@ export const messages: Record> = { prompt: 'Prompt', reset: 'Reset', submit: 'Submit', + confirmTitle: 'Please Confirm', }, 'zh-CN': { cancel: '取消', @@ -18,6 +19,7 @@ export const messages: Record> = { prompt: '提示', reset: '重置', submit: '提交', + confirmTitle: '请确认', }, }; diff --git a/packages/@core/ui-kit/form-ui/src/use-vben-form.ts b/packages/@core/ui-kit/form-ui/src/use-vben-form.ts index 00c60d05b..da3c57d90 100644 --- a/packages/@core/ui-kit/form-ui/src/use-vben-form.ts +++ b/packages/@core/ui-kit/form-ui/src/use-vben-form.ts @@ -6,7 +6,7 @@ import type { import { defineComponent, h, isReactive, onBeforeUnmount, watch } from 'vue'; -import { useStore } from '@vben-core/shared/store'; +import { useSelector } from '@vben-core/shared/store'; import { FormApi } from './form-api'; import VbenUseForm from './vben-use-form.vue'; @@ -19,7 +19,7 @@ export function useVbenForm< const api = new FormApi(options as unknown as VbenFormProps); const extendedApi: ExtendedFormApi = api as never; extendedApi.useStore = (selector) => { - return useStore(api.store, selector); + return useSelector(api.store, selector); }; const Form = defineComponent( diff --git a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue index 0a7fd35f0..5abbf3823 100644 --- a/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue +++ b/packages/@core/ui-kit/popup-ui/src/drawer/drawer.vue @@ -54,7 +54,8 @@ const components = globalShareState.getComponents(); const id = useId(); provide('DISMISSABLE_DRAWER_ID', id); -// const wrapperRef = ref(); +// @ts-expect-error unused +const wrapperRef = ref(); const { $t } = useSimpleLocale(); const { isMobile } = useIsMobile(); @@ -285,8 +286,8 @@ const getForceMount = computed(() => { -
diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts b/packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts index a55a4d238..75769d349 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts +++ b/packages/@core/ui-kit/shadcn-ui/src/components/button/button.ts @@ -2,7 +2,7 @@ import type { AsTag } from 'reka-ui'; import type { Component } from 'vue'; -import type { ButtonVariants, ButtonVariantSize } from '../../ui'; +import type { ButtonVariants } from '../../ui'; export interface VbenButtonProps { /** @@ -19,8 +19,8 @@ export interface VbenButtonProps { class?: any; disabled?: boolean; loading?: boolean; - size?: ButtonVariantSize; - variant?: ButtonVariants; + size?: ButtonVariants['size']; + variant?: ButtonVariants['variant']; } export type CustomRenderType = (() => Component | string) | string; diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/button/icon-button.vue b/packages/@core/ui-kit/shadcn-ui/src/components/button/icon-button.vue index ac626c1b4..f9c7b0be9 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/button/icon-button.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/button/icon-button.vue @@ -16,7 +16,7 @@ interface Props extends VbenButtonProps { tooltip?: string; tooltipDelayDuration?: number; tooltipSide?: 'bottom' | 'left' | 'right' | 'top'; - variant?: ButtonVariants; + variant?: ButtonVariants['variant']; } const props = withDefaults(defineProps(), { @@ -24,7 +24,7 @@ const props = withDefaults(defineProps(), { onClick: () => {}, tooltipDelayDuration: 200, tooltipSide: 'bottom', - variant: 'icon', + variant: 'ghost', }); const slots = useSlots(); diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params.vue b/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params.vue index 8c0accac4..dd1cce797 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible-params.vue @@ -7,7 +7,7 @@ import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'; import { useNamespace } from '@vben-core/composables'; -import { ChevronsDown } from 'lucide-vue-next'; +import { ChevronsDown } from '@lucide/vue'; import { CollapsibleContent, CollapsibleRoot, diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue b/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue index f76dbf13e..d6e4e2ccc 100755 --- a/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/collapsible/collapsible.vue @@ -5,7 +5,7 @@ import type { ClassType } from '@vben-core/typings'; import { computed } from 'vue'; -import { ChevronsDown } from 'lucide-vue-next'; +import { ChevronsDown } from '@lucide/vue'; import { CollapsibleContent, CollapsibleRoot, diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-cell.vue b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-cell.vue new file mode 100644 index 000000000..a67f606d2 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-cell.vue @@ -0,0 +1,129 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-item.vue b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-item.vue new file mode 100644 index 000000000..677bb16c4 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-item.vue @@ -0,0 +1,57 @@ + diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-row.vue b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-row.vue new file mode 100644 index 000000000..645d2f3bc --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions-row.vue @@ -0,0 +1,112 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions.vue b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions.vue new file mode 100644 index 000000000..a170b5055 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/descriptions.vue @@ -0,0 +1,91 @@ + + + diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/index.ts b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/index.ts new file mode 100644 index 000000000..1003a73a3 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/index.ts @@ -0,0 +1,4 @@ +export { default as VbenDescriptionsItem } from './descriptions-item.vue'; +export { default as VbenDescriptions } from './descriptions.vue'; + +export * from './types'; diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/types.ts b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/types.ts new file mode 100644 index 000000000..0dd11c697 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/types.ts @@ -0,0 +1,93 @@ +import type { Component, CSSProperties } from 'vue'; + +/** 响应式断点,与 antdv-next 保持一致 */ +export type DescriptionsBreakpoint = + | 'lg' + | 'md' + | 'sm' + | 'xl' + | 'xs' + | 'xxl' + | 'xxxl'; + +/** 当前命中的断点集合 */ +export type ScreenMap = Partial>; + +export type DescriptionsLayout = 'horizontal' | 'vertical'; + +export type DescriptionsSize = 'large' | 'middle' | 'small'; + +/** 列数,可为固定数字或按断点配置 */ +export type DescriptionsColumn = + | number + | Partial>; + +/** 单项跨列,支持固定数字、'filled'(占满当前行剩余)或按断点配置 */ +export type DescriptionsItemSpan = + | 'filled' + | number + | Partial>; + +/** 可渲染内容:字符串/数字/渲染函数/组件 */ +export type DescriptionsRenderNode = (() => any) | Component | number | string; + +export interface DescriptionsItemType { + /** 内容 */ + content?: DescriptionsRenderNode; + /** 内容样式 */ + contentStyle?: CSSProperties; + /** 唯一 key */ + key?: number | string; + /** 标签 */ + label?: DescriptionsRenderNode; + /** 标签样式 */ + labelStyle?: CSSProperties; + /** 跨列 */ + span?: DescriptionsItemSpan; +} + +export interface DescriptionsProps { + /** 是否展示边框 */ + bordered?: boolean; + class?: any; + /** 是否显示冒号(仅非 bordered 的水平布局生效) */ + colon?: boolean; + /** 一行的列数 */ + column?: DescriptionsColumn; + /** 统一的内容样式 */ + contentStyle?: CSSProperties; + /** 操作区域,位于标题右侧 */ + extra?: string; + /** 数据驱动的列表项;不传则读取默认插槽中的 VbenDescriptionsItem */ + items?: DescriptionsItemType[]; + /** 统一的标签样式 */ + labelStyle?: CSSProperties; + /** 布局方式 */ + layout?: DescriptionsLayout; + /** 尺寸 */ + size?: DescriptionsSize; + /** 标题 */ + title?: string; +} + +export interface DescriptionsItemProps { + content?: DescriptionsRenderNode; + contentStyle?: CSSProperties; + label?: DescriptionsRenderNode; + labelStyle?: CSSProperties; + span?: DescriptionsItemSpan; +} + +/** 归一化后的内部项,span 已解析为数字 */ +export interface InternalDescriptionsItem { + _index?: number; + class?: string; + content?: DescriptionsRenderNode; + contentStyle?: CSSProperties; + filled?: boolean; + key?: number | string; + label?: DescriptionsRenderNode; + labelStyle?: CSSProperties; + span?: number; + style?: CSSProperties; +} diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/use-descriptions.ts b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/use-descriptions.ts new file mode 100644 index 000000000..2c1f04f41 --- /dev/null +++ b/packages/@core/ui-kit/shadcn-ui/src/components/descriptions/use-descriptions.ts @@ -0,0 +1,216 @@ +import type { VNode } from 'vue'; + +import type { + DescriptionsBreakpoint, + DescriptionsColumn, + DescriptionsItemType, + InternalDescriptionsItem, + ScreenMap, +} from './types'; + +import { Comment, computed, Fragment } from 'vue'; + +import { useBreakpoints } from '@vueuse/core'; + +/** 默认列数映射 */ +export const DEFAULT_COLUMN_MAP: Record = { + lg: 3, + md: 3, + sm: 2, + xl: 3, + xs: 1, + xxl: 3, + xxxl: 4, +}; + +/** 由大到小的断点顺序,matchScreen 按此顺序取第一个命中的值 */ +const RESPONSIVE_ARRAY: DescriptionsBreakpoint[] = [ + 'xxxl', + 'xxl', + 'xl', + 'lg', + 'md', + 'sm', + 'xs', +]; + +/** 断点像素值 */ +const BREAKPOINT_PX = { + sm: 576, + md: 768, + lg: 992, + xl: 1200, + xxl: 1600, + xxxl: 2000, +}; + +/** + * 在给定的断点配置中,按由大到小的顺序取第一个命中的值 + */ +export function matchScreen( + screens: ScreenMap, + screenSizes?: Partial>, +): number | undefined { + if (!screenSizes) return undefined; + for (const breakpoint of RESPONSIVE_ARRAY) { + if (screens[breakpoint] && screenSizes[breakpoint] !== undefined) { + return screenSizes[breakpoint]; + } + } + return undefined; +} + +/** + * 监听视口宽度,返回当前命中的断点集合 + */ +export function useScreens() { + const breakpoints = useBreakpoints(BREAKPOINT_PX); + return computed(() => ({ + lg: breakpoints.lg.value, + md: breakpoints.md.value, + sm: breakpoints.sm.value, + xl: breakpoints.xl.value, + xs: !breakpoints.sm.value, + xxl: breakpoints.xxl.value, + xxxl: breakpoints.xxxl.value, + })); +} + +/** + * 计算最终列数:固定数字直接返回,否则按断点解析 + */ +export function resolveColumn( + column: DescriptionsColumn | undefined, + screens: ScreenMap, +): number { + if (typeof column === 'number') return column; + return matchScreen(screens, { ...DEFAULT_COLUMN_MAP, ...column }) ?? 3; +} + +/** + * 归一化列表项:将 span 解析为数字,'filled' 标记为 filled + */ +export function normalizeItems( + items: DescriptionsItemType[], + screens: ScreenMap, +): InternalDescriptionsItem[] { + return items.map((item, index) => { + const { span, ...rest } = item; + if (span === 'filled') { + return { ...rest, _index: index, filled: true }; + } + return { + ...rest, + _index: index, + span: typeof span === 'number' ? span : matchScreen(screens, span), + }; + }); +} + +/** + * 行装箱算法:根据列数与各项 span 将列表项拆分为多行, + * 并补齐每行最后一项以占满列数。移植自 antdv-next useRow。 + */ +export function calcRows( + items: InternalDescriptionsItem[], + column: number, +): InternalDescriptionsItem[][] { + let rows: InternalDescriptionsItem[][] = []; + let tmpRow: InternalDescriptionsItem[] = []; + let count = 0; + + items.filter(Boolean).forEach((item) => { + const { filled, ...rest } = item; + // filled:占满当前行剩余,并立即换行 + if (filled) { + tmpRow.push(rest); + rows.push(tmpRow); + tmpRow = []; + count = 0; + return; + } + + const restSpan = column - count; + count += item.span || 1; + + if (count >= column) { + // 超出列数时,将当前项 span 收敛为剩余列数,避免溢出 + tmpRow.push(count > column ? { ...rest, span: restSpan } : rest); + rows.push(tmpRow); + tmpRow = []; + count = 0; + } else { + tmpRow.push(rest); + } + }); + + if (tmpRow.length > 0) rows.push(tmpRow); + + // 补齐:若一行总 span 不足列数,扩展最后一项 + rows = rows.map((row) => { + const total = row.reduce((acc, item) => acc + (item.span || 1), 0); + if (total < column) { + const last = row[row.length - 1]; + if (last) { + last.span = column - (total - (last.span || 1)); + } + } + return row; + }); + + return rows; +} + +/** 标记组件类型为 DescriptionsItem,便于从插槽 vnode 中识别 */ +export const DESCRIPTIONS_ITEM_NAME = 'VbenDescriptionsItem'; + +function isItemVNode(node: VNode): boolean { + const type = node.type as any; + return ( + !!type && + (type.__isDescriptionsItem === true || type.name === DESCRIPTIONS_ITEM_NAME) + ); +} + +function flattenVNodes(nodes: VNode[]): VNode[] { + const result: VNode[] = []; + for (const node of nodes) { + if (node.type === Fragment && Array.isArray(node.children)) { + result.push(...flattenVNodes(node.children as VNode[])); + } else if (node.type !== Comment) { + result.push(node); + } + } + return result; +} + +/** + * 从默认插槽的 vnode 中解析出列表项,支持 + * content 写法 + */ +export function parseItemsFromSlot(nodes: VNode[]): DescriptionsItemType[] { + return flattenVNodes(nodes) + .filter((node) => isItemVNode(node)) + .map((node) => { + const props = (node.props ?? {}) as Record; + const children = (node.children ?? {}) as Record; + const labelSlot = + typeof children.label === 'function' ? children.label : undefined; + const contentDefaultSlot = + typeof children.default === 'function' ? children.default : undefined; + const contentSlot = + typeof children.content === 'function' + ? children.content + : contentDefaultSlot; + return { + class: props.class, + content: contentSlot ?? props.content, + contentStyle: props.contentStyle ?? props['content-style'], + key: node.key ?? undefined, + label: labelSlot ?? props.label, + labelStyle: props.labelStyle ?? props['label-style'], + span: props.span, + style: props.style, + } as DescriptionsItemType; + }); +} diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/index.ts b/packages/@core/ui-kit/shadcn-ui/src/components/index.ts index be157b887..7e90a2b3e 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/index.ts +++ b/packages/@core/ui-kit/shadcn-ui/src/components/index.ts @@ -6,6 +6,7 @@ export * from './checkbox'; export * from './collapsible'; export * from './context-menu'; export * from './count-to-animator'; +export * from './descriptions'; export * from './dropdown-menu'; export * from './expandable-arrow'; export * from './full-screen'; @@ -21,4 +22,5 @@ export * from './segmented'; export * from './select'; export * from './spine-text'; export * from './spinner'; +export * from './table-action'; export * from './tooltip'; diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue b/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue index a7dc21cd7..e64e65575 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/pin-input/input.vue @@ -3,7 +3,7 @@ import type { PinInputProps } from './types'; import { computed, onBeforeUnmount, ref, useId, watch } from 'vue'; -import { PinInput, PinInputGroup, PinInputInput } from '../../ui'; +import { PinInput, PinInputGroup, PinInputSlot } from '../../ui'; import { VbenButton } from '../button'; defineOptions({ @@ -101,7 +101,7 @@ const pinType = 'text' as const; >
-
diff --git a/packages/@core/ui-kit/shadcn-ui/src/components/segmented/segmented.vue b/packages/@core/ui-kit/shadcn-ui/src/components/segmented/segmented.vue index ea0248ad9..88985565d 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/components/segmented/segmented.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/components/segmented/segmented.vue @@ -45,7 +45,7 @@ function activeClass(tab: string): string[] {