plain-design 1.0.0-beta.30 → 1.0.0-beta.32

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.
Files changed (61) hide show
  1. package/dist/plain-design.commonjs.min.js +3 -3
  2. package/dist/plain-design.min.css +11 -6
  3. package/dist/plain-design.min.js +3 -3
  4. package/dist/report.html +2 -2
  5. package/package.json +3 -3
  6. package/src/packages/components/$previewer/ImagePreviewerFixedContainer.tsx +107 -0
  7. package/src/packages/components/$previewer/image-previewer-fixed-container.scss +18 -0
  8. package/src/packages/components/$previewer/index.tsx +52 -0
  9. package/src/packages/components/Application/service/useApplicationService.tsx +2 -0
  10. package/src/packages/components/Carousel/carousel.scss +391 -0
  11. package/src/packages/components/Carousel/index.tsx +569 -22
  12. package/src/packages/components/CarouselItem/index.tsx +77 -0
  13. package/src/packages/components/ImagePreviewer/ImagePreviewer.tsx +572 -0
  14. package/src/packages/components/ImagePreviewer/ImagePreviewerButtonBar.tsx +140 -0
  15. package/src/packages/components/ImagePreviewer/ImagePreviewerCarouselImage.tsx +54 -0
  16. package/src/packages/components/ImagePreviewer/ImagePreviewerGallery.tsx +202 -0
  17. package/src/packages/components/ImagePreviewer/PreviewerLoading.tsx +26 -0
  18. package/src/packages/components/ImagePreviewer/image-previewer.scss +244 -0
  19. package/src/packages/components/ImagePreviewer/image-previewer.utils.tsx +135 -0
  20. package/src/packages/components/ImagePreviewer/index.tsx +5 -0
  21. package/src/packages/components/ImagePreviewer/previewer-loading.scss +52 -0
  22. package/src/packages/components/Input/useMultipleInput.tsx +2 -79
  23. package/src/packages/components/InputNumber/NumberResize.tsx +14 -1
  24. package/src/packages/components/InputNumber/input-number.utils.tsx +7 -5
  25. package/src/packages/components/InputNumber/useInputNumber.public.tsx +25 -6
  26. package/src/packages/components/Scroll/index.tsx +6 -6
  27. package/src/packages/components/SortList/index.tsx +191 -0
  28. package/src/packages/components/SortList/sort-list.scss +11 -0
  29. package/src/packages/components/StackCard/index.tsx +260 -0
  30. package/src/packages/components/StackCard/stack-card.scss +28 -0
  31. package/src/packages/components/StackCardItem/index.tsx +23 -0
  32. package/src/packages/components/Table/standard/PlcOperation/PlcOperation.tsx +1 -1
  33. package/src/packages/components/Table/standard/PlcTree/RenderPlcTreeNode.tsx +2 -1
  34. package/src/packages/components/Table/table/body/row.tsx +1 -1
  35. package/src/packages/components/Table/table/use/useTableDraggier.row.tsx +1 -1
  36. package/src/packages/components/Table/table/utils/createTableHooks.ts +1 -1
  37. package/src/packages/components/Tree/RenderTreeNode.tsx +2 -1
  38. package/src/packages/components/Tree/index.tsx +2 -1
  39. package/src/packages/components/VirtualList/index.tsx +12 -3
  40. package/src/packages/components/VirtualList/useVirtualList.tsx +129 -86
  41. package/src/packages/components/VirtualList/virtual-list.scss +31 -17
  42. package/src/packages/components/VirtualTable/index.tsx +1 -1
  43. package/src/packages/entry.tsx +5 -1
  44. package/src/packages/uses/useDragHorizontalScroll.ts +82 -0
  45. package/src/packages/utils/ComponentUtils.ts +10 -0
  46. package/src/packages/utils/buildCycleIndexList.ts +31 -0
  47. package/src/packages/utils/getDeviceInfo.ts +44 -44
  48. package/src/packages/utils/getRectAutoFormat.ts +9 -0
  49. package/src/packages/utils/notNull.ts +9 -0
  50. package/src/pages/index/app.scss +1 -1
  51. package/src/pages/index/components/normal/DemoCarousel.tsx +178 -73
  52. package/src/pages/index/components/normal/DemoKeepAlive.tsx +25 -25
  53. package/src/pages/index/components/normal/DemoNumber.tsx +4 -1
  54. package/src/pages/index/components/normal/DemoSortList.tsx +70 -0
  55. package/src/pages/index/components/normal/DemoStackCard.tsx +356 -0
  56. package/src/pages/index/components/normal/DemoVirtualList.tsx +89 -3
  57. package/src/pages/index/components/service/DemoImagePreviewer.tsx +185 -0
  58. package/src/pages/index/home/AppHome.tsx +18 -3
  59. package/src/pages/index/home/menus.tsx +3 -1
  60. package/src/packages/components/CarouselGroup/carousel.scss +0 -143
  61. package/src/packages/components/CarouselGroup/index.tsx +0 -274
