diff --git a/packages/devui-vue/devui/tree/src/components/icon-close.tsx b/packages/devui-vue/devui/tree/src/components/icon-close.tsx index b8e76af6fb..bb356a0cc6 100644 --- a/packages/devui-vue/devui/tree/src/components/icon-close.tsx +++ b/packages/devui-vue/devui/tree/src/components/icon-close.tsx @@ -1,6 +1,4 @@ -import { VNodeTypes } from 'vue'; - -export const IconClose = (): VNodeTypes => ( +export const IconClose = () => ( ( +export const IconOpen = () => ( , + }, + }, + setup(props) { + const { data } = toRefs(props); + + return () => { + return ( + + {data.value.isLeaf ? ( + + ) : data.value.expanded ? ( + + ) : ( + + )} + + ); + }; + }, +}); diff --git a/packages/devui-vue/devui/tree/src/components/tree-node.tsx b/packages/devui-vue/devui/tree/src/components/tree-node.tsx new file mode 100644 index 0000000000..26c472deb7 --- /dev/null +++ b/packages/devui-vue/devui/tree/src/components/tree-node.tsx @@ -0,0 +1,30 @@ +import { defineComponent, PropType, ref, toRefs } from 'vue'; +import { ITreeNode } from '../core/tree-factory-types'; +import DTreeNodeToggle from './tree-node-toggle'; + +export default defineComponent({ + name: 'DTreeNode', + props: { + data: { + type: Object as PropType, + }, + }, + setup(props) { + const { data } = toRefs(props); + + return () => { + return ( +
+
+
+ + {data.value.label} +
+
+
+ ); + }; + }, +}); diff --git a/packages/devui-vue/devui/tree/src/core/tree-factory-types.ts b/packages/devui-vue/devui/tree/src/core/tree-factory-types.ts index 378072dada..a031c778d1 100644 --- a/packages/devui-vue/devui/tree/src/core/tree-factory-types.ts +++ b/packages/devui-vue/devui/tree/src/core/tree-factory-types.ts @@ -1,3 +1,5 @@ +import { Ref } from 'vue'; + // 外部数据结构先只考虑嵌套结构 export interface ITreeNode { label: string; @@ -18,6 +20,47 @@ export interface IInnerTreeNode extends ITreeNode { level: number; idType?: 'random'; parentId?: string; + isLeaf?: boolean; } export type valueof = T[keyof T]; + +export interface IUseCore { + getLevel: (node: ITreeNode) => number; + getChildren: (node: ITreeNode) => IInnerTreeNode[]; + getIndex: (node: ITreeNode) => number; + getNode: (node: ITreeNode) => IInnerTreeNode; + setNodeValue: (node: IInnerTreeNode, key: keyof IInnerTreeNode, value: valueof) => void; + setTree: (newTree: ITreeNode[]) => void; +}; + +export interface IUseCheck { + checkNode: (node: IInnerTreeNode) => void; + uncheckNode: (node: IInnerTreeNode) => void; +} + +export interface IUseDisable { + disableSelectNode: (node: IInnerTreeNode) => void; + disableCheckNode: (node: IInnerTreeNode) => void; + disableToggleNode: (node: IInnerTreeNode) => void; +} + +export interface IUseOperate { + insertBefore: (parentNode: ITreeNode, node: ITreeNode, referenceNode: ITreeNode, cut: boolean) => void; + removeNode: (node: ITreeNode) => void; + editNode: (node: ITreeNode, label: string) => void; +} + +export interface IUseSelect { + selectNode: (node: IInnerTreeNode) => void; +} + +export interface IUseToggle { + expandNode: (node: ITreeNode) => void; + collapseNode: (node: ITreeNode) => void; + toggleNode: (node: ITreeNode) => void; +} + +export type IUseTree = { + treeData: Ref +} & IUseCore & IUseCheck & IUseDisable & IUseOperate & IUseSelect & IUseToggle; diff --git a/packages/devui-vue/devui/tree/src/core/tree-factory.ts b/packages/devui-vue/devui/tree/src/core/tree-factory.ts index 33b3f592e1..8fe062afea 100644 --- a/packages/devui-vue/devui/tree/src/core/tree-factory.ts +++ b/packages/devui-vue/devui/tree/src/core/tree-factory.ts @@ -16,7 +16,7 @@ export default class TreeFactory { this.setTree(tree); } - getTree(flat?: boolean = false): IInnerTreeNode[] { + getTree(flat?: boolean = true): IInnerTreeNode[] { if (flat) { return this._innerTree; } else { diff --git a/packages/devui-vue/devui/tree/src/core/use-check.ts b/packages/devui-vue/devui/tree/src/core/use-check.ts new file mode 100644 index 0000000000..6650683eda --- /dev/null +++ b/packages/devui-vue/devui/tree/src/core/use-check.ts @@ -0,0 +1,21 @@ +import { Ref } from 'vue'; +import { IInnerTreeNode, IUseCore } from './tree-factory-types'; + +export default function useCheck(data: Ref, core: IUseCore) { + console.log('useCheck:', data, data.value); + + const { setNodeValue } = core; + + const checkNode = (node: IInnerTreeNode): void => { + setNodeValue(node, 'checked', true); + } + + const uncheckNode = (node: IInnerTreeNode): void => { + setNodeValue(node, 'checked', false); + } + + return { + checkNode, + uncheckNode, + } +} \ No newline at end of file diff --git a/packages/devui-vue/devui/tree/src/core/use-core.ts b/packages/devui-vue/devui/tree/src/core/use-core.ts new file mode 100644 index 0000000000..23270d6a21 --- /dev/null +++ b/packages/devui-vue/devui/tree/src/core/use-core.ts @@ -0,0 +1,48 @@ +import { Ref } from 'vue'; +import { IInnerTreeNode, ITreeNode, IUseCore, valueof } from './tree-factory-types'; +import { generateInnerTree } from './utils'; + +export default function useCore(data: Ref): IUseCore { + console.log('useCore:', data, data.value); + + const getLevel = (node: ITreeNode): number => { + return data.value.find((item) => item.id === node.id).level; + } + + const getChildren = (node: ITreeNode): IInnerTreeNode[] => { + let result = []; + const startIndex = data.value.findIndex((item) => item.id === node.id); + + for (let i = startIndex + 1; i < data.value.length && getLevel(node) < data.value[i].level; i++) { + result.push(data.value[i]); + } + console.log('result:', result); + + return result; + } + + const getIndex = (node: ITreeNode): number => { + return data.value.findIndex((item) => item.id === node.id); + } + + const getNode = (node: ITreeNode): IInnerTreeNode => { + return data.value.find((item) => item.id === node.id); + } + + const setNodeValue = (node: IInnerTreeNode, key: keyof IInnerTreeNode, value: valueof): void => { + data.value[getIndex(node)][key] = value; + } + + const setTree = (newTree: ITreeNode[]): void => { + data.value = generateInnerTree(newTree); + } + + return { + getLevel, + getChildren, + getIndex, + getNode, + setNodeValue, + setTree, + } +} \ No newline at end of file diff --git a/packages/devui-vue/devui/tree/src/core/use-disable.ts b/packages/devui-vue/devui/tree/src/core/use-disable.ts new file mode 100644 index 0000000000..d524c48ccf --- /dev/null +++ b/packages/devui-vue/devui/tree/src/core/use-disable.ts @@ -0,0 +1,26 @@ +import { Ref } from 'vue'; +import { IInnerTreeNode, IUseCore } from './tree-factory-types'; + +export default function useDisable(data: Ref, core: IUseCore) { + console.log('useDisable:', data, data.value); + + const { setNodeValue } = core; + + const disableSelectNode = (node: IInnerTreeNode): void => { + setNodeValue(node, 'disableSelect', true); + } + + const disableCheckNode = (node: IInnerTreeNode): void => { + setNodeValue(node, 'disableCheck', true); + } + + const disableToggleNode = (node: IInnerTreeNode): void => { + setNodeValue(node, 'disableToggle', true); + } + + return { + disableSelectNode, + disableCheckNode, + disableToggleNode, + } +} \ No newline at end of file diff --git a/packages/devui-vue/devui/tree/src/core/use-operate.ts b/packages/devui-vue/devui/tree/src/core/use-operate.ts new file mode 100644 index 0000000000..a01a7d9b3f --- /dev/null +++ b/packages/devui-vue/devui/tree/src/core/use-operate.ts @@ -0,0 +1,28 @@ +import { Ref } from 'vue'; +import { IInnerTreeNode, ITreeNode, IUseCore } from './tree-factory-types'; + +export default function useOperate(data: Ref, core: IUseCore) { + console.log('useOperate:', data, data.value); + + const { setNodeValue, getChildren } = core; + + const insertBefore = (parentNode: ITreeNode, node: ITreeNode, referenceNode: ITreeNode, cut: boolean = false): void => { + // TODO + } + + const removeNode = (node: ITreeNode): void => { + data.value = data.value.filter(item => { + return item.id !== node.id && !getChildren(node).map(nodeItem => nodeItem.id).includes(item.id); + }) + } + + const editNode = (node: ITreeNode, label: string): void => { + setNodeValue(node, 'label', label); + } + + return { + insertBefore, + removeNode, + editNode, + } +} \ No newline at end of file diff --git a/packages/devui-vue/devui/tree/src/core/use-select.ts b/packages/devui-vue/devui/tree/src/core/use-select.ts new file mode 100644 index 0000000000..de74b38ee9 --- /dev/null +++ b/packages/devui-vue/devui/tree/src/core/use-select.ts @@ -0,0 +1,16 @@ +import { Ref } from 'vue'; +import { IInnerTreeNode, IUseCore } from './tree-factory-types'; + +export default function useSelect(data: Ref, core: IUseCore) { + console.log('useSelect:', data, data.value); + + const { setNodeValue } = core; + + const selectNode = (node: IInnerTreeNode): void => { + setNodeValue(node, 'selected', true); + } + + return { + selectNode, + } +} \ No newline at end of file diff --git a/packages/devui-vue/devui/tree/src/core/use-toggle.ts b/packages/devui-vue/devui/tree/src/core/use-toggle.ts new file mode 100644 index 0000000000..42a78b8f26 --- /dev/null +++ b/packages/devui-vue/devui/tree/src/core/use-toggle.ts @@ -0,0 +1,32 @@ +import { Ref } from 'vue'; +import { IInnerTreeNode, ITreeNode, IUseCore } from './tree-factory-types'; + +export default function useToggle(data: Ref, core: IUseCore) { + console.log('useToggle:', data, data.value); + + const { getNode, setNodeValue } = core; + + const expandNode = (node: ITreeNode): void => { + setNodeValue(node, 'expanded', true); + } + + const collapseNode = (node: ITreeNode): void => { + setNodeValue(node, 'expanded', false); + } + + const toggleNode = (node: ITreeNode): void => { + console.log('toggleNode node:', node); + + if (getNode(node).expanded) { + setNodeValue(node, 'expanded', false); + } else { + setNodeValue(node, 'expanded', true); + } + } + + return { + expandNode, + collapseNode, + toggleNode, + } +} \ No newline at end of file diff --git a/packages/devui-vue/devui/tree/src/core/use-tree.ts b/packages/devui-vue/devui/tree/src/core/use-tree.ts new file mode 100644 index 0000000000..44f6d70960 --- /dev/null +++ b/packages/devui-vue/devui/tree/src/core/use-tree.ts @@ -0,0 +1,22 @@ +import { ref } from 'vue'; +import { IInnerTreeNode, ITreeNode, IUseCore, IUseTree } from './tree-factory-types'; +import useToggle from './use-toggle'; +import useCore from './use-core'; +import { generateInnerTree } from './utils'; + +export const DEFAULT_TREE_PLUGINS = [useCore, useToggle]; + +export default function useTree(tree: ITreeNode[], plugins = []): Partial { + const treeData = ref(generateInnerTree(tree)); + + const core: IUseCore = useCore(treeData); + + const pluginMethods = DEFAULT_TREE_PLUGINS.concat(plugins).reduce((acc, plugin) => { + return { ...acc, ...plugin(treeData, core) }; + }, {}); + + return { + treeData, + ...pluginMethods, + }; +} diff --git a/packages/devui-vue/devui/tree/src/core/utils.ts b/packages/devui-vue/devui/tree/src/core/utils.ts index 36149f9cc0..d14d57ba3c 100644 --- a/packages/devui-vue/devui/tree/src/core/utils.ts +++ b/packages/devui-vue/devui/tree/src/core/utils.ts @@ -54,7 +54,7 @@ export function generateInnerTree( } if (!newItem[key]) { - return acc.concat(newItem); + return acc.concat({ ...newItem, isLeaf: true }); } else { return acc.concat(omit(newItem, 'children'), generateInnerTree(newItem[key], key, level, path)); }