From 42d82875cee41a9b7a617764b95143abb2c2b5bf Mon Sep 17 00:00:00 2001 From: PanFu Date: Sat, 16 May 2026 10:44:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=201=E3=80=81=E5=AE=8C=E5=96=84tree?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E7=9A=84=E5=85=A8=E9=80=89=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E4=B8=8D=E6=AD=A3=E7=A1=AE=E3=80=81=E5=85=A8=E9=80=89=E6=B2=A1?= =?UTF-8?q?=E6=9C=89label=E3=80=81item=E5=86=85=E5=AE=B9=E8=B6=85=E9=95=BF?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E5=A4=8D=E9=80=89=E6=A1=86=E5=AF=B9=E9=BD=90?= =?UTF-8?q?=E9=94=99=E4=B9=B1=E3=80=81item=E5=86=85=E5=AE=B9=E8=B6=85?= =?UTF-8?q?=E9=95=BF=E6=B2=A1=E6=9C=89tips=E6=97=A0=E6=B3=95=E7=9C=8B?= =?UTF-8?q?=E5=88=B0=E5=AE=8C=E6=95=B4=E5=86=85=E5=AE=B9=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98=20(#7915)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: PanFu --- .../ui-kit/shadcn-ui/src/ui/tree/tree.vue | 68 +++++++++++++++---- .../ui-kit/shadcn-ui/src/ui/tree/types.ts | 2 + 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue b/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue index 9ed9e3e9d..81d5e4eda 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tree/tree.vue @@ -6,7 +6,7 @@ import type { ClassType, Recordable } from '@vben-core/typings'; import type { TreeProps } from './types'; -import { onMounted, ref, watchEffect } from 'vue'; +import { computed, onMounted, ref, watchEffect } from 'vue'; import { ChevronRight, IconifyIcon } from '@vben-core/icons'; import { cn, get } from '@vben-core/shared/utils'; @@ -192,6 +192,32 @@ function isNodeDisabled(item: FlattenedItem>) { return props.disabled || get(item.value, props.disabledField); } +// 计算全选/半选状态 +const selectAllStatus = computed<'indeterminate' | boolean>(() => { + if (!props.multiple) return false; + if (!modelValue.value || !Array.isArray(modelValue.value)) return false; + + const allValues = flattenData.value + .filter((item) => !get(item.value, props.disabledField)) + .map((item) => get(item.value, props.valueField)); + + const selectedCount = allValues.filter((v) => + (modelValue.value as (number | string)[]).includes(v), + ).length; + + if (selectedCount === 0) return false; + if (selectedCount === allValues.length) return true; + return 'indeterminate'; +}); + +function onSelectAllChange(checked: 'indeterminate' | boolean) { + if (checked === true) { + checkAll(); + } else { + unCheckAll(); + } +} + function onToggle(item: FlattenedItem>) { emits('expand', item); } @@ -316,14 +342,16 @@ defineExpose({ :class="{ 'rotate-90': expanded?.length > 0 }" class="text-foreground/80 hover:text-foreground size-4 cursor-pointer transition" /> - +
+ + {{ selectAllLabel }} +
@@ -371,8 +399,9 @@ defineExpose({ !isNodeDisabled(item) && onToggle(item); } " - class="tree-node focus:ring-grass8 my-0.5 flex items-center rounded p-1 outline-hidden focus:ring-2" + class="tree-node focus:ring-grass8 my-0.5 flex items-center rounded p-1 outline-hidden" > +
-
+