@@ -0,0 +1,260 @@
1
+ import {CSSProperties, designComponent, getComponentCls, iHTMLDivElement, isReact, mergeAttrs, onBeforeUnmount, onMounted, PropType, reactive, useClasses, useModel, useRefs} from "plain-design-composition";
2
+ import './stack-card.scss';
3
+ import {findReactElement} from "../../utils/findReactElement";
4
+ import StackCardItem from "../StackCardItem";
5
+ import {createEffects} from "plain-utils/utils/createEffects";
6
+ import {addElementListener} from "plain-utils/dom/addElementListener";
7
+ import {delay} from "plain-utils/utils/delay";
8
+
9
+ export const StackCard = designComponent({
10
+ name: 'stack-card',
11
+ props: {
12
+ modelValue: { type: [String, Number] }, // 双向绑定值,当前显示哪一个子节点
13
+ leftBufferCount: { type: Number }, // 左侧显示元素个数
14
+ rightBufferCount: { type: Number }, // 右侧显示元素个数
15
+ stackItemWidth: { type: Number, default: 24 }, // 每一张折叠卡片横向偏移距离
16
+ stackItemHeight: { type: Number, default: 12 }, //每一张折叠卡片纵向向偏移距离
17
+ scaleRate: { type: Number, default: 0.1 }, // 缩放比例
18
+ verticalOffset: { type: Boolean }, // 开启纵向偏移
19
+ verticalAlign: { type: String as PropType<'top' | 'center' | 'bottom'>, default: 'center' }, // 纵向对齐方式
20
+ },
21
+ emits: {
22
+ onUpdateModelValue: (val?: string | number) => true,
23
+ },
24
+ slots: ['default'],
25
+ setup({ props, slots, event: { emit } }) {
26
+
27
+ const { effects } = createEffects();
28
+
29
+ const { refs, onRef } = useRefs({ el: iHTMLDivElement, container: iHTMLDivElement });
30
+
31
+ const model = useModel(() => props.modelValue, emit.onUpdateModelValue, { onChange: async () => {await utils.resetData();} });
32
+
33
+ const classes = useClasses(() => [
34
+ getComponentCls('stack-card'),
35
+ {
36
+ 'stack-card-touching': draggier.draggierState.touching
37
+ }
38
+ ]);
39
+
40
+ const draggier = (() => {
41
+
42
+ const draggierState = reactive({ left: 0, touching: false, });
43
+
44
+ const touchstart = (e: TouchEvent | MouseEvent) => {
45
+ e.preventDefault();
46
+ e.stopPropagation();
47
+
48
+ let staticState = {
49
+ startLeft: draggierState.left,
50
+ startX: 'touches' in e ? e.touches[0].clientX : e.clientX,
51
+ };
52
+ const { effects: draggierEffects } = createEffects();
53
+
54
+ const touchmove = (e: TouchEvent | MouseEvent) => {
55
+ let durX = Math.ceil(('touches' in e ? e.touches[0].clientX : e.clientX) - staticState.startX);
56
+ if (!draggierState.touching) {
57
+ if (Math.abs(durX) > 10) {
58
+ draggierState.touching = true;
59
+ draggierEffects.push(() => {
60
+ draggierState.touching = false;
61
+ if (!!renderData) {
62
+ /**
63
+ * 设置滚动位置为最近的子节点
64
+ * @author 韦胜健
65
+ * @date 2024/1/4 21:12
66
+ */
67
+ const { closestItem, itemsList, itemValList } = renderData;
68
+ draggierState.left = itemsList.find(i => i.index === closestItem.index)!.startLeft;
69
+ const newVal = itemValList[closestItem.index];
70
+ if (newVal != model.value) {model.value = newVal;}
71
+ }
72
+ });
73
+ }
74
+ }
75
+ if (!draggierState.touching) {return;}
76
+ /*实时设置滚动位置*/
77
+ durX = Math.ceil(durX / 3);
78
+ draggierState.left = -durX + staticState.startLeft;
79
+ };
80
+
81
+ draggierEffects.push(addElementListener(document.body, 'mousemove', touchmove));
82
+ draggierEffects.push(addElementListener(document.body, 'mouseup', draggierEffects.clear));
83
+ draggierEffects.push(addElementListener(document.body, 'touchmove', touchmove));
84
+ draggierEffects.push(addElementListener(document.body, 'touchend', draggierEffects.clear));
85
+ };
86
+
87
+ return { draggierState, touchstart };
88
+ })();
89
+
90
+ const utils = {
91
+ /**
92
+ * 重新计算当前渲染信息数据
93
+ * @author 韦胜健
94
+ * @date 2024/1/4 21:13
95
+ */
96
+ resetData: async () => {
97
+ await delay();
98
+ const activeIndex = model.value == null ? -1 : renderData.itemValList.indexOf(model.value);
99
+ if (activeIndex === -1) {return;}
100
+ const newItem = renderData.itemsList.find(i => i.index === activeIndex);
101
+ if (!!newItem) {
102
+ draggier.draggierState.left = renderData.itemsList.find(i => i.index === activeIndex)!.startLeft;
103
+ }
104
+ },
105
+ };
106
+
107
+ onMounted(() => {
108
+ refs.el!.addEventListener('touchstart', draggier.touchstart);
109
+ refs.el!.addEventListener('mousedown', draggier.touchstart);
110
+ utils.resetData();
111
+ });
112
+
113
+ onBeforeUnmount(effects.clear);
114
+
115
+ let renderData = {
116
+ /*子节点信息,listLeft为子节点相对于o位置的偏移量*/
117
+ itemsList: [] as { index: number, startLeft: number, endLeft: number }[],
118
+ /*子节点的val数组*/
119
+ itemValList: [] as (string | number)[],
120
+ /*子节点转换为循环列表的信息,distance为循环列表中元素距离选中元素的偏移量*/
121
+ listData: [] as { index: number, startLeft: number, endLeft: number, distance: number }[],
122
+ /*最靠近选中元素的节点数据*/
123
+ closestItem: {} as { index: number, startLeft: number, endLeft: number },
124
+ };
125
+
126
+ return () => {
127
+
128
+ const content = slots.default() as any;
129
+ /*从内容解析出来StackCardItem的数量及其props*/
130
+ const items: any[] = !content ? [] : findReactElement(content, (i: any) => i.type?.name === StackCardItem.name) || [];
131
+ const itemValList = items.map((i, index) => i.props?.val == null ? index : i.props?.val);
132
+ const total = items.length;
133
+
134
+ let leftBufferCount = props.leftBufferCount == null ? Math.ceil(total / 2) : props.leftBufferCount;
135
+ let rightBufferCount = props.rightBufferCount == null ? Math.ceil(total / 2) : props.rightBufferCount;
136
+
137
+ const elStyles = {} as CSSProperties;
138
+ elStyles.paddingLeft = `${(leftBufferCount) * props.stackItemWidth}px`;
139
+ elStyles.paddingRight = `${(rightBufferCount - 1) * props.stackItemWidth}px`;
140
+ if (!!props.verticalOffset) {
141
+ elStyles.paddingBottom = `${Math.max(leftBufferCount, rightBufferCount - 1) * props.stackItemHeight}px`;
142
+ }
143
+
144
+ let itemStyles: CSSProperties[] = [];
145
+
146
+ /*实际中拖拽的left距离,与item的startLeft匹配,item的startLeft实际上是item的中心距离*/
147
+ let pointLeft = draggier.draggierState.left;
148
+ /*拖拽的left距离向右偏移半个stackItemWidth得到offsetPointLeft, 通过offsetPointLeft在item的startLeft到endLeft之间的位置找到closetItem*/
149
+ let offsetPointLeft = Math.floor(props.stackItemWidth / 2 + pointLeft);
150
+ /*计算每个item的startLeft以及endLeft*/
151
+ const itemsList: { index: number, startLeft: number, endLeft: number; }[] = itemValList.map((_, index) => ({ index, startLeft: index * props.stackItemWidth, endLeft: (index + 1) * props.stackItemWidth, }));
152
+
153
+ /*总长度*/
154
+ const maxEndLeft = itemsList[itemsList.length - 1]?.endLeft;
155
+
156
+ /*修正left,超出maxListLeft或者小于0时,循环计算left使其落在0到maxListLeft的范围*/
157
+ if (!!itemsList.length && !!maxEndLeft) {
158
+ while (offsetPointLeft > maxEndLeft) {
159
+ offsetPointLeft -= maxEndLeft;
160
+ pointLeft -= maxEndLeft;
161
+ }
162
+ while (offsetPointLeft < 0) {
163
+ offsetPointLeft += maxEndLeft;
164
+ pointLeft += maxEndLeft;
165
+ }
166
+ }
167
+
168
+ /*找到最靠近的位置*/
169
+ const closestItem = itemsList.reduce((prev, item) => {
170
+ if (item.startLeft <= offsetPointLeft && item.endLeft >= offsetPointLeft) {
171
+ return { distance: Math.abs(item.startLeft - pointLeft), item };
172
+ } else {
173
+ return prev;
174
+ }
175
+ }, { distance: Infinity, item: null } as { distance: number, item: { index: number, startLeft: number, endLeft: number } | null }).item!;
176
+
177
+ /*循环列表前半部分*/
178
+ const leftItemsList = itemsList.slice(0, closestItem.index);
179
+ /*循环列表后半部分*/
180
+ const rightItemsList = itemsList.slice(closestItem.index);
181
+
182
+ /*根据左侧右侧预留的空位填补前后列表*/
183
+ while (leftItemsList.length - rightItemsList.length > 1) {rightItemsList.push(leftItemsList.shift()!);}
184
+ while (rightItemsList.length - leftItemsList.length > 1) {leftItemsList.unshift(rightItemsList.pop()!);}
185
+ while (leftItemsList.length > leftBufferCount) {
186
+ const shiftItem = leftItemsList.shift()!;
187
+ if (rightItemsList.length < rightBufferCount) {
188
+ rightItemsList.push(shiftItem);
189
+ }
190
+ }
191
+ while (rightItemsList.length > rightBufferCount) {
192
+ const popItem = rightItemsList.pop()!;
193
+ if (leftItemsList.length < leftBufferCount) {
194
+ leftItemsList.unshift(popItem);
195
+ }
196
+ }
197
+
198
+ /*此时后半部分的第一个元素就是选中的元素,索引就是前半部分列表的最后一个位置+1*/
199
+ const activeIndex = leftItemsList.length;
200
+ /*根据最靠近left的节点来计算当前的少量偏移*/
201
+ const offsetLeft = closestItem.startLeft - pointLeft;
202
+ /*计算循环列表中每个节点距离选中节点的偏移距离*/
203
+ const list = [...leftItemsList, ...rightItemsList].map((item, index) => ({
204
+ ...item,
205
+ distance: (index - activeIndex) * props.stackItemWidth,
206
+ zIndex: 0,
207
+ }));
208
+ /*先排序,然后根据顺序设置zIndex,最靠近left的节点zIndex最大*/
209
+ list.sort((a, b) => Math.abs(a.distance) - Math.abs(b.distance)).forEach((i, index) => {
210
+ i.zIndex = total - index;
211
+ });
212
+ /*计算子节点样式*/
213
+ itemStyles = itemsList.map((_, index) => {
214
+ const item = list.find(i => i.index === index);
215
+ if (!item) {
216
+ /*不在前部分列表后部分列表的元素,可能是因为预留位置不足,这里不做显示*/
217
+ return {
218
+ opacity: 0,
219
+ transition: 'none'
220
+ };
221
+ }
222
+ /*元素的偏移距离*/
223
+ const left = item.distance + offsetLeft;
224
+ /*根据偏移距离实时计算缩放大小,越靠近left,越接近1*/
225
+ const scale = Math.max(0, 1 - Number((Math.abs(left) / props.stackItemWidth * props.scaleRate).toFixed(2)));
226
+ return {
227
+ transform: `translate3d(${left}px,${props.verticalOffset ? `${left / props.stackItemWidth * props.stackItemHeight}px` : 0},0) scale(${scale})`,
228
+ transformOrigin: `${left > 0 ? 'right' : 'left'} ${props.verticalAlign}`,
229
+ zIndex: item.zIndex,
230
+ // opacity: total - item.zIndex === 0 ? 1 : 0.75,
231
+ };
232
+ });
233
+
234
+ renderData = {
235
+ itemsList,
236
+ closestItem,
237
+ listData: list,
238
+ itemValList,
239
+ };
240
+
241
+ return (
242
+ <div className={classes.value} style={elStyles} ref={onRef.el}>
243
+ <div className="stack-card-container" ref={onRef.container}>
244
+ {items.map((Item: any, index: number) => {
245
+ const ItemComp = isReact ? Item.type : Item;
246
+ return (
247
+ <ItemComp
248
+ {...mergeAttrs({ style: itemStyles[index] || {} }, Item.props)}
249
+ key={index}
250
+ />
251
+ );
252
+ })}
253
+ </div>
254
+ </div>
255
+ );
256
+ };
257
+ },
258
+ });
259
+
260
+ export default StackCard;
@@ -0,0 +1,28 @@
1
+ @include comp(stack-card) {
2
+ display: block;
3
+ box-sizing: border-box;
4
+
5
+ .stack-card-container {
6
+ position: relative;
7
+ @include comp(stack-card-item) {
8
+ transition: transform linear 100ms, opacity linear 300ms;
9
+
10
+ &:not(:first-child) {
11
+ position: absolute;
12
+ inset: 0;
13
+ }
14
+ &:first-child {
15
+ position: relative;
16
+ }
17
+
18
+ & > img {
19
+ width: 100%;
20
+ }
21
+ }
22
+ }
23
+
24
+ &:not(.stack-card-touching) {
25
+ @include comp(stack-card-item) {
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,23 @@
1
+ import {designComponent, getComponentCls, useClasses} from "plain-design-composition";
2
+
3
+ export const StackCardItem = designComponent({
4
+ name: 'stack-card-item',
5
+ props: {
6
+ val: { type: [String, Number] },
7
+ },
8
+ slots: ['default'],
9
+ setup({ props, slots, event: { emit } }) {
10
+
11
+ const classes = useClasses(() => [
12
+ getComponentCls('stack-card-item')
13
+ ]);
14
+
15
+ return () => (
16
+ <div className={classes.value}>
17
+ {slots.default()}
18
+ </div>
19
+ );
20
+ },
21
+ });
22
+
23
+ export default StackCardItem;
@@ -281,7 +281,7 @@ export const PlcOperation = designComponent({
281
281
  * @author 韦胜健
282
282
  * @date 2022.11.25 19:20
283
283
  */
284
- effects.push(table.hooks.onVirtualPageIndexChange.use(({ start }) => {state.virtualRowStart = start;}));
284
+ effects.push(table.hooks.onVirtualPageIndexChange.use(({ startIndex }) => {state.virtualRowStart = startIndex;}));
285
285
 
286
286
  onBeforeUnmount(() => effects.clear());
287
287
 
@@ -80,6 +80,7 @@ export const RenderPlcTreeNode = designComponent({
80
80
  {props.treeCore.props.multiple && (
81
81
  <div className={`tree-node-checker plc-tree-button`}>
82
82
  <Checkbox
83
+ key={props.treeNode.id}
83
84
  checkStatus={checkStatus.value}
84
85
  disabled={!props.treeNode.isCheckAble()}
85
86
  onClick={handler.onClickCheckbox}
@@ -89,7 +90,7 @@ export const RenderPlcTreeNode = designComponent({
89
90
  {props.treeCore.props.draggable && (
90
91
  <div
91
92
  className={classnames(['tree-node-draggier', 'plc-tree-button', { 'tree-node-draggier-disable': !props.treeCore.utils.isAllowDraggable(props.treeNode) }])}
92
- onMouseDown={handler.onMousedownDraggier.value}
93
+ onMouseDown={props.treeNode.empty ? undefined : handler.onMousedownDraggier.value}
93
94
  >
94
95
  <Icon icon="pi-menu"/>
95
96
  </div>
@@ -34,7 +34,7 @@ export const PltRow = designComponent({
34
34
  render: () => {
35
35
  return props.table.hooks.onRenderRow.exec({
36
36
  content: (
37
- <tr className={classes.value} style={{ height: `${props.table.numberState.bodyRowHeight}px` }} data-vid={props.vid} {...handler}>
37
+ <tr className={classes.value} style={{ height: `${props.table.numberState.bodyRowHeight}px` }} data-vid={props.vid} data-idx={String(props.node.state.index == null ? "" : props.node.state.index)} {...handler}>
38
38
  {(props.table.bodyPlcList.value?.map((plc) => <PltCell key={plc.plcKey} table={props.table} node={props.node} plc={plc}/>))}
39
39
  </tr>
40
40
  ),
@@ -191,7 +191,7 @@ export function useTableDraggierRow(
191
191
 
192
192
  const start = (e: iMouseEvent, node: iTableNode) => {
193
193
  const { clientY } = !e ? { clientY: prevClientY } : ClientZoom.getClientPosition(e);
194
- const rowEl = (Array.from(refs.el!.querySelectorAll(`.plt-row`)) as HTMLTableRowElement[]).find(i => i.dataset.vid == node.state.index.toString())!;
194
+ const rowEl = (Array.from(refs.el!.querySelectorAll(`.plt-row`)) as HTMLTableRowElement[]).find(i => i.dataset.idx == node.state.index.toString())!;
195
195
  const rowRect = rowEl.getBoundingClientRect();
196
196
  staticState = { startY: clientY, startTop: rowRect.top, node, rowEl, data: null, };
197
197
  /*清理状态*/
@@ -19,7 +19,7 @@ export const createTableHooks = () => {
19
19
  /*是否可以开启虚拟滚动*/
20
20
  onEnableVirtual: createSyncHooks<(data: boolean) => void>(),
21
21
  /*当虚拟滚动的pageIndex变化的时候触发动作*/
22
- onVirtualPageIndexChange: createHooks<(data: { pageIndex: number, start: number }) => void>(),
22
+ onVirtualPageIndexChange: createHooks<(data: { pageIndex: number, startIndex: number }) => void>(),
23
23
  /*点击行动作*/
24
24
  onClickRow: createHooks<(data: { e: iMouseEvent, node: iTableNode }) => void>(),
25
25
  /*双击行动作*/
@@ -131,7 +131,7 @@ export const RenderTreeNode = designComponent({
131
131
  {tree.props.draggable && !tree.props.customDraggier && (
132
132
  <div
133
133
  className={classnames(['tree-node-draggier', { 'tree-node-draggier-disable': !tree.utils.isAllowDraggable(props.treeNode) }])}
134
- onMouseDown={handler.onMousedownDraggier.value}
134
+ onMouseDown={props.treeNode.empty ? undefined : handler.onMousedownDraggier.value}
135
135
  >
136
136
  <Icon icon="pi-menu"/>
137
137
  </div>
@@ -139,6 +139,7 @@ export const RenderTreeNode = designComponent({
139
139
  {tree.props.multiple && (
140
140
  <div className="tree-node-checker">
141
141
  <Checkbox
142
+ key={props.treeNode.id}
142
143
  checkStatus={checkStatus.value}
143
144
  disabled={!props.treeNode.isCheckAble()}
144
145
  onClick={handler.onClickCheckbox}
@@ -26,6 +26,7 @@ export const Tree = designComponent({
26
26
  scroll: { type: Boolean }, // 是否使用滚动容器
27
27
  innerAttrs: { type: Object as PropType<PlainObject> },// inner 节点属性类型
28
28
  contentWrap: { type: Boolean }, // 节点内容是否换行显示,默认不换行
29
+ virtualRowType: { type: String as PropType<'virtualIndex' | 'realIndex'> }, // 虚拟滚动的行渲染类型,virtualIndex意思为使用虚拟的行索引作为key渲染行,好处是虚拟滚动时完全复用行组件实例;realIndex意味着虚拟滚动时新的行会重新初始化
29
30
  ...TreePropsOptions,
30
31
  },
31
32
  emits: {
@@ -94,7 +95,7 @@ export const Tree = designComponent({
94
95
  {{
95
96
  content: ({ data }: { data: { item: iTreeNode, index: number, vIndex: number, vid: string }[] }) => (
96
97
  <div className="tree-node-list">
97
- {data.map(({ item, vid }, flatIndex) => <RenderTreeNode key={flatIndex} treeNode={item} dataVid={vid} multiple={props.multiple}/>)}
98
+ {data.map(({ item, vid, index }, vIndex) => <RenderTreeNode key={(props.virtualRowType === 'realIndex') ? index : vIndex} treeNode={item} dataVid={vid} multiple={props.multiple}/>)}
98
99
  </div>
99
100
  )
100
101
  }}
@@ -11,12 +11,14 @@ export const VirtualList = designComponent({
11
11
  size: { type: Number, require: true, default: 40 }, // 每一行高度
12
12
  dynamicSize: { type: Boolean }, // 标识列表中的每一行高度不是固定的,但是还是需要提供 size 属性,而且size属性不能与每一行的高度差距太多;
13
13
  disabled: { type: Boolean }, // 禁用虚拟滚动
14
- width: { type: [String, Number] }, // 当需要横向滚动是,需要指定内容宽度
14
+ horizontal: { type: Boolean }, // 启用横向虚拟滚动
15
+ width: { type: [String, Number] }, // 纵向虚拟滚动需要横向滚动时,需要指定内容宽度
16
+ height: { type: [String, Number] }, // 横向虚拟滚动需要纵向滚动时,需要指定内容高度
15
17
  },
16
18
  emits: {
17
19
  onScroll: (e: Event) => true,
18
20
  onRefScroll: (scroll: typeof Scroll.use.class | null) => true,
19
- onPageIndexChange: (data: { pageIndex: number, start: number }) => true,
21
+ onPageIndexChange: (data: { pageIndex: number, startIndex: number, endIndex: number }) => true,
20
22
  },
21
23
  scopeSlots: {
22
24
  default: (scope: { item: any, index: number, vid: string, vIndex: number }) => {},
@@ -43,7 +45,14 @@ export const VirtualList = designComponent({
43
45
  render: () => {
44
46
  const { list } = virtual.offsetData.value;
45
47
  return (
46
- <Scroll scrollX={!!props.width} onScroll={virtual.handler.onScroll} ref={onRefScroll as any} className={virtual.classes.value} disableTransitionWhenScroll>
48
+ <Scroll
49
+ scrollX={!!props.width || props.horizontal}
50
+ scrollY={!!props.height || !props.horizontal}
51
+ onScroll={virtual.handler.onScroll}
52
+ ref={onRefScroll as any}
53
+ className={virtual.classes.value}
54
+ disableTransitionWhenScroll
55
+ >
47
56
  <div className="virtual-list-strut" style={virtual.strutStyles.value}>
48
57
  <div className="virtual-list-content" style={virtual.contentStyles.value} ref={onRef.content}>
49
58
  {scopeSlots.content.isExist() ?