oxy-uni-ui 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,31 @@
1
1
  @import '../common/abstracts/variable';
2
2
  @import '../common/abstracts/mixin';
3
+ @import '../oxy-virtual-scroll/index.scss';
4
+
5
+ .oxy-tree {
6
+ position: relative;
7
+ &__virtual-scroll {
8
+ position: relative;
9
+ overflow: hidden;
10
+ }
11
+
12
+ &__view {
13
+ height: 100%;
14
+ }
15
+
16
+ &__container {
17
+ position: relative;
18
+ }
19
+
20
+ &__items {
21
+ position: absolute;
22
+ top: 0;
23
+ left: 0;
24
+ right: 0;
25
+ }
26
+ }
3
27
 
4
28
  .oxy-tree-node-content {
5
- line-height: 44px;
6
- height: 44px;
7
29
  display: flex;
8
30
  width: fit-content;
9
31
  width: 100%;
@@ -11,13 +33,13 @@
11
33
  align-items: center;
12
34
 
13
35
  &.expanded {
14
- .oxy-tree-node-icon {
36
+ :deep(.oxy-tree-node-icon) {
15
37
  transform: rotate(0deg);
16
38
  }
17
39
  }
18
40
 
19
41
  &.is-leaf {
20
- .oxy-tree-node-icon {
42
+ :deep(.oxy-tree-node-icon) {
21
43
  opacity: 0;
22
44
  }
23
45
  }
@@ -51,11 +73,11 @@
51
73
  }
52
74
  }
53
75
 
54
- .oxy-tree-node-icon {
76
+ :deep(.oxy-tree-node-icon) {
55
77
  transform: rotate(-90deg);
56
78
  }
57
79
 
58
80
  .oxy-tree-node {
59
81
  flex: 1;
60
82
  }
61
- }
83
+ }
@@ -1,17 +1,41 @@
1
1
  <template>
2
2
  <view class="oxy-tree" :class="customClass" :style="customStyle">
3
- <oxy-virtual-scroll v-if="data.length" :data="flattenTree" :height="maxHeight" item-height="44px" :scrollX="true">
4
- <template #item="{ item }">
5
- <treeNodeContent :node="item" @toggle-expand="toggleExpand" @toggle-checked="toggleChecked" @click="handleClick">
6
- <template #default="{ node, data }">
7
- <slot :node="node" :data="data"></slot>
8
- </template>
9
- </treeNodeContent>
10
- </template>
11
- </oxy-virtual-scroll>
3
+ <view v-if="data.length" class="oxy-tree__virtual-scroll" :style="{ height: height }">
4
+ <scroll-view class="oxy-tree__view" scroll-y :scroll-x="true" :scroll-top="scrollTop" @scroll="handleScroll">
5
+ <view class="oxy-tree__container" :style="{ height: totalHeight + 'px' }">
6
+ <view class="oxy-tree__items" :style="{ transform: `translateY(${virtualOffsetY}px)` }">
7
+ <view v-for="(item, index) in virtualData" :key="index">
8
+ <view class="oxy-tree-node-content" :style="getNodeStyle(item)" :class="getNodeClass(item)" @click="handleNodeClick(item)">
9
+ <!-- 兼容支付宝、微信小程序 -->
10
+ <view @tap.stop="handleClickExpand(item)">
11
+ <oxy-icon name="fill-arrow-down" custom-class="oxy-tree-node-icon" size="22px"></oxy-icon>
12
+ </view>
13
+ <oxy-checkbox
14
+ v-if="showCheckbox"
15
+ :modelValue="item.checked"
16
+ :disabled="item.disabled"
17
+ :indeterminate="item.immediate"
18
+ shape="square"
19
+ ></oxy-checkbox>
20
+ <view class="oxy-tree-node">
21
+ <slot name="node" :node="item" :data="item.data">
22
+ {{ item.label }}
23
+ </slot>
24
+ </view>
25
+ </view>
26
+ </view>
27
+ </view>
28
+ </view>
29
+ </scroll-view>
30
+ </view>
12
31
  <view v-else>
