UNPKG

@ithinkdt/naive

Version:

iThinkDT Naive UI

246 lines (228 loc) 8.83 kB
import { defineComponent, reactive, watch } from 'vue' import { NIcon, NButton, NTree, NScrollbar } from 'ithinkdt-ui' import { walkTree } from '@ithinkdt/common' import { nanoid } from 'nanoid' import { IPlus, IPlus2, IClose } from './assets' export const DtTreeInput = defineComponent({ name: 'DtTreeInput', props: { value: { type: Array, default: () => [], }, onCreate: { type: Function, default: () => ({}), }, readonly: { type: Boolean, default: false, }, renderItem: { type: Function, required: true, }, creatable: { type: Function, default: () => true, }, removable: { type: Function, default: () => true, }, }, emits: { 'update-value': () => true, 'update:value': () => true, }, setup(props, { emit }) { const keyField = '__id' const emitValue = (v) => { emit('update:value', v) emit('update-value', v) } const onCreate = () => { const obj = props.onCreate() Object.defineProperty(obj, keyField, { value: nanoid(), enumerable: false, configurable: false, writable: true, }) return obj } const renderSuffix = ({ option }) => { return ( <div onClick={(e) => { e.stopImmediatePropagation() }} style="display: flex; gap: 6px; align-items: center; margin: 0 8px" > <NButton key="create" type="primary" text style="font-size: 22px" onClick={() => { walkTree(props.value, (it, i, p) => { if (it[keyField] === option[keyField]) { if (p) { p.children.splice(i + 1, 0, onCreate()) emitValue([...(props.value ?? [])]) } else { const v = [...props.value] v.splice(i + 1, 0, onCreate()) emitValue(v) } return false } }) }} > <NIcon> <IPlus /> </NIcon> </NButton> <NButton key="create-child" type="primary" text style="font-size: 20px" disabled={!props.creatable(option)} onClick={() => { option.children ||= [] option.children.push(onCreate()) emitValue([...(props.value ?? [])]) }} > <NIcon> <IPlus2 /> </NIcon> </NButton> <NButton key="delete" type="error" text style="font-size: 20px" disabled={!props.removable(option)} onClick={() => { walkTree(props.value, (it, i, p) => { if (it[keyField] === option[keyField]) { if (p) { p.children.splice(i, 1) if (p.children.length === 0) { delete p.children } emitValue([...(props.value ?? [])]) } else { const v = [...props.value] v.splice(i, 1) emitValue(v) } return false } }) }} > <NIcon> <IClose /> </NIcon> </NButton> </div> ) } watch( () => props.value, (v) => { walkTree(v, (it) => { if (it && !it[keyField]) { Object.defineProperty(it, keyField, { value: nanoid(), enumerable: false, configurable: false, writable: true, }) } }) }, { immediate: true, deep: false }, ) const findSiblingsAndIndex = (node, nodes) => { if (!nodes) return [] for (let i = 0; i < nodes.length; ++i) { const siblingNode = nodes[i] if (siblingNode.key === node.key) return [nodes, i] const [siblings, index] = findSiblingsAndIndex(node, siblingNode.children) if (siblings && index !== undefined) return [siblings, index] } return [] } const handleDrop = ({ node, dragNode, dropPosition }) => { const [dragNodeSiblings, dragNodeIndex] = findSiblingsAndIndex(dragNode, props.value) if (dragNodeSiblings === undefined || dragNodeIndex === undefined) return dragNodeSiblings.splice(dragNodeIndex, 1) switch (dropPosition) { case 'inside': { if (node.children) { node.children.unshift(dragNode) } else { node.children = [dragNode] } break } case 'before': { const [nodeSiblings, nodeIndex] = findSiblingsAndIndex(node, props.value) if (nodeSiblings === undefined || nodeIndex === undefined) return nodeSiblings.splice(nodeIndex, 0, dragNode) break } case 'after': { const [nodeSiblings, nodeIndex] = findSiblingsAndIndex(node, props.value) if (nodeSiblings === undefined || nodeIndex === undefined) return nodeSiblings.splice(nodeIndex + 1, 0, dragNode) break } // No default } emitValue([...props.value]) } return () => { const tree = props.value?.length ? ( <NTree data={props.value} keyField={keyField} draggable={!props.readonly} blockLine renderLabel={({ option }) => props.renderItem({ value: option, readonly: props.readonly })} renderSuffix={props.readonly ? undefined : renderSuffix} defaultExpandAll onDrop={handleDrop} ></NTree> ) : undefined if (props.readonly) return tree return ( <div style="display: flex; flex-direction: column"> <NButton dashed type="primary" onClick={() => { emitValue(reactive([...(props.value || []), onCreate()])) }} style="width: 160px; margin: 0 0 16px 30px" > {{ icon: () => ( <NIcon size={16}> <IPlus /> </NIcon> ), default: () => '添加', }} </NButton> <NScrollbar style="flex: 1 1 0%">{tree}</NScrollbar> </div> ) } }, })