Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions examples/sites/demos/pc/app/tree/virtual-basic.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<template>
<div>
<tiny-tree
:data="data"
:nodeHeight="50"
:treeHeight="400"
:show-line="showLine === 'show'"
default-expand-all
class="tiny-tree"
@node-click="nodeClick"
></tiny-tree>
</div>
</template>

<script lang="jsx">
import { Tree } from '@opentiny/vue'

export default {
components: {
TinyTree: Tree
},
data() {
return {
showLine: 'hide',
data: this.generateTreeData(100)
}
},
methods: {
generateTreeData(num) {
const data = []
for (let i = 0; i < num; i++) {
const children = []
for (let j = 0; j < Math.floor(Math.random() * 3) + 1; j++) {
// 每个节点随机生成1-3个子节点
children.push({
label: `数据 ${i}-${j}`,
children: [], // 子节点可以继续递归生成,
id: i.toString()
})
}
data.push({
label: `数据 ${i}`,
children,
id: i.toString()
})
}
return data
},
nodeClick(data, node, vm) {
console.log('点击节点事件:', { data, node, vm })
}
}
}
</script>

<style scoped></style>
9 changes: 9 additions & 0 deletions examples/sites/demos/pc/app/tree/webdoc/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,15 @@ export default {
`
},
codeFiles: ['other.vue']
},
{
demoId: 'virtual-tree',
name: {
'zh-CN': '虚拟树',
'en-US': 'virtual tree'
},
desc: {},
codeFiles: ['virtual-basic.vue']
}
]
}
9 changes: 3 additions & 6 deletions packages/renderless/src/tree-node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,6 @@ export const handleContextMenu =
}
})
}

export const handleExpandIconClick =
({ api, state }) =>
(event, node) => {
Expand All @@ -179,28 +178,26 @@ export const handleExpandIconClick =
event.stopPropagation()
}
}

export const handleExpandClick =
({ emit, vm, props, state }) =>
(nodeClickFlag) => {
if (nodeClickFlag) {
state.tree.$parent.$emit('node-click', props.node.data, props.node, vm)
}

if (props.node.isLeaf) {
return
}

if (state.expanded) {
state.tree.$parent.$emit('node-collapse', props.node.data, props.node, vm)
props.node.collapse()

emit('node-collapse', props.node.data, props.node, vm)
} else {
props.node.expand()
emit('node-expand', props.node.data, props.node, vm)
}
}

// 点击节点 或 点击checkbox的,都会进入该函数
export const handleCheckChange =
({ nextTick, props, state }) =>
(value, event = {}) => {
Expand Down Expand Up @@ -367,7 +364,7 @@ export const computedExpandIcon =

return 'icon-chevron-right'
}
// tiny 新增
// tiny 新增(需要)
export const computedIndent =
() =>
({ node, showLine }, { tree }) => {
Expand Down
22 changes: 20 additions & 2 deletions packages/renderless/src/tree/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import { getNodeKey as innerGetNodekey } from '../common/deps/tree-model/util'
import { KEY_CODE } from '../common'
import TreeStore from '../common/deps/tree-model/tree-store'
import type TreeNode from '../common/deps/tree-model/node'
import { addClass, removeClass } from '../common/deps/dom'
import { on, off } from '../common/deps/dom'
import { getDataset } from '../common/dataset'
Expand Down Expand Up @@ -100,7 +101,7 @@ export const dragStart =
emit('node-drag-start', treeNode.node, event)
}

// 移动时,判断释放在目标元素的前,后,或inner。 刚移动未离开拖动元素时,为none。

const getDropType = (dropPrev, dropInner, dropNext, dropNode) => {
let dropType
const targetPosition = dropNode.$el.getBoundingClientRect()
Expand Down Expand Up @@ -448,7 +449,10 @@ export const filter =
}
}

export const getNodeKey = (props) => (node) => innerGetNodekey(props.nodekey, node.data)
export const getNodeKey = (props) => (node) => {
return innerGetNodekey(props.nodekey, node.data)
}


export const getNodePath =
({ props, state }) =>
Expand Down Expand Up @@ -1044,3 +1048,17 @@ export const setCheckedByNodeKey =
plainNode.node.setChecked(checked, !checkStrictly)
}
}

// 扁平化数据(虚拟滚动)
export const computedFlattenedTreeData = () => (props, state) => {
const data = state.root.childNodes
const stack: TreeNode[] = data.slice().reverse()
const newData: TreeNode[] = []
while (stack.length) {
const node = stack.pop()!
newData.push(node)
if (!node.expanded) continue
stack.push(...(node.childNodes || []).reverse().filter((v) => v.expanded))
}
return newData
}
36 changes: 27 additions & 9 deletions packages/renderless/src/tree/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ import {
initPlainNodeStore,
handleCheckPlainNode,
handleClickPlainNode,
setCheckedByNodeKey
setCheckedByNodeKey,
computedFlattenedTreeData
} from './index'
import { random } from '../common/string'

Expand Down Expand Up @@ -128,11 +129,14 @@ export const api = [
'setDeleteDisabledKeys',
'handleCheckPlainNode',
'handleClickPlainNode',
'setCheckedByNodeKey'
'setCheckedByNodeKey',
'computedFlattenedTreeData',
'updateFlattenedTreeData'
]

const initState = ({ reactive, emitter, props, computed, api }) => {
const state = reactive({
flattenedTreeData: [],
loaded: !props.lazy,
checkEasily: false,
root: null,
Expand Down Expand Up @@ -224,7 +228,8 @@ const initApi = ({ state, dispatch, broadcast, props, vm, constants, t, emit, ap
dragEnd: dragEnd({ state, emit }),
clearCurrentStore: clearCurrentStore(state),
initIsCurrent: debounce(20, initIsCurrent({ props, state })),
setCheckedByNodeKey: setCheckedByNodeKey({ props, state })
setCheckedByNodeKey: setCheckedByNodeKey({ props, state }),
computedFlattenedTreeData: computedFlattenedTreeData(props)
})

const initWatcher = ({ watch, props, api, state, isVue2 }) => {
Expand Down Expand Up @@ -257,17 +262,25 @@ const initWatcher = ({ watch, props, api, state, isVue2 }) => {
(value) => (state.action.addDisabled = value || []),
{ immediate: true }
)

watch(
() => state.flattenedTreeData.filter((n) => n.expanded).length,
(v, oldV) => {
if (oldV?.length && v?.filter((n) => n.expanded) === oldV?.filter((n) => n.expanded)) return
},
{ deep: true }
)
}

export const renderless = (
props,
{ computed, onMounted, onUpdated, reactive, watch, provide, onBeforeUnmount },
{ computed, onMounted, onUpdated, reactive, watch, provide, onBeforeUnmount, ref },
{ vm, t, emit, constants, broadcast, dispatch, service, emitter, nextTick },
{ isVue2 }
) => {
const api = {}
const state = initState({ reactive, emitter, props, computed, api })

const scrollRef = ref(null)
provide('parentEmitter', state.emitter)

Object.assign(api, initApi({ state, dispatch, broadcast, props, vm, constants, t, emit, api }), {
Expand All @@ -283,6 +296,7 @@ export const renderless = (
deleteConfirm: deleteConfirm({ state, props, api }),
getSameDataIndex,
loopGetTreeData,
scrollRef,
cancelDelete: cancelDelete({ state }),
openEdit: openEdit({ props, state, api, emit }),
saveNode: saveNode({ state, emit, api }),
Expand All @@ -297,14 +311,18 @@ export const renderless = (
switchToggle: switchToggle({ state }),
initPlainNodeStore: initPlainNodeStore({ props, state }),
handleCheckPlainNode: handleCheckPlainNode({ props, emit }),
handleClickPlainNode: handleClickPlainNode(emit)
handleClickPlainNode: handleClickPlainNode(emit),
updateFlattenedTreeData: (data, node, vm) => {
state.flattenedTreeData = api.computedFlattenedTreeData(props, state)
}
})

api.created()

state.flattenedTreeData = api.computedFlattenedTreeData(props, state)
initWatcher({ watch, props, api, state, isVue2 })

onMounted(api.wrapMounted)
onMounted(() => {
api.wrapMounted()
})

onUpdated(api.updated)

Expand Down
1 change: 0 additions & 1 deletion packages/renderless/src/virtual-scroll/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ export const handleScroll = ({ props, state, virtualScroll, nextTick, items, ...
viewStart + viewNum + bufferItems < state.data.length ? viewStart + viewNum + bufferItems : state.data.length

state.visibleData = state.data.slice(start, end)

state.translate = start * props.itemSize

return
Expand Down
19 changes: 14 additions & 5 deletions packages/renderless/src/virtual-scroll/vue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const api = [
'calculateTotalSize',
'scrollToItem'
]
export const renderless = (props, { reactive, nextTick, watch, onMounted, ref }, { service }) => {
export const renderless = (props, { reactive, nextTick, watch, onMounted, ref, computed }, { service }) => {
service = initService({ props, service })

const api = {}
Expand Down Expand Up @@ -61,24 +61,33 @@ export const renderless = (props, { reactive, nextTick, watch, onMounted, ref },
state.temporary.prerender = true
api.handleScroll()
}

// 组件挂载后加载滚动事件
onMounted(() => {
const handle = (isFirst) => {
if (!virtualScroll.value) return
nextTick(() => {
api.initPositions()
state.temporary.prerender = false
api.handleScroll()
state.totalSize = api.calculateTotalSize
})
})
}
// 组件挂载后加载滚动事件
onMounted(handle)
watch(
() => state.visibleData,
(newVisibleData) => {
state.totalSize = api.calculateTotalSize
},
{ immediate: true }
)
watch(
() => props.data,
(newData) => {
state.data = newData
console.log('newData', newData.length)
nextTick(api.handleScroll)
},
{ deep: true }
)

return api
}
1 change: 1 addition & 0 deletions packages/vue/src/tree/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ export default defineComponent({
default: false
},
nodeHeight: Number,
treeHeight: Number,
shrinkIcon: Object,
expandIconColor: String,
shrinkIconColor: String,
Expand Down
Loading