Skip to content

Commit 7a74e45

Browse files
committed
feat: support showLine #89
1 parent af332ca commit 7a74e45

File tree

10 files changed

+322
-46
lines changed

10 files changed

+322
-46
lines changed

examples/Feature.vue

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,53 @@
138138
</div>
139139
</div>
140140

141+
<!-- 连接线 -->
142+
<div class="panel">
143+
<div class="header">连接线</div>
144+
<div class="body">
145+
<div class="interface">
146+
<div style="height: 300px">
147+
<VTree
148+
ref="basicTreeRef"
149+
:data="showLineTreeData"
150+
:showLine="{ type: showLineType, polyline: showLinePolyline }"
151+
defaultExpandAll
152+
/>
153+
</div>
154+
</div>
155+
<div class="desc">
156+
<div class="desc-block">
157+
传入 showLine 可展示连接线,type 可指定连接线类型
158+
</div>
159+
<div class="desc-block">
160+
showLine 传入对象可设置连接线的宽度,连线类型与颜色等<br/>
161+
showLine.type:
162+
163+
<button
164+
v-for="showLineType in (['solid', 'dashed'] as showLineType[])"
165+
:key="showLineType"
166+
@click="onShowLineTypeBtnClick(showLineType)"
167+
>
168+
{{ showLineType }}
169+
</button>
170+
<p>当前连接线类型:{{showLineType}}</p>
171+
</div>
172+
<div class="desc-block">
173+
showLine.polyline:
174+
175+
<button
176+
v-for="polyline in [true, false]"
177+
:key="polyline"
178+
@click="onShowLinePolylineBtnClick(polyline)"
179+
>
180+
{{ polyline }}
181+
</button>
182+
<p>是否启用折线:{{showLinePolyline}}</p>
183+
</div>
184+
</div>
185+
</div>
186+
</div>
187+
141188
<!-- 远程 -->
142189
<div class="panel">
143190
<div class="header">远程</div>
@@ -165,6 +212,7 @@
165212

