plain-design 1.0.0-beta.130 → 1.0.0-beta.132

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 (51) hide show
  1. package/dist/plain-design.commonjs.min.js +2 -2
  2. package/dist/plain-design.min.css +5 -4
  3. package/dist/plain-design.min.js +2 -2
  4. package/dist/report.html +2 -2
  5. package/package.json +2 -2
  6. package/src/packages/components/AutoTable/auto-table.scss +21 -0
  7. package/src/packages/components/AutoTable/filter/useTableOption.filter.state.ts +3 -2
  8. package/src/packages/components/AutoTable/use/useTableOption.column.popper.tsx +1 -1
  9. package/src/packages/components/AutoTable/use/useTableOption.fill.tsx +2 -2
  10. package/src/packages/components/AutoTable/use/useTableOption.hooks.tsx +4 -2
  11. package/src/packages/components/AutoTable/use/useTableOption.pagination.tsx +1 -1
  12. package/src/packages/components/AutoTable/use/useTableOption.table.tsx +5 -3
  13. package/src/packages/components/AutoTable/use/useTableOption.tips.tsx +2 -2
  14. package/src/packages/components/AutoTable/utils/TableOption.space.tsx +5 -2
  15. package/src/packages/components/Button/index.tsx +9 -2
  16. package/src/packages/components/Cascade/createSingleCascadeRender.tsx +1 -0
  17. package/src/packages/components/Dialog/index.tsx +6 -2
  18. package/src/packages/components/Dropdown/index.tsx +14 -1
  19. package/src/packages/components/Form/form.scss +4 -0
  20. package/src/packages/components/Form/layout/useFormLayout.tsx +1 -0
  21. package/src/packages/components/FormItem/index.tsx +7 -0
  22. package/src/packages/components/Input/useSuggestionInput.tsx +2 -1
  23. package/src/packages/components/Input/uses/useInputEnterHandler.tsx +9 -2
  24. package/src/packages/components/ListOption/index.tsx +20 -0
  25. package/src/packages/components/ListPanel/index.tsx +30 -0
  26. package/src/packages/components/ListPanel/list-panel.scss +20 -0
  27. package/src/packages/components/Scroll/index.tsx +1 -0
  28. package/src/packages/components/Select/createPublicSelectRender.tsx +4 -2
  29. package/src/packages/components/Select/decodeSelectRenderNode.tsx +4 -2
  30. package/src/packages/components/Select/index.tsx +13 -1
  31. package/src/packages/components/Select/select.utils.tsx +3 -2
  32. package/src/packages/components/Table/plc/use/useTableAutoSpan.tsx +112 -0
  33. package/src/packages/components/Table/plc/useTablePlc.tsx +2 -1
  34. package/src/packages/components/Table/plc/utils/plc.utils.ts +3 -0
  35. package/src/packages/components/Table/standard/PlcOperation/PlcOperation.tsx +3 -3
  36. package/src/packages/components/Table/table/Table.tsx +10 -6
  37. package/src/packages/components/Table/table/body/cell.tsx +34 -3
  38. package/src/packages/components/Table/table/body/useCellValue.tsx +14 -5
  39. package/src/packages/components/Table/table/head/head-cell.tsx +6 -0
  40. package/src/packages/components/Table/table/table.scss +4 -0
  41. package/src/packages/components/Table/table/use/useTableFormEditor.tsx +5 -1
  42. package/src/packages/components/Table/table/use/useTableModifyEditor.tsx +8 -2
  43. package/src/packages/components/Table/table/utils/createTableHooks.ts +5 -3
  44. package/src/packages/components/Table/table/utils/table.utils.ts +3 -2
  45. package/src/packages/components/VirtualTable/index.tsx +6 -0
  46. package/src/packages/components/VirtualTable/virtual-table.scss +3 -3
  47. package/src/packages/components/createSimpleDate/index.ts +49 -0
  48. package/src/packages/components/useContextmenuOptions/index.tsx +40 -0
  49. package/src/packages/components/usePopup/trigger/useManagerTrigger.clickOutside.tsx +18 -23
  50. package/src/packages/components/useTableFilter/index.ts +73 -0
  51. package/src/packages/entry.tsx +6 -0
