Browse Source

feat: 1、完善tree组件的全选状态不正确、全选没有label、item内容超长导致复选框对齐错乱、item内容超长没有tips无法看到完整内容的问题 (#7915)

Co-authored-by: PanFu <panfu@zhihuaai.com>
PanFu 4 tuần trước cách đây
mục cha
commit
42d82875ce

+ 56 - 12
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<Recordable<any>>) {
   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<Recordable<any>>) {
   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"
         />
-        <Checkbox
-          v-if="multiple"
-          @click.stop
-          @update:model-value="
-            (checked: boolean | 'indeterminate') =>
-              checked === true ? checkAll() : unCheckAll()
-          "
-        />
+        <div class="flex items-center gap-1 item-all-checkbox">
+          <Checkbox
+            v-if="multiple"
+            :model-value="selectAllStatus"
+            :indeterminate="selectAllStatus === 'indeterminate'"
+            @click.stop
+            @update:model-value="onSelectAllChange"
+          />
+          <span v-if="selectAllLabel">{{ selectAllLabel }}</span>
+        </div>
       </div>
     </div>
     <TransitionGroup :name="transition ? 'fade' : ''">
@@ -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"
       >
+        <!-- class="hover:ring-2" 鼠标移动上去时2px的圆环边框 -->
         <ChevronRight
           v-if="
             item.hasChildren &&
@@ -389,7 +418,7 @@ defineExpose({
           "
         />
         <div v-else class="h-4 w-4"></div>
-        <div class="flex items-center gap-1">
+        <div class="flex items-center gap-1 item-checkbox">
           <Checkbox
             v-if="multiple"
             :model-value="isSelected && !isNodeDisabled(item)"
@@ -407,7 +436,8 @@ defineExpose({
             "
           />
           <div
-            class="flex items-center gap-1"
+            class="flex items-center gap-1 item-checkbox"
+            :title="get(item.value, labelField)"
             @click="
               (event: MouseEvent) => {
                 if (isNodeDisabled(item)) {
@@ -457,6 +487,20 @@ defineExpose({
   border: 1px solid #666;
 }
 
+.item-checkbox {
+  width: 100%;
+  overflow: hidden;
+}
+
+.item-all-checkbox {
+  width: 100%;
+  overflow: hidden;
+
+  .text-label {
+    margin-left: 8px;
+  }
+}
+
 /* 1. 声明过渡效果 */
 .fade-move,
 .fade-enter-active,

+ 2 - 0
packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts

@@ -31,6 +31,8 @@ export interface TreeProps {
   labelField?: string;
   /** 是否多选 */
   multiple?: boolean;
+  /** 选择全部时的文字 */
+  selectAllLabel?: string;
   /** 显示由iconField指定的图标 */
   showIcon?: boolean;
   /** 启用展开收缩动画 */