166213
<script lang="ts">
167214
import VTree, { TreeNode } from '../src'
215+
import { showLineType } from '../src/constants'
168216
import { IgnoreType } from '../src/types'
169217
import treeDataGenerator from '../tests/tree-data-generator'
170218
import { defineComponent, ref, nextTick } from 'vue'
@@ -214,6 +262,80 @@ export default defineComponent({
214262
const checkableCascade = ref(true)
215263
const both = ref(genData().data)
216264
const bothValue = ref([])
265+
const showLineTreeData = ref([
266+
{
267+
title: 'node-1',
268+
id: 'node-1',
269+
children: [
270+
{
271+
title: 'node-1-1',
272+
id: 'node-1-1',
273+
children: [
274+
{
275+
title: 'node-1-1-1',
276+
id: 'node-1-1-1',
277+
},
278+
{
279+
title: 'node-1-1-2',
280+
id: 'node-1-1-2',
281+
},
282+
{
283+
title: 'node-1-1-3',
284+
id: 'node-1-1-3',
285+
},
286+
],
287+
},
288+
{
289+
title: 'node-1-2',
290+
id: 'node-1-2',
291+
children: [
292+
{
293+
title: 'node-1-2-1',
294+
id: 'node-1-2-1',
295+
},
296+
{
297+
title: 'node-1-2-2',
298+
id: 'node-1-2-2',
299+
},
300+
],
301+
},
302+
{
303+
title: 'node-1-3',
304+
id: 'node-1-3',
305+
children: [
306+
{
307+
title: 'node-1-3-1',
308+
id: 'node-1-3-1',
309+
},
310+
{
311+
title: 'node-1-3-2',
312+
id: 'node-1-3-2',
313+
},
314+
],
315+
},
316+
],
317+
},
318+
{
319+
title: 'node-2',
320+
id: 'node-2',
321+
children: [
322+
{
323+
title: 'node-2-1',
324+
id: 'node-2-1',
325+
children: [
326+
{
327+
title: 'node-2-1-1',
328+
id: 'node-2-1-1',
329+
},
330+
{
331+
title: 'node-2-1-2',
332+
id: 'node-2-1-2',
333+
},
334+
],
335+
},
336+
],
337+
},
338+
])
217339
const remoteShow = ref(false)
218340
const remoteLoad = (
219341
node: TreeNode | null,
@@ -285,6 +407,16 @@ export default defineComponent({
285407
console.log('node-right-click mouse position', e.x, e.y)
286408
}
287409
410+
const showLineType = ref()
411+
const onShowLineTypeBtnClick = (type: showLineType) => {
412+
showLineType.value = type
413+
}
414+
415+
const showLinePolyline = ref(false)
416+
const onShowLinePolylineBtnClick = (polyline: boolean) => {
417+
showLinePolyline.value = polyline
418+
}
419+
288420
return {
289421
// 基本用法
290422
basicUsage,
@@ -314,6 +446,13 @@ export default defineComponent({
314446
both,
315447
bothValue,
316448
449+
// 连接线
450+
showLineTreeData,
451+
showLineType,
452+
onShowLineTypeBtnClick,
453+
showLinePolyline,
454+
onShowLinePolylineBtnClick,
455+
317456
// 远程
318457
remoteShow,
319458
remoteLoad,

examples/Performance.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<div class="container">
33
<div class="tree">
4-
<VTree ref="tree" :data="treeData" checkable selectable></VTree>
4+
<VTree ref="tree" :data="treeData" checkable selectable :showLine="{ polyline: true }"></VTree>
55
</div>
66
<div class="control">
77
<div class="desc-block">

src/components/Tree.vue

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
:key="node[keyField]"
1616
:data="node"
1717
:getNode="getNode"
18+
:noSiblingNodeMap="noSiblingNodeMap"
1819
v-on="treeNodeListeners"
1920
:class="
2021
typeof nodeClassName === 'function'
@@ -23,7 +24,6 @@
2324
"
2425
:style="{
2526
minHeight: `${nodeMinHeight}px`,
26-
paddingLeft: `${node._level * nodeIndent}px`
2727
}"
2828
@check="handleNodeCheck"
2929
@select="handleNodeSelect"
@@ -150,6 +150,8 @@ export interface TreeProps {
150150
/** 节点根元素的 class ,传入函数以对每个节点定制 class */
151151
nodeClassName?: string | object | Array<string | object> | ((node: TreeNode) => string | object | Array<string | object>),
152152
153+
/** 连接线 */
154+
showLine?: boolean | ShowLine
153155
154156
/** 子节点缩进 */
155157
nodeIndent?: number,
@@ -204,6 +206,9 @@ import {
204206
watch,
205207
onMounted,
206208
onBeforeUnmount,
209+
computed,
210+
reactive,
211+
toRefs,
207212
} from 'vue'
208213
import TreeStore, { TreeNode } from '../store'
209214
import CTreeNode from './TreeNode.vue'
@@ -213,7 +218,7 @@ import {
213218
dragHoverPartEnum,
214219
} from '../constants'
215220
import { STORE_EVENTS, TREE_EVENTS, TREE_NODE_EVENTS } from '../constants/events'
216-
import { TreeNodeKeyType, INonReactiveData, IgnoreType, AnyPropsArrayType, LoadFn } from '../types'
221+
import { TreeNodeKeyType, INonReactiveData, IgnoreType, AnyPropsArrayType, LoadFn, ShowLine } from '../types'
217222
import { useTreeCls } from '../hooks/useTreeCls'
218223
import { useVirtualList } from '../hooks/useVirtualList'
219224
import { useIframeResize } from '../hooks/useIframeResize'
@@ -342,6 +347,43 @@ const {
342347
updateRender,
343348
})
344349
350+
const noSiblingNodeMap = computed(() => {
351+
const parentsOfFirstNode: TreeNode[] = []
352+
let nodeParent = renderNodes.value[0]?._parent
353+
354+
while (nodeParent) {
355+
parentsOfFirstNode.push(nodeParent)
356+
nodeParent = nodeParent._parent
357+
}
358+
359+
const nodesToIterate = parentsOfFirstNode.concat(renderNodes.value)
360+
361+
const map: Record<string, true> = {}
362+
const stack: TreeNode[] = []
363+
nodesToIterate.forEach((renderNode) => {
364+
const currentNodeLevel = renderNode._level
365+
let length = stack.length
366+
while (length) {
367+
const stackNode = stack[length - 1]
368+
const stackNodeLevel = stackNode._level
369+
if (stackNodeLevel > currentNodeLevel) {
370+
map[stackNode[props.keyField]] = true
371+
stack.pop()
372+
} else if (stackNodeLevel === currentNodeLevel) {
373+
stack.pop()
374+
break
375+
} else break
376+
length--
377+
}
378+
stack.push(renderNode)
379+
})
380+
stack.forEach((node) => {
381+
map[node[props.keyField]] = true
382+
})
383+
384+
return map
385+
})
386+
345387
type VModelType = TreeNodeKeyType | TreeNodeKeyType[]
346388
347389
const sameValue = (newVal: VModelType, valueCache: VModelType | undefined): boolean => {
@@ -555,9 +597,11 @@ const treeNodePropKeys = [
555597
'draggable',
556598
'droppable',
557599
'render',
600+
'nodeIndent',
601+
'showLine',
558602
] as const
559603
560-
const treeNodeProps = pickReadonly(props, ...treeNodePropKeys)
604+
const treeNodeProps = reactive(pickReadonly(toRefs(props), ...treeNodePropKeys))
561605
562606
defineExpose({
563607
setData,

0 commit comments

Comments
 (0)