@@ -0,0 +1,112 @@
1
+ import {computed, inject, provide} from "@peryl/react-compose";
2
+ import {Table} from "../../index";
3
+
4
+ export function useTableAutoSpan(getConfig: () => { data: Record<string, any>[] }) {
5
+
6
+ const table = Table.use.inject(null);
7
+
8
+ const spans = computed(() => {
9
+ const { data } = getConfig();
10
+ const bodyPlcList = table?.bodyPlcList.value;
11
+ if (!bodyPlcList) {return null;}
12
+ const rowSpanFields = Array.from(new Set(bodyPlcList.filter(i => i.props.autoRowSpan && !!i.props.field).map(i => i.props.field!)));
13
+ const colSpanFields = Array.from(new Set(bodyPlcList.filter(i => i.props.autoColSpan && !!i.props.field).map(i => i.props.field!)));
14
+ // console.log({ data, rowSpanFields, colSpanFields, });
15
+ const spans = calcSpans(data, { rowSpanFields, colSpanFields });
16
+ // console.log(spans);
17
+ return { spans, data };
18
+ });
19
+
20
+ provide('@@TABLE_AUTO_SPAN', spans);
21
+
22
+ return spans;
23
+ }
24
+
25
+ export function injectTableAutoSpan() {
26
+ return inject('@@TABLE_AUTO_SPAN') as ReturnType<typeof useTableAutoSpan>;
27
+ }
28
+
29
+ export const calcRowSpans = (data: Record<string, any>[], fields: string[]) => {
30
+
31
+ /*结果数组,每行数据都记录下每个字段的span值*/
32
+ const resultSpanMap: Record<string, number>[] = [];
33
+
34
+ /*遍历每一行数据的时候,记录下要合并的字段,如果字段值变了则记录下这条数据的rowIndex到prevIndex[field],也就是 prevIndex[field] = rowIndex*/
35
+ const prevIndex: any = fields.reduce((prev, field) => (prev[field] = 0, prev), {} as Record<string, number>);
36
+
37
+ /*遍历每一行数据时,先初始化一个rowSpanMap,以这个为原始模板*/
38
+ const templateRowMap = fields.reduce((prev, field) => (prev[field] = 1, prev), {} as Record<string, number>);
39
+
40
+ data.forEach((row, rowIndex) => {
41
+
42
+ const rowSpanMap = { ...templateRowMap };
43
+
44
+ /*第一条数据任何字段的span都不可能为0,从第二条数据开始,字段值相同的field才会设置span为0实现合并单元格的功能*/
45
+ if (rowIndex === 0) {
46
+ resultSpanMap.push(rowSpanMap);
47
+ return;
48
+ }
49
+
50
+ /*遍历每一个要合并的字段*/
51
+ fields.forEach((field, fieldIndex) => {
52
+ if (
53
+ /*字段值,与上一条数据字段的值相同,并且*/
54
+ row[field] === data[prevIndex[field]][field] && (
55
+ /*是第一个合并的字段,或者上一个合并的字段是合并状态*/
56
+ fieldIndex == 0 || (rowSpanMap[fields[fieldIndex - 1]] === 0)
57
+ )
58
+ ) {
59
+ /*当前字段应该合并*/
60
+ resultSpanMap[prevIndex[field]][field]++;
61
+ rowSpanMap[field] = 0;
62
+ } else {
63
+ /*字段值不一样,标记新的记录下这个新的rowIndex,后续合并字段都操作这个rowIndex来实现*/
64
+ prevIndex[field] = rowIndex;
65
+ }
66
+ });
67
+
68
+ resultSpanMap.push(rowSpanMap);
69
+ });
70
+
71
+ return data.map((_, rowIndex) => resultSpanMap[rowIndex]);
72
+ };
73
+
74
+ export const calcColSpans = (data: Record<string, any>[], fields: string[]) => {
75
+ const resultSpanMap: Record<string, any>[] = [];
76
+
77
+ data.forEach((row) => {
78
+ const colSpanMap: Record<string, number> = {};
79
+ let prevField: any = null;
80
+ fields.forEach((field, fieldIndex) => {
81
+
82
+ /*第一个字段的colSpan不可能为0*/
83
+ if (fieldIndex === 0) {
84
+ prevField = field;
85
+ colSpanMap[field] = 1;
86
+ return;
87
+ }
88
+
89
+ if (
90
+ /*当前字段的值与上一个字段的值相等,则当前字段应该合并*/
91
+ row[prevField] === row[field]
92
+ ) {
93
+ colSpanMap[field] = 0;
94
+ colSpanMap[prevField]++;
95
+ } else {
96
+ /*当前字段与上一个字段值不相等,则标记当前字段为新的起始位置*/
97
+ colSpanMap[field] = 1;
98
+ prevField = field;
99
+ }
100
+
101
+ });
102
+ resultSpanMap.push(colSpanMap);
103
+ });
104
+
105
+ return data.map((_, rowIndex) => resultSpanMap[rowIndex]);
106
+ };
107
+
108
+ export const calcSpans = (data: Record<string, any>[], config: { rowSpanFields?: string[], colSpanFields?: string[] }) => {
109
+ const rowSpans = !config.rowSpanFields?.length ? null : calcRowSpans(data, config.rowSpanFields);
110
+ const colSpans = !config.colSpanFields?.length ? null : calcColSpans(data, config.colSpanFields);
111
+ return data.map((_, rowIndex) => ({ row: rowSpans?.[rowIndex], col: colSpans?.[rowIndex] }));
112
+ };
@@ -47,7 +47,7 @@ export function useTablePlc(
47
47
  getPlcTypeArr: null as null | (() => tPlcType[]),
48
48
  });
