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.
- package/dist/plain-design.commonjs.min.js +2 -2
- package/dist/plain-design.min.css +5 -4
- package/dist/plain-design.min.js +2 -2
- package/dist/report.html +2 -2
- package/package.json +2 -2
- package/src/packages/components/AutoTable/auto-table.scss +21 -0
- package/src/packages/components/AutoTable/filter/useTableOption.filter.state.ts +3 -2
- package/src/packages/components/AutoTable/use/useTableOption.column.popper.tsx +1 -1
- package/src/packages/components/AutoTable/use/useTableOption.fill.tsx +2 -2
- package/src/packages/components/AutoTable/use/useTableOption.hooks.tsx +4 -2
- package/src/packages/components/AutoTable/use/useTableOption.pagination.tsx +1 -1
- package/src/packages/components/AutoTable/use/useTableOption.table.tsx +5 -3
- package/src/packages/components/AutoTable/use/useTableOption.tips.tsx +2 -2
- package/src/packages/components/AutoTable/utils/TableOption.space.tsx +5 -2
- package/src/packages/components/Button/index.tsx +9 -2
- package/src/packages/components/Cascade/createSingleCascadeRender.tsx +1 -0
- package/src/packages/components/Dialog/index.tsx +6 -2
- package/src/packages/components/Dropdown/index.tsx +14 -1
- package/src/packages/components/Form/form.scss +4 -0
- package/src/packages/components/Form/layout/useFormLayout.tsx +1 -0
- package/src/packages/components/FormItem/index.tsx +7 -0
- package/src/packages/components/Input/useSuggestionInput.tsx +2 -1
- package/src/packages/components/Input/uses/useInputEnterHandler.tsx +9 -2
- package/src/packages/components/ListOption/index.tsx +20 -0
- package/src/packages/components/ListPanel/index.tsx +30 -0
- package/src/packages/components/ListPanel/list-panel.scss +20 -0
- package/src/packages/components/Scroll/index.tsx +1 -0
- package/src/packages/components/Select/createPublicSelectRender.tsx +4 -2
- package/src/packages/components/Select/decodeSelectRenderNode.tsx +4 -2
- package/src/packages/components/Select/index.tsx +13 -1
- package/src/packages/components/Select/select.utils.tsx +3 -2
- package/src/packages/components/Table/plc/use/useTableAutoSpan.tsx +112 -0
- package/src/packages/components/Table/plc/useTablePlc.tsx +2 -1
- package/src/packages/components/Table/plc/utils/plc.utils.ts +3 -0
- package/src/packages/components/Table/standard/PlcOperation/PlcOperation.tsx +3 -3
- package/src/packages/components/Table/table/Table.tsx +10 -6
- package/src/packages/components/Table/table/body/cell.tsx +34 -3
- package/src/packages/components/Table/table/body/useCellValue.tsx +14 -5
- package/src/packages/components/Table/table/head/head-cell.tsx +6 -0
- package/src/packages/components/Table/table/table.scss +4 -0
- package/src/packages/components/Table/table/use/useTableFormEditor.tsx +5 -1
- package/src/packages/components/Table/table/use/useTableModifyEditor.tsx +8 -2
- package/src/packages/components/Table/table/utils/createTableHooks.ts +5 -3
- package/src/packages/components/Table/table/utils/table.utils.ts +3 -2
- package/src/packages/components/VirtualTable/index.tsx +6 -0
- package/src/packages/components/VirtualTable/virtual-table.scss +3 -3
- package/src/packages/components/createSimpleDate/index.ts +49 -0
- package/src/packages/components/useContextmenuOptions/index.tsx +40 -0
- package/src/packages/components/usePopup/trigger/useManagerTrigger.clickOutside.tsx +18 -23
- package/src/packages/components/useTableFilter/index.ts +73 -0
- 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((
|
|
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((
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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'}`}>
|
|
@@ -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:
|
|
28
|
+
onClickCell: createHooks<(data: { e: iMouseEvent, node: iTableNode, plc: tPlc }) => void>(),
|
|
29
29
|
/*双击单元格动作*/
|
|
30
|
-
onDblclickCell: createHooks<(data: { e: iMouseEvent, node: iTableNode, plc:
|
|
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
|
+
}
|