diff --git a/packages/@core/base/icons/src/lucide.ts b/packages/@core/base/icons/src/lucide.ts index 70e6a426..a167aea0 100644 --- a/packages/@core/base/icons/src/lucide.ts +++ b/packages/@core/base/icons/src/lucide.ts @@ -32,6 +32,7 @@ export { Grip, GripVertical, Menu as IconDefault, + Inbox, Info, InspectionPanel, Languages, diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tree/index.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/tree/index.ts index 1abd4c50..45162b8a 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/tree/index.ts +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tree/index.ts @@ -1,2 +1,4 @@ export { default as VbenTree } from './tree.vue'; +export type { TreeProps } from './types'; +export { treePropsDefaults } from './types'; export type { FlattenedItem } from 'radix-vue'; 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 3e497c51..5f50d2df 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 @@ -14,25 +14,9 @@ import { cn, get } from '@vben-core/shared/utils'; import { TreeItem, TreeRoot } from 'radix-vue'; import { Checkbox } from '../checkbox'; +import { treePropsDefaults } from './types'; -const props = withDefaults(defineProps(), { - allowClear: false, - autoCheckParent: true, - bordered: false, - checkStrictly: false, - defaultExpandedKeys: () => [], - defaultExpandedLevel: 0, - disabled: false, - disabledField: 'disabled', - expanded: () => [], - iconField: 'icon', - labelField: 'label', - multiple: false, - showIcon: true, - transition: true, - valueField: 'value', - childrenField: 'children', -}); +const props = withDefaults(defineProps(), treePropsDefaults()); const emits = defineEmits<{ expand: [value: FlattenedItem>]; @@ -41,7 +25,9 @@ const emits = defineEmits<{ interface InnerFlattenItem, P = number | string> { hasChildren: boolean; + id: P; level: number; + parentId: null | P; parents: P[]; value: T; } @@ -50,24 +36,25 @@ function flatten, P = number | string>( items: T[], childrenField: string = 'children', level = 0, + parentId: null | P = null, parents: P[] = [], ): InnerFlattenItem[] { const result: InnerFlattenItem[] = []; items.forEach((item) => { const children = get(item, childrenField) as Array; - const val = { + const id = get(item, props.valueField) as P; + const val: InnerFlattenItem = { hasChildren: Array.isArray(children) && children.length > 0, + id, level, + parentId, parents: [...parents], value: item, }; result.push(val); if (val.hasChildren) result.push( - ...flatten(children, childrenField, level + 1, [ - ...parents, - get(item, props.valueField), - ]), + ...flatten(children, childrenField, level + 1, id, [...parents, id]), ); }); return result; @@ -103,15 +90,10 @@ function updateTreeValue() { treeValue.value = undefined; } else { if (Array.isArray(val)) { - let filteredValues = val.filter((v) => { + const filteredValues = val.filter((v) => { const item = getItemByValue(v); return item && !get(item, props.disabledField); }); - - if (!props.checkStrictly && props.autoCheckParent) { - filteredValues = processParentSelection(filteredValues); - } - treeValue.value = filteredValues.map((v) => getItemByValue(v)); if (filteredValues.length !== val.length) { @@ -128,35 +110,7 @@ function updateTreeValue() { } } } -function processParentSelection( - selectedValues: Array, -): Array { - if (props.checkStrictly) return selectedValues; - const result = [...selectedValues]; - - for (let i = result.length - 1; i >= 0; i--) { - const currentValue = result[i]; - if (currentValue === undefined) continue; - const currentItem = getItemByValue(currentValue); - - if (!currentItem) continue; - - const children = get(currentItem, props.childrenField); - if (Array.isArray(children) && children.length > 0) { - const hasSelectedChildren = children.some((child) => { - const childValue = get(child, props.valueField); - return result.includes(childValue); - }); - - if (!hasSelectedChildren) { - result.splice(i, 1); - } - } - } - - return result; -} function updateModelValue(val: Arrayable>) { if (Array.isArray(val)) { const filteredVal = val.filter((v) => !get(v, props.disabledField)); @@ -204,6 +158,22 @@ function collapseAll() { expanded.value = []; } +function checkAll() { + if (props.multiple) { + modelValue.value = flattenData.value.map((item) => + get(item.value, props.valueField), + ); + updateTreeValue(); + } +} + +function unCheckAll() { + if (props.multiple) { + modelValue.value = []; + updateTreeValue(); + } +} + function isNodeDisabled(item: FlattenedItem>) { return props.disabled || get(item.value, props.disabledField); } @@ -229,8 +199,45 @@ function onSelect(item: FlattenedItem>, isSelected: boolean) { ); }) ?.parents?.forEach((p) => { - if (Array.isArray(modelValue.value) && !modelValue.value.includes(p)) { - modelValue.value.push(p); + if (Array.isArray(modelValue.value) && !modelValue.value.includes(p)) { + modelValue.value.push(p); + } + }); + } + if ( + !props.checkStrictly && + props.multiple && + props.autoCheckParent && + !isSelected + ) { + flattenData.value + .find((i) => { + return ( + get(i.value, props.valueField) === get(item.value, props.valueField) + ); + }) + ?.parents?.reverse() + .forEach((p) => { + const children = flattenData.value.filter((i) => { + return ( + i.parents.length > 0 && + i.parents.includes(p) && + i.id !== item._id && + i.parentId === p + ); + }); + if (Array.isArray(modelValue.value)) { + const hasSelectedChild = children.some((child) => + (modelValue.value as unknown[]).includes( + get(child.value, props.valueField), + ), + ); + if (!hasSelectedChild) { + const index = modelValue.value.indexOf(p); + if (index !== -1) { + modelValue.value.splice(index, 1); + } + } } }); } @@ -243,6 +250,8 @@ defineExpose({ collapseNodes, expandAll, expandNodes, + checkAll, + unCheckAll, expandToLevel, getItemByValue, }); @@ -263,15 +272,41 @@ defineExpose({ v-slot="{ flattenItems }" :class=" cn( - 'text-blackA11 container select-none list-none rounded-lg p-2 text-sm font-medium', + 'text-blackA11 container select-none list-none rounded-lg text-sm font-medium', $attrs.class as unknown as ClassType, bordered ? 'border' : '', ) " > -
+
+
+
+ + +
+
-
- -
-
+
+ -
+
- - - {{ get(item.value, labelField) }} - + " + > + + + {{ get(item.value, labelField) }} + +
+
-
+
diff --git a/packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts b/packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts index 97b09139..72dc19c4 100644 --- a/packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts +++ b/packages/@core/ui-kit/shadcn-ui/src/ui/tree/types.ts @@ -40,3 +40,24 @@ export interface TreeProps { /** 值字段 */ valueField?: string; } + +export function treePropsDefaults() { + return { + allowClear: false, + autoCheckParent: true, + bordered: false, + checkStrictly: false, + defaultExpandedKeys: () => [], + defaultExpandedLevel: 0, + disabled: false, + disabledField: 'disabled', + expanded: () => [], + iconField: 'icon', + labelField: 'label', + multiple: false, + showIcon: true, + transition: true, + valueField: 'value', + childrenField: 'children', + }; +} diff --git a/packages/effects/common-ui/src/components/index.ts b/packages/effects/common-ui/src/components/index.ts index 543fcf3c..5914d44c 100644 --- a/packages/effects/common-ui/src/components/index.ts +++ b/packages/effects/common-ui/src/components/index.ts @@ -9,6 +9,7 @@ export * from './loading'; export * from './page'; export * from './resize'; export * from './tippy'; +export * from './tree'; export * from '@vben-core/form-ui'; export * from '@vben-core/popup-ui'; @@ -27,7 +28,6 @@ export { VbenPinInput, VbenSelect, VbenSpinner, - VbenTree, } from '@vben-core/shadcn-ui'; export type { FlattenedItem } from '@vben-core/shadcn-ui'; diff --git a/packages/effects/common-ui/src/components/tree/index.ts b/packages/effects/common-ui/src/components/tree/index.ts new file mode 100644 index 00000000..ce3bc5c6 --- /dev/null +++ b/packages/effects/common-ui/src/components/tree/index.ts @@ -0,0 +1 @@ +export { default as Tree } from './tree.vue'; diff --git a/packages/effects/common-ui/src/components/tree/tree.vue b/packages/effects/common-ui/src/components/tree/tree.vue new file mode 100644 index 00000000..1f2fcc17 --- /dev/null +++ b/packages/effects/common-ui/src/components/tree/tree.vue @@ -0,0 +1,25 @@ + + + diff --git a/playground/src/views/system/role/modules/form.vue b/playground/src/views/system/role/modules/form.vue index e3c1d02d..511fb04b 100644 --- a/playground/src/views/system/role/modules/form.vue +++ b/playground/src/views/system/role/modules/form.vue @@ -7,7 +7,7 @@ import type { SystemRoleApi } from '#/api/system/role'; import { computed, nextTick, ref } from 'vue'; -import { useVbenDrawer, VbenTree } from '@vben/common-ui'; +import { Tree, useVbenDrawer } from '@vben/common-ui'; import { IconifyIcon } from '@vben/icons'; import { Spin } from 'ant-design-vue'; @@ -92,9 +92,6 @@ function getNodeClass(node: Recordable) { const classes: string[] = []; if (node.value?.type === 'button') { classes.push('inline-flex'); - if (node.index % 3 >= 1) { - classes.push('!pl-0'); - } } return classes.join(' '); @@ -105,7 +102,7 @@ function getNodeClass(node: Recordable) {
- +