13
32
  <oxy-status-tip image="content" :tip="emptyText" />
14
33
  </view>
34
+
35
+ <!-- 回到顶部按钮 -->
36
+ <view v-if="showBackToTop && showBackTopBtn" class="oxy-virtual-scroll__back-top" @click="scrollToTop">
37
+ <oxy-icon name="backtop" color="#fff" size="20px"></oxy-icon>
38
+ </view>
15
39
  </view>
16
40
  </template>
17
41
 
@@ -27,23 +51,27 @@ export default {
27
51
  </script>
28
52
 
29
53
  <script lang="ts" setup>
30
- import { computed, nextTick, provide, ref, toRefs, watch } from 'vue'
31
- import type { RawTreeNode, TreeNode } from './types'
32
- import { textProps } from './types'
33
- import treeNodeContent from './components/tree-node-content.vue'
34
- import { isSetsEqual, useTreeMethods } from '.'
35
-
36
- // 树结构类型定义
37
- type Tree = {
38
- treeNodeMap: Map<string, TreeNode>
39
- levelTreeNodeMap: Map<number, TreeNode[]>
40
- maxLevel: number
41
- treeNodes: TreeNode[]
42
- }
54
+ import { computed, nextTick, provide, type Ref, ref, toRefs, watch } from 'vue'
55
+ import type { RawTreeNode, Tree, TreeInstance, TreeNode } from './types'
56
+ import { treeProps, type TreeExpose } from './types'
57
+ import { isSetsEqual, useTreeMethods } from './utils'
58
+ import { useVirtualScroll } from '../composables/useVirtualScroll'
43
59
 
44
60
  // 获取组件的 props 和 emit 函数
45
- const props = defineProps(textProps)
46
- const { modelValue, data, defaultExpandedKeys, expandAll, showCheckbox, checkStrictly, selectionLeafOnly } = toRefs(props)
61
+ const props = defineProps(treeProps)
62
+ const {
63
+ modelValue,
64
+ data,
65
+ defaultExpandedKeys,
66
+ expandAll,
67
+ showCheckbox,
68
+ checkStrictly,
69
+ selectionLeafOnly,
70
+ height,
71
+ itemHeight,
72
+ nodeKey,
73
+ backToTopThreshold
74
+ } = toRefs(props)
47
75
  const emit = defineEmits<{
48
76
  (e: 'node-click', node: TreeNode): void
49
77
  (e: 'update:modelValue', value: string[] | string | undefined): void
@@ -58,11 +86,8 @@ const hiddenNodeKeySet = ref<Set<string>>(new Set())
58
86
  const checkedKeys = ref<Set<string>>(new Set())
59
87
  const immediateKeySet = ref<Set<string>>(new Set())
60
88
  const currentNode = ref<TreeNode>()
61
- provide('treeProps', props)
62
- provide('currentNode', currentNode)
63
-
64
- const tree = ref<Tree>()
65
89
 
90
+ const tree = ref<Tree>() as Ref<Tree>
66
91
  const flattenTree = computed<TreeNode[]>(() => {
67
92
  const expandedKeys = expandedKeySet.value
68
93
  const hiddenKeys = hiddenNodeKeySet.value
@@ -93,6 +118,32 @@ const flattenTree = computed<TreeNode[]>(() => {
93
118
  traverse()
94
119
  return flattenNodes
95
120
  })
121
+
122
+ // 虚拟滚动逻辑
123
+ const {
124
+ scrollTop,
125
+ showBackTopBtn,
126
+ virtualData,
127
+ startIndex,
128
+ virtualOffsetY,
129
+ totalHeight,
130
+ displayData,
131
+ updateVisibleData,
132
+ scrollToTop: virtualScrollToTop,
133
+ scrollToBottom: virtualScrollToBottom,
134
+ scrollToPosition: virtualScrollToPosition,
135
+ scrollToElement: virtualScrollToElement,
136
+ scrollToElementById: virtualScrollToElementById,
137
+ onScroll: handleVirtualScroll
138
+ } = useVirtualScroll({
139
+ data: flattenTree,
140
+ virtual: ref(true),
141
+ height: height,
142
+ itemHeight: itemHeight,
143
+ idKey: ref('key'),
144
+ backToTopThreshold: backToTopThreshold
145
+ })
146
+
96
147
  const createTree = (data: RawTreeNode[]) => {
97
148
  const treeNodeMap = new Map<string, TreeNode>()
98
149
  const levelTreeNodeMap = new Map<number, TreeNode[]>()
@@ -197,6 +248,11 @@ const handleClick = (node: TreeNode) => {
197
248
  updateValue()
198
249
  }
199
250
  }
251
+
252
+ // 滚动事件处理
253
+ const handleScroll = (event: any) => {
254
+ handleVirtualScroll(event.detail.scrollTop)
255
+ }
200
256
  const updateParentNode = (node: TreeNode) => {
201
257
  if (!node.parent) return
202
258
  updateNode(node.parent)
@@ -257,6 +313,30 @@ const updateValue = () => {
257
313
  emit('update:modelValue', currentNode.value?.key)
258
314
  }
259
315
  }
316
+ const getNodeStyle = (item: TreeNode) => {
317
+ return {
318
+ height: itemHeight.value,
319
+ paddingLeft: `${(item.level - 1) * (props.indent || 16)}px`
320
+ }
321
+ }
322
+ const getNodeClass = (item: TreeNode) => {
323
+ const currentValue = currentNode?.value
324
+ return {
325
+ expanded: item.expanded,
326
+ checked: item.checked,
327
+ 'is-leaf': item.isLeaf,
328
+ immediate: item.immediate,
329
+ 'is-current': currentValue === item,
330
+ 'is-disabled': item.disabled
331
+ }
332
+ }
333
+ const handleNodeClick = (item: TreeNode) => {
334
+ toggleChecked(item, !item.checked, true)
335
+ handleClick(item)
336
+ }
337
+ const handleClickExpand = (item: TreeNode) => {
338
+ toggleExpand(item, !item.expanded)
339
+ }
260
340
  watch(
261
341
  () => data.value,
262
342
  () => {
@@ -277,10 +357,47 @@ watch(
277
357
  initValue()
278
358
  }
279
359
  )
280
- defineExpose({
360
+ const getNodeById = (id: string | number) => {
361
+ return tree.value?.treeNodeMap.get(id as string)
362
+ }
363
+ const scrollToTop = () => {
364
+ virtualScrollToTop()
365
+ }
366
+ const scrollToBottom = () => {
367
+ const visibleCount = flattenTree.value.length
368
+ const containerHeight = parseFloat(height.value || '0')
369
+ const targetScrollTop = Math.max(visibleCount * parseFloat(itemHeight.value) - containerHeight, 0)
370
+ virtualScrollToPosition(targetScrollTop)
371
+ }
372
+ const scrollToPosition = (position: number | string) => {
373
+ virtualScrollToPosition(position)
374
+ }
375
+ const scrollToElement = (node: any) => {
376
+ expandNode(node)
377
+ nextTick(() => {
378
+ virtualScrollToElement(node)
379
+ })
380
+ }
381
+ const scrollToElementById = (id: string | number) => {
382
+ const targetNode = tree.value?.treeNodeMap.get(id as string)
383
+ if (targetNode) {
384
+ expandNode(targetNode)
385
+ nextTick(() => {
386
+ virtualScrollToElementById(id)
387
+ })
388
+ }
389
+ }
390
+ defineExpose<TreeExpose>({
281
391
  toggleExpand,
282
392
  toggleChecked,
283
- tree
393
+ getNodeById,
394
+ useTree: () => tree.value,
395
+ getTree: () => tree,
396
+ scrollToTop,
397
+ scrollToBottom,
398
+ scrollToPosition,
399
+ scrollToElement,
400
+ scrollToElementById
284
401
  })
285
402
  </script>
286
403
 
@@ -1,6 +1,13 @@
1
- import type { ExtractPropTypes } from 'vue'
1
+ import type { ComponentPublicInstance, ExtractPropTypes, Ref } from 'vue'
2
2
  import { baseProps, makeArrayProp, makeBooleanProp, makeNumberProp, makeNumericProp, makeStringProp } from '../common/props'
3
3
 
4
+ // 树结构类型定义
5
+ export type Tree = {
6
+ treeNodeMap: Map<string, TreeNode>
7
+ levelTreeNodeMap: Map<number, TreeNode[]>
8
+ maxLevel: number
9
+ treeNodes: TreeNode[]
10
+ }
4
11
  // 原始树节点类型
5
12
  export type RawTreeNode = {
6
13
  disabled?: boolean
@@ -25,13 +32,13 @@ export type TreeNode = {
25
32
  isCurrent?: boolean
26
33
  }
27
34
 
28
- export const textProps = {
35
+ export const treeProps = {
29
36
  ...baseProps,
30
37
  data: makeArrayProp<RawTreeNode>(),
31
38
  showCheckbox: makeBooleanProp(false),
32
39
  childrenKey: makeStringProp('children'),
33
40
  labelKey: makeStringProp('name'),
34
- valueKey: makeStringProp('id'),
41
+ nodeKey: makeStringProp('id'),
35
42
  defaultExpandedKeys: makeArrayProp<string>(),
36
43
  expandAll: makeBooleanProp(false),
37
44
  checkStrictly: makeBooleanProp(false),
@@ -40,9 +47,39 @@ export const textProps = {
40
47
  default: ''
41
48
  },
42
49
  emptyText: makeStringProp('暂无数据'),
43
- maxHeight: makeStringProp('300px'),
50
+ height: makeStringProp('300px'),
51
+ /**
52
+ * 是否显示回到顶部按钮
53
+ * 类型:boolean
54
+ * 默认值:false
55
+ */
56
+ showBackToTop: makeBooleanProp(false),
57
+ /**
58
+ * 滚动多远显示backToTop
59
+ * 类型:number
60
+ * 默认值:'300px'
61
+ */
62
+ backToTopThreshold: makeStringProp('300px'),
63
+ /**
64
+ * 单个项目高度
65
+ * 类型:number
66
+ * 默认值:'44px'
67
+ */
68
+ itemHeight: makeStringProp('44px'),
44
69
  indent: makeNumberProp(16),
45
70
  selectionLeafOnly: makeBooleanProp(false)
46
71
  }
47
-
48
- export type TextProps = ExtractPropTypes<typeof textProps>
72
+ export type TreeExpose = {
73
+ toggleExpand: (node: TreeNode, flag: boolean) => void
74
+ toggleChecked: (node: TreeNode, flag: boolean, isClick: boolean) => void
75
+ getNodeById: (id: string | number) => TreeNode | undefined
76
+ useTree: () => Tree
77
+ getTree: () => Ref<Tree>
78
+ scrollToTop: () => void
79
+ scrollToBottom: () => void
80
+ scrollToPosition: (position: number | string) => void
81
+ scrollToElement: (item: any) => void
82
+ scrollToElementById: (id: string | number) => void
83
+ }
84
+ export type TreeProps = ExtractPropTypes<typeof treeProps>
85
+ export type TreeInstance = ComponentPublicInstance<TreeProps, TreeExpose>
@@ -0,0 +1,51 @@
1
+ import type { RawTreeNode, TreeProps } from './types'
2
+
3
+ export const useTreeMethods = (props: TreeProps) => {
4
+ function getDisabled(node: RawTreeNode): boolean {
5
+ return node.disabled ?? false
6
+ }
7
+
8
+ function getChildren(node: RawTreeNode): RawTreeNode[] {
9
+ return node[props.childrenKey] ?? []
10
+ }
11
+
12
+ function getLabel(node: RawTreeNode): string {
13
+ return node[props.labelKey] ?? ''
14
+ }
15
+
16
+ function getKey(node: RawTreeNode | undefined): string {
17
+ if (!node) {
18
+ return ''
19
+ }
20
+ return (node[props.nodeKey] ?? '') as string
21
+ }
22
+
23
+ return {
24
+ getDisabled,
25
+ getChildren,
26
+ getLabel,
27
+ getKey
28
+ }
29
+ }
30
+ /**
31
+ * 判断两个 Set 中的元素是否完全相同(不考虑顺序)
32
+ * @param {Set} setA - 第一个 Set
33
+ * @param {Set} setB - 第二个 Set
34
+ * @returns {boolean} 两个 Set 是否相同
35
+ */
36
+ export function isSetsEqual(setA: Set<any>, setB: Set<any>) {
37
+ // 步骤1:如果大小不同,直接返回 false
38
+ if (setA.size !== setB.size) {
39
+ return false
40
+ }
41
+
42
+ // 步骤2:遍历 setA 的所有元素,检查 setB 是否都包含
43
+ for (const item of setA) {
44
+ if (!setB.has(item)) {
45
+ return false // 有元素不匹配,返回 false
46
+ }
47
+ }
48
+
49
+ // 所有元素匹配,返回 true
50
+ return true
51
+ }
@@ -3,10 +3,10 @@
3
3
  @include b(virtual-scroll) {
4
4
  position: relative;
5
5
  width: 100%;
6
+ height: 100%;
6
7
 
7
8
  @include e(view) {
8
9
  width: 100%;
9
- height: 100%;
10
10
  }
11
11
  @include e(container) {
12
12
  position: relative;
@@ -1,26 +1,44 @@
1
1
  <template>
2
- <view :class="['oxy-virtual-scroll', customClass]" :style="{ height: height }">
2
+ <view :class="['oxy-virtual-scroll', customClass]" :style="customStyle">
3
3
  <!-- 滚动内容区域 -->
4
4
  <scroll-view
5
- ref="scrollView"
5
+ v-if="data.length"
6
+ :style="{ height: height }"
6
7
  class="oxy-virtual-scroll__view"
7
- :scroll-y="true"
8
+ :scroll-y="scrollY"
8
9
  :scroll-x="scrollX"
10
+ :upper-threshold="upperThreshold"
11
+ :lower-threshold="lowerThreshold"
9
12
  :scroll-top="scrollTop"
10
- :scroll-into-view="itemId"
11
- v-bind="$attrs"
13
+ :scroll-left="scrollLeft"
14
+ :scroll-with-animation="scrollWithAnimation"
15
+ :enable-back-to-top="enableBackToTop"
16
+ :show-scrollbar="showScrollbar"
17
+ :refresher-enabled="refresherEnabled"
18
+ :refresher-threshold="refresherThreshold"
19
+ :refresher-default-style="refresherDefaultStyle"
20
+ :refresher-background="refresherBackground"
21
+ :refresher-triggered="triggered"
22
+ :enable-flex="enableFlex"
23
+ :scroll-anchoring="scrollAnchoring"
12
24
  @scroll="onScroll"
13
25
  @scrolltoupper="onScrollUpper"
14
26
  @scrolltolower="onScrollLower"
15
27
  >
16
28
  <view class="oxy-virtual-scroll__container" :style="{ height: totalHeight + 'px' }">
17
29
  <view class="oxy-virtual-scroll__items" :style="{ transform: `translateY(${virtualOffsetY}px)` }">
18
- <slot name="item" v-for="(item, index) in virtualData" :item="item" :index="startIndex + index"></slot>
30
+ <view v-for="(item, index) in virtualData" :key="index">
31
+ <slot name="item" :item="item" :index="startIndex + index"></slot>
32
+ </view>
19
33
  <slot name="bottom"></slot>
20
34
  </view>
21
35
  </view>
22
36
  </scroll-view>
23
37
 
38
+ <view v-else>
39
+ <oxy-status-tip image="content" :tip="emptyText" />
40
+ </view>
41
+
24
42
  <!-- 回到顶部按钮 -->
25
43
  <view v-if="showBackToTop && showBackTopBtn" class="oxy-virtual-scroll__back-top" @click="scrollToTop">
26
44
  <oxy-icon name="backtop" color="#fff" size="20px"></oxy-icon>
@@ -40,91 +58,47 @@ export default {
40
58
  </script>
41
59
 
42
60
  <script lang="ts" setup>
43
- import { computed, onMounted, ref, watch, nextTick } from 'vue'
61
+ import { toRefs } from 'vue'
44
62
  import type { ScrollViewOnScrollEvent, ScrollViewOnScrolltolowerEvent, ScrollViewOnScrolltoupperEvent } from '@uni-helper/uni-app-types'
45
- import { VirtualScrollEngine } from './virtual-scroll'
46
- import { virtualScrollProps } from './types'
47
- defineOptions({
48
- inheritAttrs: false
49
- })
63
+ import { virtualScrollProps, type VirtualScrollExpose } from './types'
64
+ import { useVirtualScroll } from '../composables/useVirtualScroll'
65
+
50
66
  const props = defineProps(virtualScrollProps)
51
67
  const emit = defineEmits(['scroll', 'scroll-to-upper', 'scroll-to-lower'])
52
68
 
53
- const scrollView = ref(null)
54
- const itemId = ref<string>('')
55
- const scrollTop = ref<number>(0)
56
- const showBackTopBtn = ref<boolean>(false)
57
- const virtualData = ref<any[]>([])
58
- const startIndex = ref<number>(0)
59
- const virtualOffsetY = ref<number>(0)
60
- const virtualEngine = ref<any>(null)
61
-
62
- // 显示的数据(索引处理后的)
63
- const displayData = computed<any[]>(() => {
64
- return props.data
65
- })
66
-
67
- // 虚拟列表总高度
68
- const totalHeight = computed(() => {
69
- return displayData.value.length * parseFloat(props.itemHeight) // 假设每项高度50px
70
- })
71
-
72
- watch(
73
- () => props.data,
74
- () => {
75
- nextTick(initScrollData)
76
- },
77
- {
78
- immediate: true,
79
- deep: true
80
- }
81
- )
82
-
83
- onMounted(() => {
84
- initScrollEngine()
69
+ // 解构props用于组合式函数
70
+ const { data, virtual, height, itemHeight, idKey, showBackToTop, backToTopThreshold } = toRefs(props)
71
+
72
+ // 使用虚拟滚动组合式函数
73
+ const {
74
+ scrollTop,
75
+ showBackTopBtn,
76
+ virtualData,
77
+ startIndex,
78
+ virtualOffsetY,
79
+ totalHeight,
80
+ displayData,
81
+ initScrollData,
82
+ initScrollEngine,
83
+ updateVisibleData,
84
+ scrollToTop: virtualScrollToTop,
85
+ scrollToBottom: virtualScrollToBottom,
86
+ scrollToPosition: virtualScrollToPosition,
87
+ scrollToElement: virtualScrollToElement,
88
+ scrollToElementById: virtualScrollToElementById,
89
+ onScroll: handleVirtualScroll
90
+ } = useVirtualScroll({
91
+ data,
92
+ virtual,
93
+ height,
94
+ itemHeight,
95
+ idKey,
96
+ backToTopThreshold
85
97
  })
86
98
 
87
- // 初始化滚动数据
88
- function initScrollData() {
89
- if (!props.virtual) {
90
- // 非虚拟滚动模式:直接使用全部数据
91
- virtualData.value = displayData.value
92
- virtualOffsetY.value = 0
93
- return
94
- }
95
- virtualEngine.value = new VirtualScrollEngine({
96
- containerHeight: parseFloat(props.height),
97
- itemHeight: parseFloat(props.itemHeight),
98
- data: displayData.value
99
- })
100
- updateVisibleData()
101
- }
102
-
103
- // 初始化滚动引擎
104
- function initScrollEngine() {
105
- if (!props.virtual) return
106
- virtualEngine.value = new VirtualScrollEngine({
107
- containerHeight: parseFloat(props.height),
108
- itemHeight: parseFloat(props.itemHeight),
109
- data: displayData.value
110
- })
111
- }
112
-
113
- // 更新可见数据
114
- function updateVisibleData() {
115
- if (!props.virtual) return // 非虚拟模式不处理
116
- if (virtualEngine.value && scrollView.value) {
117
- const { visibleData, offsetY } = virtualEngine.value.updateVisibleData(scrollTop.value || 0)
118
- virtualData.value = visibleData
119
- virtualOffsetY.value = offsetY
120
- }
121
- }
122
-
123
99
  // 滚动事件
124
100
  function onScroll(event: ScrollViewOnScrollEvent) {
125
- scrollTop.value = event.detail.scrollTop
126
- showBackTopBtn.value = scrollTop.value > parseFloat(props.backToTopThreshold)
127
- updateVisibleData()
101
+ handleVirtualScroll(event.detail.scrollTop)
128
102
  emit('scroll', event)
129
103
  }
130
104
 
@@ -138,39 +112,24 @@ function onScrollLower(event: ScrollViewOnScrolltolowerEvent) {
138
112
  emit('scroll-to-lower', event)
139
113
  }
140
114
 
141
- // 回到顶部
142
- function scrollToTop() {
143
- scrollTop.value = 0
144
- nextTick(() => {
145
- scrollTop.value = 0
146
- })
115
+ // 滚动方法
116
+ const scrollToTop = () => {
117
+ virtualScrollToTop()
147
118
  }
148
- function scrollToBottom() {
149
- scrollToPosition(totalHeight.value)
119
+ const scrollToBottom = () => {
120
+ virtualScrollToBottom()
150
121
  }
151
-
152
- // 滚动到指定位置
153
- function scrollToPosition(position: number | string) {
154
- scrollTop.value = typeof position === 'number' ? position : parseFloat(position)
122
+ const scrollToPosition = (position: number | string) => {
123
+ virtualScrollToPosition(position)
155
124
  }
156
-
157
- // 滚动到指定元素
158
- function scrollToElement(item: any) {
159
- const index = props.data.findIndex((o) => item[props.idKey] && o[props.idKey] && o[props.idKey] === item[props.idKey])
160
- if (index > 0) {
161
- const scrollDistance = parseFloat(props.itemHeight) * index
162
- scrollToPosition(scrollDistance)
163
- }
125
+ const scrollToElement = (item: any) => {
126
+ virtualScrollToElement(item)
164
127
  }
165
- function scrollToElementById(id: string | number) {
166
- const index = props.data.findIndex((o) => id && o[props.idKey] && o[props.idKey] === id)
167
- if (index > 0) {
168
- const scrollDistance = parseFloat(props.itemHeight) * index
169
- scrollToPosition(scrollDistance)
170
- }
128
+ const scrollToElementById = (id: string | number) => {
129
+ virtualScrollToElementById(id)
171
130
  }
172
131
 
173
- defineExpose({
132
+ defineExpose<VirtualScrollExpose>({
174
133
  scrollToTop,
175
134
  scrollToBottom,
176
135
  scrollToPosition,