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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) 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/Scroll/index.tsx +6 -6
  24. package/src/packages/components/SortList/index.tsx +191 -0
  25. package/src/packages/components/SortList/sort-list.scss +11 -0
  26. package/src/packages/components/StackCard/index.tsx +260 -0
  27. package/src/packages/components/StackCard/stack-card.scss +28 -0
  28. package/src/packages/components/StackCardItem/index.tsx +23 -0
  29. package/src/packages/components/Table/standard/PlcOperation/PlcOperation.tsx +1 -1
  30. package/src/packages/components/Table/standard/PlcTree/RenderPlcTreeNode.tsx +2 -1
  31. package/src/packages/components/Table/table/body/row.tsx +1 -1
  32. package/src/packages/components/Table/table/use/useTableDraggier.row.tsx +1 -1
  33. package/src/packages/components/Table/table/utils/createTableHooks.ts +1 -1
  34. package/src/packages/components/Tree/RenderTreeNode.tsx +2 -1
  35. package/src/packages/components/Tree/index.tsx +2 -1
  36. package/src/packages/components/VirtualList/index.tsx +12 -3
  37. package/src/packages/components/VirtualList/useVirtualList.tsx +129 -86
  38. package/src/packages/components/VirtualList/virtual-list.scss +31 -17
  39. package/src/packages/components/VirtualTable/index.tsx +1 -1
  40. package/src/packages/entry.tsx +5 -1
  41. package/src/packages/uses/useDragHorizontalScroll.ts +82 -0
  42. package/src/packages/utils/ComponentUtils.ts +10 -0
  43. package/src/packages/utils/buildCycleIndexList.ts +31 -0
  44. package/src/packages/utils/getDeviceInfo.ts +44 -44
  45. package/src/packages/utils/getRectAutoFormat.ts +9 -0
  46. package/src/packages/utils/notNull.ts +9 -0
  47. package/src/pages/index/app.scss +1 -1
  48. package/src/pages/index/components/normal/DemoCarousel.tsx +178 -73
  49. package/src/pages/index/components/normal/DemoKeepAlive.tsx +25 -25
  50. package/src/pages/index/components/normal/DemoSortList.tsx +70 -0
  51. package/src/pages/index/components/normal/DemoStackCard.tsx +356 -0
  52. package/src/pages/index/components/normal/DemoVirtualList.tsx +89 -3
  53. package/src/pages/index/components/service/DemoImagePreviewer.tsx +185 -0
  54. package/src/pages/index/home/AppHome.tsx +18 -3
  55. package/src/pages/index/home/menus.tsx +3 -1
  56. package/src/packages/components/CarouselGroup/carousel.scss +0 -143
  57. 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() ?