From 21f7d75d3b5c93e92936745f388619b6a18487c9 Mon Sep 17 00:00:00 2001 From: sufuwang Date: Thu, 7 Oct 2021 18:10:47 +0800 Subject: [PATCH] feat: lazy --- devui/tree/src/composables/use-lazy.ts | 64 +++++++++++++++++++++ devui/tree/src/tree-types.ts | 6 +- devui/tree/src/tree.scss | 8 +++ devui/tree/src/tree.tsx | 77 +++++++++++++++++++------- devui/tree/src/util.ts | 27 +++++++++ docs/components/tree/index.md | 21 ++++++- 6 files changed, 181 insertions(+), 22 deletions(-) create mode 100644 devui/tree/src/composables/use-lazy.ts diff --git a/devui/tree/src/composables/use-lazy.ts b/devui/tree/src/composables/use-lazy.ts new file mode 100644 index 00000000..7b7debd0 --- /dev/null +++ b/devui/tree/src/composables/use-lazy.ts @@ -0,0 +1,64 @@ +import { ref, Ref } from 'vue' +import { TreeData, TreeItem } from '../tree-types' + +interface TypeReflectValue { + // 外部传入 + id: keyof TypeReflect // 懒加载节点 id + onGetNodeData: () => Promise // 懒加载获取数据函数, 当前是节点 children + renderLoading?: (id: string) => any // loadingTemplate 挂载 + // useLazy 内部使用 + loadingTargetId?: string // loadingTemplate 挂载节点 id + dataSource?: TreeData // 懒加载数据 +} +interface TypeReflect { + [key: string]: TypeReflectValue +} +type TypeHandleInit = (item: TreeItem, value: TypeReflectValue) => void +type TypeGetLazyData = (key: keyof TypeReflect) => Promise | any +type TypeUseLazy = () => { + lazyNodesReflect: Ref + handleInitLazyNodeReflect: TypeHandleInit + getLazyData: TypeGetLazyData +} + +const useLazy: TypeUseLazy = () => { + const reflect = ref({}) + + const handleInit: TypeHandleInit = (item, value) => { + if (!item.isParent) { + return + } + const key = reflect.value[value.id]?.id.toString() + if (!key) { + reflect.value[value.id] = { + ...value, + loadingTargetId: `devui-tree_loadingTemplate-${value.id}`, + dataSource: null + } + } + } + + const getLazyData: TypeGetLazyData = async (key) => { + const ds = reflect.value[key] + if (ds.dataSource) { + return ds.dataSource + } + const handleLoading = reflect.value[key].renderLoading(reflect.value[key].loadingTargetId) + try { + reflect.value[key].dataSource = await ds.onGetNodeData() + } catch(e) { + console.error(e) + } finally { + handleLoading.loadingInstance.close() + } + return reflect.value[key].dataSource + } + + return { + lazyNodesReflect: reflect, + handleInitLazyNodeReflect: handleInit, + getLazyData, + } +} + +export default useLazy diff --git a/devui/tree/src/tree-types.ts b/devui/tree/src/tree-types.ts index 1af7c611..8284c8e8 100644 --- a/devui/tree/src/tree-types.ts +++ b/devui/tree/src/tree-types.ts @@ -1,8 +1,12 @@ import type { PropType, ExtractPropTypes } from 'vue' export interface TreeItem { + id: string label: string - children: TreeData + isParent?: boolean + level: number + open?: boolean + children?: TreeData [key: string]: any } diff --git a/devui/tree/src/tree.scss b/devui/tree/src/tree.scss index 113b3e35..2ffc1624 100644 --- a/devui/tree/src/tree.scss +++ b/devui/tree/src/tree.scss @@ -44,6 +44,14 @@ $keyframe-blue: #5e7ce0; align-items: center; height: 30px; width: 100%; + + .devui-tree-node_loading { + margin-left: 50px; + + .devui-loading-area { + background: none; + } + } } .devui-tree-node__children { diff --git a/devui/tree/src/tree.tsx b/devui/tree/src/tree.tsx index 0d7fae87..3aa3d681 100644 --- a/devui/tree/src/tree.tsx +++ b/devui/tree/src/tree.tsx @@ -1,9 +1,11 @@ import { defineComponent, toRefs } from 'vue' -import { treeProps, TreeProps } from './tree-types' -import { flatten } from './util' +import { treeProps, TreeProps, TreeItem } from './tree-types' +import { flatten, precheckTree } from './util' +import Loading from '../../loading/src/service' import useToggle from './composables/use-toggle' import useMergeNode from './composables/use-merge-node' import useHighlightNode from './composables/use-highlight' +import useLazy from './composables/use-lazy' import IconOpen from './assets/open.svg' import IconClose from './assets/close.svg' import './tree.scss' @@ -13,42 +15,78 @@ export default defineComponent({ props: treeProps, emits: [], setup(props: TreeProps) { - const { data } = toRefs(props) + const { data } = toRefs({ ...props, data: precheckTree(props.data) }) const flatData = flatten(data.value) const { mergeData } = useMergeNode(data.value) - const { openedData, toggle } = useToggle(mergeData.value) const { nodeClassNameReflect, handleInitNodeClassNameReflect, handleClickOnNode } = useHighlightNode() + const { lazyNodesReflect, handleInitLazyNodeReflect, getLazyData } = useLazy() const Indent = () => { return } - - const renderNode = (item) => { - // 现在数据里面没有 key , 未来做优化需要 key 值嘛? - const { key = '', label, disabled, open, level, children } = item - const nodeId = handleInitNodeClassNameReflect(disabled, key, label) + const renderNode = (item: TreeItem) => { + const { id = '', label, disabled, open, isParent, level, children } = item + handleInitNodeClassNameReflect(disabled, id) + handleInitLazyNodeReflect(item, { + id, + onGetNodeData: async () => { + return new Promise((resolve) => { + setTimeout(() => { + resolve([ + { + id: `It is a test Node-1 ID = ${id}`, + label: `It is a test Node-1 ID = ${id}`, + level: item.level + 1 + }, { + id: `It is a test Node-2 ID = ${id}`, + label: `It is a test Node-2 ID = ${id}`, + level: item.level + 1 + } + ]) + }, 4000) + }) + }, + renderLoading: (id) => { + return Loading.open({ + target: document.getElementById(id), + message: '加载中...', + positionType: 'relative', + zIndex: 1, + }) + } + }) + const renderNodeWithIcon = (item: TreeItem) => { + const handleClick = async (target: MouseEvent) => { + if (item.isParent) { + item.children = await getLazyData(id) // item 按引用传递 + } + return toggle(target, item) + } + return ( + isParent || children + ? open + ? + : + : + ) + } return (
handleClickOnNode(nodeId)} + class={`devui-tree-node__content ${nodeClassNameReflect.value[id]}`} + onClick={() => handleClickOnNode(id)} >
- { - children - ? open - ? toggle(target, item)} /> - : toggle(target, item)} /> - : - } + {renderNodeWithIcon(item)} - { label } + {label} + {item.isParent &&
}
@@ -69,7 +107,6 @@ export default defineComponent({ } }) } - return () => { return (
diff --git a/devui/tree/src/util.ts b/devui/tree/src/util.ts index 1517f1c3..65c68355 100644 --- a/devui/tree/src/util.ts +++ b/devui/tree/src/util.ts @@ -1,3 +1,5 @@ +import { TreeData, TreeItem } from './tree-types' + export const omit = (obj: unknown, key: string): unknown => { return Object.entries(obj) .filter(item => item[0] !== key) @@ -9,3 +11,28 @@ export const flatten = (tree: Array, key = 'children'): Array => { !item[key] ? acc.concat(item) : acc.concat(item, flatten(item[key], key)) ), []) } + +/** + * 用于设置 Tree Node 的 ID 属性 + * 应用场景: 懒加载 loading 后元素定位 + */ +const precheckNodeId = (d: TreeItem): TreeItem => { + const random = parseInt((Math.random() * (10 ** 8)).toString().padEnd(8, '0')) + return { ...d, id: d.id ? `${d.id}_${random}` : `${d.label.replaceAll(' ', '-')}_${random}` } +} + +/** + * 用于 Tree Node 的数据格式检查 + */ +export const precheckTree = (ds: TreeData): TreeData => { + return ds.map(d => { + const dd = precheckNodeId(d) + if (d.children) { + return { + ...dd, + children: precheckTree(d.children) + } + } + return dd + }) +} diff --git a/docs/components/tree/index.md b/docs/components/tree/index.md index f2c350b6..dc2dc70a 100644 --- a/docs/components/tree/index.md +++ b/docs/components/tree/index.md @@ -70,6 +70,7 @@ export default defineComponent({ ] }, { + id: 'dynamic 12', label: 'parent node 13 - without children - dynamic loading', isParent: true, level: 2, @@ -189,19 +190,24 @@ export default defineComponent({ const data = ref([ { label: 'parent node 1', + level: 1, children: [ { label: 'parent node 11', open: true, + level: 2, children: [ { label: 'parent node 111', + level: 3, children: [ { label: 'parent node 1111', + level: 4, children: [ { label: 'leaf node 11111', + level: 5, } ] } @@ -213,25 +219,32 @@ export default defineComponent({ }, { label: 'parent node 2', + level: 1, children: [ { label: 'parent node 21', + level: 2, open: true, children: [ { label: 'leaf node 211', + level: 3, }, { label: 'leaf node 212', + level: 3, }, { label: 'leaf node 213', + level: 3, }, { label: 'leaf node 214', + level: 3, }, { label: 'leaf node 215', + level: 3, }, ] }, @@ -239,15 +252,19 @@ export default defineComponent({ }, { label: 'parent node 3', + level: 1, children: [ { label: 'leaf node 31', + level: 2, children: [ { label: 'leaf node 311', + level: 3, children: [ { label: 'leaf node 3111', + level: 4, } ] } @@ -255,9 +272,11 @@ export default defineComponent({ }, { label: 'leaf node 32', + level: 2, }, { label: 'leaf node 33', + level: 2, } ] } @@ -270,4 +289,4 @@ export default defineComponent({ }) ``` -::: \ No newline at end of file +::: -- Gitee