49
49
 
50
- onBeforeUnmount(()=>{state.getTableEl = null})
50
+ onBeforeUnmount(() => {state.getTableEl = null;});
51
51
 
52
52
  hooks.onPlcTypes.use(list => {state.getPlcTypeArr = () => list;});
53
53
 
@@ -109,6 +109,7 @@ export function useTablePlc(
109
109
  const virtualFlatPlcList = (() => {
110
110
  const state = reactive({ scrollLeft: 0 });
111
111
  const setScrollLeft = throttle((scrollLeft: number) => {state.scrollLeft !== scrollLeft && (state.scrollLeft = scrollLeft);}, 50);
112
+ onBeforeUnmount(() => {setScrollLeft.dispose();});
112
113
  hooks.onScroll.use((e) => {setScrollLeft(e.target.scrollLeft);});
113
114
  return computed((): tPlc[] => {
114
115
  if (!plcData.value) {return [];}
@@ -36,6 +36,7 @@ export const PlcGroupPropsOptions = {
36
36
  noPadding: { type: Boolean }, // 是否不兼容表格的虚拟滚动功能
37
37
  colDraggable: { type: Boolean, default: null }, // 列是否可以拖拽排序
38
38
  link: { type: Boolean }, // 超链接样式
39
+ tip: { type: [String, Function] as PropType<string | (() => any)> }, // 提示说明
39
40
 
40
41
  hide: { type: Boolean }, // 是否隐藏
41
42
  order: { type: [String, Number] }, // 列排序
@@ -71,6 +72,8 @@ export const PlcPropsOptions = {
71
72
  hideInForm: { type: Boolean }, // 是否再表单编辑中显示
72
73
  formLabel: { type: String }, // 在表单编辑中的label文本
73
74
  overflowTooltip: { type: Boolean }, // 是否在内容文本溢出之后使用tooltip显示
75
+ autoRowSpan: { type: Boolean }, // 字段值相同则自动合并行
76
+ autoColSpan: { type: Boolean }, // 字段值相同则自动合并列
74
77
 
75
78
  // 编辑相关
76
79
  ...PlcValidatePropsOption, // 校验属性
@@ -222,12 +222,12 @@ export const PlcOperation = designComponent({
222
222
  }
223
223
  });
224
224
  const width = maxWidth + state.cellPaddingX * 2;
225
- console.warn({ width });
225
+ // console.warn({ width });
226
226
  if (!state.plcOperationWidth || width > state.plcOperationWidth) {
227
- console.log({
227
+ /*console.log({
228
228
  'plcOperationWidth.value': plcOperationWidth.value,
229
229
  'width + 4': width + 4,
230
- });
230
+ });*/
231
231
  if (width + 4 > plcOperationWidth.value) {
232
232
  state.plcOperationWidth = width + 4;
233
233
  }
@@ -245,14 +245,18 @@ export const Table = designComponent({
245
245
  ),
246
246
  });
247
247
 
248
- hooks.onClickCell.use(({ e, node }) => {
249
- event.emit.onClickCell(node, e);
250
- hooks.onClickRow.exec({ e, node });
248
+ hooks.onClickCell.use((val) => {
249
+ event.emit.onClickCell(val.node, val.e, val.plc);
250
+ hooks.onClickRow.exec({ e: val.e, node: val.node });
251
251
  });
252
252
 
253
- hooks.onDblclickCell.use(({ e, node }) => {
254
- event.emit.onDblclickCell(node, e);
255
- hooks.onDblclickRow.exec({ e, node });
253
+ hooks.onDblclickCell.use((val) => {
254
+ event.emit.onDblclickCell(val.node, val.e, val.plc);
255
+ hooks.onDblclickRow.exec({ e: val.e, node: val.node });
256
+ });
257
+
258
+ hooks.onContextmenuCell.use((val) => {
259
+ event.emit.onContextmenuCell(val.node, val.e, val.plc);
256
260
  });
257
261
 
258
262
  hooks.onClickHeadCell.use(({ e, plc }) => {!plc.props.disableAutoEmitClickTitle && event.emit.onClickHead(plc, e);});
@@ -10,6 +10,7 @@ import {useCellValue} from "./useCellValue";
10
10
  import {createTooltip} from "../../../useTooltip";
11
11
  import {createEffects} from "@peryl/utils/createEffects";
12
12
  import {usePopup} from "../../../usePopup";
13
+ import {injectTableAutoSpan} from "../../plc/use/useTableAutoSpan";
13
14
 
14
15
  export const PltCell = designComponent({
15
16
  name: 'plt-cell',
@@ -23,6 +24,9 @@ export const PltCell = designComponent({
23
24
  const { refs, onRef } = useRefs({ el: iHTMLElement });
24
25
 
25
26
  const popup = usePopup();
27
+
28
+ const tableAutoSpan = injectTableAutoSpan();
29
+
26
30
  /**
27
31
  * 单元格校验
28
32
  * @author 韦胜健
@@ -51,6 +55,11 @@ export const PltCell = designComponent({
51
55
  props.table.hooks.onDblclickCell.exec({ e, node: props.node, plc: props.plc });
52
56
  };
53
57
 
58
+ const onContextmenu = (e: iMouseEvent) => {
59
+ // if (bodyCell.value().editable) {return;}
60
+ props.table.hooks.onContextmenuCell.exec({ e, node: props.node, plc: props.plc });
61
+ };
62
+
54
63
  /**
55
64
  * 鼠标进入单元格
56
65
  * @author 韦胜健
@@ -70,7 +79,9 @@ export const PltCell = designComponent({
70
79
  * @author 韦胜健
71
80
  * @date 2022/12/5 20:35
72
81
  */
73
- const { bodyCell } = useCellValue({ optionGetter: () => ({ node: props.node, plc: props.plc }), formEdit: false });
82
+ const { effects: cellValueEffects } = createEffects();
83
+ onBeforeUnmount(() => cellValueEffects.clear());
84
+ const { bodyCell } = useCellValue({ optionGetter: () => ({ node: props.node, plc: props.plc }), formEdit: false, effects: cellValueEffects });
74
85
 
75
86
  /**
76
87
  * 单元格样式类名
@@ -143,7 +154,7 @@ export const PltCell = designComponent({
143
154
  * 则不显示overflowTooltip
144
155
  */
145
156
  if (!props.plc.props.overflowTooltip || !bodyCell || bodyCell.editable) {
146
- return emptyOverflowTooltipOption
157
+ return emptyOverflowTooltipOption;
147
158
  } else {
148
159
  return {
149
160
  reference: !props.plc.props.overflowTooltip || !bodyCell || bodyCell.editable ? null : refs.el,
@@ -182,14 +193,32 @@ export const PltCell = designComponent({
182
193
  return { update };
183
194
  })();
184
195
 
196
+ /*每次渲染都记录上一次的span,某次渲染在offsetData中找不到自己的offsetIndex时,就用上一次的span*/
197
+ let prevSpan = { rowspan: 1, colspan: 1 };
198
+
185
199
  return {
186
200
  render: () => {
187
201
  const { node, plc } = props;
188
202
  const { body, editable } = bodyCell.value();
189
- const span = !!props.table.props.spanMethod ? props.table.props.spanMethod({ node, plc }) : { rowspan: 1, colspan: 1 };
203
+ let span = !!props.table.props.spanMethod ? props.table.props.spanMethod({ node, plc }) : { rowspan: 1, colspan: 1 };
190
204
  if (plc.props.editColSpan != null && node.state.edit) {
191
205
  span.colspan = typeof plc.props.editColSpan === "function" ? plc.props.editColSpan(node) : plc.props.editColSpan;
192
206
  }
207
+ if (!!tableAutoSpan.value && (plc.props.autoRowSpan || plc.props.autoColSpan)) {
208
+ const { spans, data: offsetData } = tableAutoSpan.value;
209
+ /*要用offsetIndex,而不是props.node.state.index,这个是行数据在所有数据中的真实索引,而不是在虚拟列表数据中的索引*/
210
+ /*计算的spans仅含有虚拟数据的合并结果*/
211
+ const offsetIndex = offsetData.indexOf(props.node.data);
212
+ if (offsetIndex > -1) {
213
+ const rowSpan = !plc.props.field ? null : spans[offsetIndex].row?.[plc.props.field];
214
+ if (rowSpan != null) {span.rowspan = rowSpan;}
215
+ const colSpan = !plc.props.field ? null : spans[offsetIndex].col?.[plc.props.field];
216
+ if (colSpan != null) {span.colspan = colSpan;}
217
+ } else {
218
+ span = prevSpan;
219
+ }
220
+ }
221
+ prevSpan = span;
193
222
  if (span.rowspan === 0 || span.colspan === 0) {
194
223
  /*rowspan为0时,不会正确合并单元格,如果要合并单元格则不渲染这个td*/
195
224
  return null;
@@ -201,6 +230,7 @@ export const PltCell = designComponent({
201
230
  // console.log('cell', props.plc.props.title);
202
231
  return (
203
232
  <td
233
+ data-span={String(span.rowspan != 1 || span.colspan != 1)}
204
234
  ref={onRef.el}
205
235
  rowSpan={span.rowspan}
206
236
  colSpan={span.colspan}
@@ -208,6 +238,7 @@ export const PltCell = designComponent({
208
238
  style={styles.value}
209
239
  onClick={onClick}
210
240
  onDoubleClick={onDblclick}
241
+ onContextMenu={onContextmenu}
211
242
  onMouseEnter={onEnter}
212
243
  onMouseLeave={onLeave}
213
244
  >
@@ -1,11 +1,13 @@
1
1
  import {iTableNode} from "../utils/table.utils";
2
2
  import {tPlc} from "../../plc/utils/plc.utils";
3
- import {computed, reactive, toRaw} from "@peryl/react-compose";
3
+ import {globalComputed, reactive, toRaw} from "@peryl/react-compose";
4
4
  import {PlainObject} from "@peryl/utils/event";
5
5
  import {LoadingText} from "../../../../utils/LoadingText";
6
6
  import {AsyncFormatter} from "../../../AsyncFormatter";
7
7
  import {Formatter} from "../../../Formatter";
8
8
  import {renderBodyCell} from "../../plc/utils/plc.render";
9
+ import {iEffects} from "@peryl/utils/createEffects";
10
+ import {delay} from "@peryl/utils/delay";
9
11
 
10
12
  /**
11
13
  * 计算cell渲染所需要的内容
@@ -16,9 +18,11 @@ export function useCellValue(
16
18
  {
17
19
  optionGetter,
18
20
  formEdit,
21
+ effects,
19
22
  }: {
20
23
  optionGetter: () => { node: iTableNode, plc: tPlc },
21
24
  formEdit: boolean,
25
+ effects: iEffects,
22
26
  }
23
27
  ) {
24
28
  /**
@@ -26,20 +30,22 @@ export function useCellValue(
26
30
  * @author 韦胜健
27
31
  * @date 2022.12.3 10:50
28
32
  */
29
- const row = computed(() => {
33
+ const row = globalComputed(() => {
30
34
  const { node } = optionGetter();
31
35
  return node.state.edit ? node.state.editRow : node.data;
32
36
  });
37
+ effects.push(() => row.effect.stop());
33
38
 
34
39
  /**
35
40
  * 当前单元格的目标数据
36
41
  * @author 韦胜健
37
42
  * @date 2022.12.3 10:50
38
43
  */
39
- const sourceValue = computed(() => {
44
+ const sourceValue = globalComputed(() => {
40
45
  const { plc } = optionGetter();
41
46
  return !!plc.props.field ? row.value[plc.props.field] : null;
42
47
  });
48
+ effects.push(() => sourceValue.effect.stop());
43
49
 
44
50
  /**
45
51
  * 异步格式化的状态
@@ -58,8 +64,9 @@ export function useCellValue(
58
64
  loading: boolean,
59
65
  }
60
66
  });
67
+ effects.push(() => delay().then(() => {asyncState.asyncValue = null;}));
61
68
 
62
- const targetValue = computed(() => {
69
+ const targetValue = globalComputed(() => {
63
70
  const { plc, node } = optionGetter();
64
71
  const { formatter, asyncFormatter, formatterOptions } = plc.props;
65
72
  const cellRenderScope = { plc: plc, node: node, row: row.value };
@@ -125,12 +132,14 @@ export function useCellValue(
125
132
  return sourceValue.value;
126
133
  }
127
134
  });
135
+ effects.push(() => targetValue.effect.stop());
128
136
 
129
- const bodyCell = computed(() => {
137
+ const bodyCell = globalComputed(() => {
130
138
  const { node, plc } = optionGetter();
131
139
  const bc = renderBodyCell({ node, plc, formEdit, targetValue: targetValue.value });
132
140
  return () => bc;
133
141
  });
142
+ effects.push(() => bodyCell.effect.stop());
134
143
 
135
144
  return { bodyCell, targetValue };
136
145
  }
@@ -7,6 +7,8 @@ import {PlainScroll} from "../../../Scroll";
7
7
  import {Icon} from "../../../Icon";
8
8
  import {renderHeadCell} from "../../plc/utils/plc.render";
9
9
  import {useTooltip} from "../../../useTooltip";
10
+ import Tooltip from "../../../Tooltip";
11
+ import {getFunctionValue} from '@peryl/utils/getFunctionValue';
10
12
 
11
13
  export const PltHeadCell = designComponent({
12
14
  name: 'plt-head-cell',
@@ -77,6 +79,10 @@ export const PltHeadCell = designComponent({
77
79
  onMouseLeave={handler.onLeave}
78
80
  >
79
81
  {content}
82
+ {!!props.tablePlc.props.tip && <Tooltip v-slots={{
83
+ default: () => <Icon icon="pi-info-circle" className="plt-head-cell-tip"/>,
84
+ popper: () => getFunctionValue(props.tablePlc.props.tip)
85
+ }}/>}
80
86
  <span className="plt-head-cell-indicator" onMouseDown={resizeHandler.mousedown}/>
81
87
  {sort.value != null && (
82
88
  <div className={`plt-head-cell-sorter plt-head-cell-sorter-${sort.value ? 'desc' : 'asc'}`}>
@@ -24,6 +24,10 @@
24
24
  font-weight: 500;
25
25
  letter-spacing: 1px;
26
26
 
27
+ .plt-head-cell-tip {
28
+ color: plv(text-3);
29
+ }
30
+
27
31
  .plt-head-cell-indicator {
28
32
  position: absolute;
29
33
  top: 0;
@@ -53,10 +53,13 @@ export function useTableFormEditor(
53
53
  * @author 韦胜健
54
54
  * @date 2022/12/5 20:45
55
55
  */
56
+ const { effects: cellBodyArrayEffects } = createEffects();
57
+ effects.push(() => cellBodyArrayEffects.clear());
56
58
  const cellBodyArray = computed(() => {
59
+ cellBodyArrayEffects.clear();
57
60
  if (!state.optionGetter) {return [];}
58
61
  const { node } = state.optionGetter();
59
- return showPlcArray.value.map(plc => useCellValue({ optionGetter: () => ({ plc, node }), formEdit: true }));
62
+ return showPlcArray.value.map(plc => useCellValue({ optionGetter: () => ({ plc, node }), formEdit: true, effects: cellBodyArrayEffects }));
60
63
  });
61
64
 
62
65
  const handler = {
@@ -136,6 +139,7 @@ export function useTableFormEditor(
136
139
  required={plc.props.required}
137
140
  rules={plc.props.rules}
138
141
  validator={plc.props.validator}
142
+ tip={plc.props.tip}
139
143
  disabled={!editable}
140
144
  {...validateAttrs}
141
145
  >
@@ -3,7 +3,7 @@ import {iTableHooks} from "../utils/createTableHooks";
3
3
  import {PlcValidatePropsOption, tPlc} from "../../plc/utils/plc.utils";
4
4
  import {createEffects} from "@peryl/utils/createEffects";
5
5
  import {PlainObject} from "@peryl/utils/event";
6
- import {computed, reactive, RenderNode, useRefs} from "@peryl/react-compose";
6
+ import {computed, onBeforeUnmount, reactive, RenderNode, useRefs} from "@peryl/react-compose";
7
7
  import {Dialog} from "../../../Dialog";
8
8
  import {delay} from "@peryl/utils/delay";
9
9
  import {Table} from "../Table";
@@ -64,11 +64,13 @@ export function useTableModifyEditor(
64
64
  * @author 韦胜健
65
65
  * @date 2022/12/5 20:45
66
66
  */
67
+ const { effects: cellBodyArrayEffects } = createEffects();
68
+ effects.push(() => cellBodyArrayEffects.clear());
67
69
  const cellBodyArray = computed(() => {
68
70
  if (!state.optionGetter) {return [];}
69
71
  const { node } = state;
70
72
  if (!node) {return [];}
71
- return availablePlcList.value.map(i => useCellValue({ optionGetter: () => ({ plc: i.getPlc(), node }), formEdit: true }));
73
+ return availablePlcList.value.map(i => useCellValue({ optionGetter: () => ({ plc: i.getPlc(), node }), formEdit: true, effects: cellBodyArrayEffects }));
72
74
  });
73
75
 
74
76
  const handler = {
@@ -124,6 +126,7 @@ export function useTableModifyEditor(
124
126
  required={plc.props.required}
125
127
  rules={plc.props.rules}
126
128
  validator={plc.props.validator}
129
+ tip={plc.props.tip}
127
130
  disabled={!editable}
128
131
  {...validateAttrs}
129
132
  >
@@ -142,6 +145,7 @@ export function useTableModifyEditor(
142
145
  initialize: (() => {
143
146
  let hasInitialized = false;
144
147
  return () => {
148
+ /*已经初始化过就不用再初始化了*/
145
149
  if (hasInitialized) {return;}
146
150
  hasInitialized = true;
147
151
  effects.push(hooks.onRender.use(
@@ -187,6 +191,8 @@ export function useTableModifyEditor(
187
191
  },
188
192
  };
189
193
 
194
+ onBeforeUnmount(() => effects.clear());
195
+
190
196
  return { methods, state };
191
197
  }
192
198
 
@@ -1,5 +1,5 @@
1
1
  import {createHooks, createRenderHook, createSyncHooks, iMouseEvent, RenderNode, StyleProperties} from "@peryl/react-compose";
2
- import {tPlcType} from "../../plc/utils/plc.utils";
2
+ import {tPlc, tPlcType} from "../../plc/utils/plc.utils";
3
3
  import {iTableMessyData, iTableNode} from "./table.utils";
4
4
  import {PlainObject} from "@peryl/utils/event";
5
5
  import {VirtualTable} from "../../../VirtualTable";
@@ -25,9 +25,11 @@ export const createTableHooks = () => {
25
25
  /*双击行动作*/
26
26
  onDblclickRow: createHooks<(data: { e: iMouseEvent, node: iTableNode }) => void>(),
27
27
  /*单击单元格动作*/
28
- onClickCell: createHooks<(data: { e: iMouseEvent, node: iTableNode, plc: tPlcType }) => void>(),
28
+ onClickCell: createHooks<(data: { e: iMouseEvent, node: iTableNode, plc: tPlc }) => void>(),
29
29
  /*双击单元格动作*/
30
- onDblclickCell: createHooks<(data: { e: iMouseEvent, node: iTableNode, plc: tPlcType }) => void>(),
30
+ onDblclickCell: createHooks<(data: { e: iMouseEvent, node: iTableNode, plc: tPlc }) => void>(),
31
+ /*右击单元格动作*/
32
+ onContextmenuCell: createHooks<(data: { e: iMouseEvent, node: iTableNode, plc: tPlc }) => void>(),
31
33
  /*鼠标进入单元格*/
32
34
  onEnterCell: createHooks<(data: { e: iMouseEvent, node: iTableNode, plc: tPlcType }) => void>(),
33
35
  /*鼠标离开单元格*/
@@ -93,8 +93,9 @@ export const TableEmitsOptions = {
93
93
 
94
94
  onClickRow: (node: iTableNode, e: iMouseEvent) => true,
95
95
  onDblclickRow: (node: iTableNode, e: iMouseEvent) => true,
96
- onClickCell: (node: iTableNode, e: iMouseEvent) => true,
97
- onDblclickCell: (node: iTableNode, e: iMouseEvent) => true,
96
+ onClickCell: (node: iTableNode, e: iMouseEvent, plc: tPlc) => true,
97
+ onDblclickCell: (node: iTableNode, e: iMouseEvent, plc: tPlc) => true,
98
+ onContextmenuCell: (node: iTableNode, e: iMouseEvent, plc: tPlc) => true,
98
99
  onClickHead: (plc: tPlcType, e: iMouseEvent) => true,
99
100
 
100
101
  onConfigPlc: (data: { plcList: tPlcType[], stateData: tPlcCacheStateData }) => true,
@@ -7,6 +7,8 @@ import './virtual-table.scss';
7
7
  import i18n from "../i18n";
8
8
  import {doNothing} from "@peryl/utils/doNothing";
9
9
  import {useWatchAutoClear} from "../../utils/watchEffectAutoClear";
10
+ import {useTableAutoSpan} from "../Table/plc/use/useTableAutoSpan";
11
+ import {iTableNode} from "../Table/table/utils/table.utils";
10
12
 
11
13
  export const VirtualTable = designComponent({
12
14
  name: 'virtual-table',
@@ -55,6 +57,8 @@ export const VirtualTable = designComponent({
55
57
  /*虚拟滚动时让出顶部距离显示表头*/
56
58
  const { virtual } = useVirtualList({ dataModel, props, refs: refs as any, emit, transform: false });
57
59
 
60
+ const tableAutoSpan = useTableAutoSpan(() => ({ data: virtual.offsetData.value.list.map(i => (i.item as iTableNode).data) }));
61
+
58
62
  /*---------------------------------------computed-------------------------------------------*/
59
63
  const styles = useStyles(style => {
60
64
  style.height = unit(props.height);
@@ -154,6 +158,8 @@ export const VirtualTable = designComponent({
154
158
  },
155
159
  render: () => {
156
160
  const { list } = virtual.offsetData.value;
161
+ /*触发响应式计算*/
162
+ doNothing(tableAutoSpan.value?.spans);
157
163
  return (
158
164
  <div style={styles.value} className={classes.value}>
159
165
  <Scroll
@@ -81,19 +81,19 @@ $scrollbarSize: 12px;
81
81
  }
82
82
 
83
83
  &:hover {
84
- td {
84
+ td:not([data-span=true]) {
85
85
  background-color: plv(table-row-hover-color);
86
86
  }
87
87
  }
88
88
 
89
89
  &.plt-row-current {
90
- td {
90
+ td:not([data-span=true]) {
91
91
  background-color: plv(table-row-current-color);
92
92
  }
93
93
  }
94
94
 
95
95
  &.plt-row-checked {
96
- td {
96
+ td:not([data-span=true]) {
97
97
  background-color: plv(primary-light-1);
98
98
  border-bottom-color: plv(primary-light-2);
99
99
  }
@@ -0,0 +1,49 @@
1
+ export function createSimpleDate(_date?: Date) {
2
+ const date = _date || new Date();
3
+ const year = date.getFullYear();
4
+ const month = date.getMonth() + 1;
5
+ const day = date.getDate();
6
+ const hour = date.getHours();
7
+ const minute = date.getMinutes();
8
+ const second = date.getSeconds();
9
+
10
+ const YYYY = String(year);
11
+ const MM = String(month).padStart(2, '0');
12
+ const DD = String(day).padStart(2, '0');
13
+ const HH = String(hour).padStart(2, '0');
14
+ const mm = String(minute).padStart(2, '0');
15
+ const ss = String(second).padStart(2, '0');
16
+
17
+ return {
18
+ 'YYYY-MM-DD HH:mm:ss': `${YYYY}-${MM}-${DD} ${HH}:${mm}:${ss}`,
19
+ 'YYYY-MM-DD': `${YYYY}-${MM}-${DD}`,
20
+ 'HH:mm:ss': `${HH}:${mm}:${ss}`,
21
+ YMDHms: Number(`${YYYY}${MM}${DD}${HH}${mm}${ss}`),
22
+ YMD: Number(`${YYYY}${MM}${DD}`),
23
+ };
24
+ }
25
+
26
+ export const parseDateString = (dateString: string): Date | null => {
27
+ const match = /(\d\d\d\d)-(\d\d)-(\d\d)/.exec(dateString);
28
+ if (!match) {return null;}
29
+ const year = Number(match[1]);
30
+ const month = Number(match[2]);
31
+ const date = Number(match[3]);
32
+ return new Date(year, month - 1, date);
33
+ };
34
+
35
+ export const parseDatetimeString = (datetimeString: string) => {
36
+ const match = /(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)/.exec(datetimeString);
37
+ if (!match) {return null;}
38
+ const year = Number(match[1]);
39
+ const month = Number(match[2]);
40
+ const date = Number(match[3]);
41
+ const hour = Number(match[4]);
42
+ const minute = Number(match[5]);
43
+ const second = Number(match[6]);
44
+ return new Date(year, month - 1, date, hour, minute, second);
45
+ };
46
+
47
+ export const nowDate = () => createSimpleDate()['YYYY-MM-DD'];
48
+ export const nowTime = () => createSimpleDate()['HH:mm:ss'];
49
+ export const nowDatetime = () => createSimpleDate()['YYYY-MM-DD HH:mm:ss'];
@@ -0,0 +1,40 @@
1
+ import {usePopup} from "../usePopup";
2
+ import {ListPanel} from "../ListPanel";
3
+ import {ListOption} from "../ListOption";
4
+ import {iMouseEvent} from "@peryl/react-compose";
5
+ import {getFunctionValue} from "@peryl/utils/getFunctionValue";
6
+
7
+ export const useContextmenuOptions = () => {
8
+
9
+ const { $popup } = usePopup();
10
+
11
+ const open = ({ x, y ,options}: { x: number, y: number, options: iContextmenuOption[]}) => {
12
+ const pop = $popup.absolute({
13
+ x, y,
14
+ noArrow: true,
15
+ noPadding: true,
16
+ type: 'contextmenu-option',
17
+ placement: 'bottom-start',
18
+ trigger: 'contextmenu',
19
+ render: () => (
20
+ <ListPanel>
21
+ {options.map((option, index) => (
22
+ <ListOption key={index} label="管理该类型" onClick={async (e) => {
23
+ const flag = await option.handler(e);
24
+ if (flag != false) {pop.hide();}
25
+ }}>
26
+ {getFunctionValue(option.label)}
27
+ </ListOption>
28
+ ))}
29
+ </ListPanel>
30
+ )
31
+ });
32
+ };
33
+
34
+ return { open };
35
+ };
36
+
37
+ export interface iContextmenuOption {
38
+ label: string | (() => any);
39
+ handler: (e: iMouseEvent) => void | boolean | Promise<void | boolean>,
40
+ }