@enableai-base/table 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4292 @@
1
+ /**
2
+ * @enableai-base/table v1.0.0
3
+ * (c) 2024-present Enableai
4
+ * @license Proprietary
5
+ **/
6
+ import React, { useRef, useImperativeHandle, useMemo, useState, useCallback, useEffect } from 'react';
7
+ import { createContextWithProvider } from '@enableai-sdk-common/react-hooks';
8
+ import { DEFAULT_FONT_SIZE, HEADER_FONT_SIZE, COLOR_BG_CELL, BORDER_LINE_WIDTH, BORDER_COLOR, COLOR_BORDER_SELECTION, CUSTOM_STYLE_LIST, COLOR_BG_CELL_HOVER, CUSTOM_CELL_STYLE_FEEDBACK_ERROR, COLOR_LINK, CUSTOM_CELL_STYLE_HIGHLIGHT_FOCUS, TOOL_ICON, TABLE_DEFAULT_ROW_HEIGHT, CUSTOM_CELL_STYLE, CUSTOM_CELL_FOCUS_STYLE, CUSTOM_CELL_FEEDBACK_ERROR_STYLE, COLOR_ICON, formatValue, getColorByStr, formatDuration } from '@enableai-base/core';
9
+ import { isMatch, mergeWith, castArray, isNil, clone, clamp, max, min, cloneDeep } from 'lodash';
10
+ import { createGroup, createImage, createText, VGroup, VText, createRect } from '@visactor/vtable/es/vrender';
11
+ import { getTextColorForBackground } from '@enableai-sdk-common/color';
12
+ import { getUUID } from '@enableai-sdk-common/id';
13
+ import { createAtomStore } from '@enableai-sdk-common/store';
14
+ import { atom } from 'jotai';
15
+ import { Command, getCommandManager, startTransaction, stopTransaction } from '@enableai-sdk-common/command';
16
+ import { EnableAIBaseFontIcon } from '@enableai-fonts/font-icon-enableai-base';
17
+ import dayjs from 'dayjs';
18
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
19
+ import timezone from 'dayjs/plugin/timezone';
20
+ import utc from 'dayjs/plugin/utc';
21
+ import { Popover, DatePicker, Form, Input, Flex, Space, Button, InputNumber, Dropdown } from 'antd';
22
+ import { JsonEditor, TableMenuModal } from '@enableai-base/grid-layout';
23
+ import { LinkOutlined } from '@ant-design/icons';
24
+ import { Select, AvatarPopoverContent } from '@enableai-base/ui';
25
+ import { useHotkeys } from 'react-hotkeys-hook';
26
+ import { DestroyClass } from '@enableai-sdk-common/core';
27
+ import * as VTable from '@visactor/vtable';
28
+ import { ListTable } from '@visactor/vtable';
29
+ import * as VTable_editors from '@visactor/vtable-editors';
30
+ import { ValidateEnum } from '@visactor/vtable-editors';
31
+
32
+ const theme = {
33
+ selectionStyle: {
34
+ cellBorderColor: COLOR_BORDER_SELECTION,
35
+ cellBgColor: "rgba(255,255,255, 0.1)"
36
+ },
37
+ frameStyle: {
38
+ borderLineWidth: BORDER_LINE_WIDTH,
39
+ // 全局边框宽度
40
+ borderColor: BORDER_COLOR
41
+ // 全局边框颜色 最外面的方框
42
+ },
43
+ cellInnerBorder: true,
44
+ // 禁用内边框隐藏逻辑
45
+ defaultStyle: {
46
+ fontSize: DEFAULT_FONT_SIZE,
47
+ borderColor: BORDER_COLOR,
48
+ borderLineWidth: BORDER_LINE_WIDTH,
49
+ bgColor: COLOR_BG_CELL,
50
+ hover: {
51
+ // CustomCellStyle 设置的样式优先级更高
52
+ // TODO 感觉会变卡,后续优化,将样式设置属性放在 item 数据里面驱动控制
53
+ cellBgColor: ({ table, row, col }) => {
54
+ const cellStyle = table.getCellStyle(col, row);
55
+ const matched = CUSTOM_STYLE_LIST.filter((item) => isMatch(cellStyle, { bgColor: item.bgColor }));
56
+ if (matched.length) return cellStyle.bgColor;
57
+ return COLOR_BG_CELL_HOVER;
58
+ },
59
+ inlineRowBgColor: ({ table, row, col }) => {
60
+ const cellStyle = table.getCellStyle(col, row);
61
+ const matched = CUSTOM_STYLE_LIST.filter((item) => isMatch(cellStyle, { bgColor: item.bgColor }));
62
+ if (matched.length) return cellStyle.bgColor;
63
+ return COLOR_BG_CELL_HOVER;
64
+ }
65
+ }
66
+ },
67
+ headerStyle: {
68
+ fontSize: HEADER_FONT_SIZE
69
+ },
70
+ bodyStyle: {
71
+ fontSize: DEFAULT_FONT_SIZE
72
+ },
73
+ frozenColumnLine: {
74
+ shadow: {
75
+ width: 4,
76
+ startColor: "rgba(00, 24, 47, 0.05)",
77
+ endColor: "rgba(00, 24, 47, 0.03)",
78
+ visible: "always"
79
+ }
80
+ }
81
+ };
82
+
83
+ const { createUseContext, Provider: VTableProvider } = createContextWithProvider(
84
+ ({
85
+ id,
86
+ columns,
87
+ initialValues,
88
+ onChange,
89
+ ref,
90
+ theme: theme$1,
91
+ disabled = false,
92
+ debug = false,
93
+ cellMenus,
94
+ headerMenus,
95
+ moreMenus,
96
+ commandManager,
97
+ uploadFileFn,
98
+ innerColumnControl = {
99
+ checkbox: true,
100
+ main: true,
101
+ plus: true
102
+ },
103
+ ...rest
104
+ }) => {
105
+ const containerRef = useRef(null);
106
+ const editorRef = useRef(null);
107
+ useImperativeHandle(ref, () => {
108
+ return {
109
+ getEditor: () => editorRef.current
110
+ };
111
+ });
112
+ return {
113
+ id,
114
+ editorRef,
115
+ theme: theme$1 ?? theme,
116
+ columns,
117
+ records: initialValues,
118
+ containerRef,
119
+ disabled,
120
+ cellMenus,
121
+ headerMenus,
122
+ moreMenus,
123
+ onChange,
124
+ commandManager,
125
+ uploadFileFn,
126
+ innerColumnControl,
127
+ debug,
128
+ ...rest
129
+ };
130
+ }
131
+ );
132
+ const useComponentVTable = createUseContext();
133
+
134
+ const INNER_TABLE_COLUMN_KEY_PLUS = "INNER_TABLE_COLUMN_KEY_PLUS";
135
+ const INNER_TABLE_COLUMN_KEY_CHECKBOX = "INNER_TABLE_COLUMN_KEY_CHECKBOX";
136
+ const INNER_TABLE_COLUMN_KEY_LIST = [INNER_TABLE_COLUMN_KEY_PLUS, INNER_TABLE_COLUMN_KEY_CHECKBOX];
137
+ const INNER_TABLE_RECORD_ID_KEY = "INNER_PRESET_RECORD_ID";
138
+
139
+ const highlightMerge = (obj1, obj2) => {
140
+ return mergeWith({}, obj1, obj2, (objValue, srcValue) => {
141
+ if (Array.isArray(objValue)) {
142
+ return objValue.concat(srcValue);
143
+ }
144
+ });
145
+ };
146
+ const getColFromColIndex = (colIndex, innerColumnControl) => {
147
+ const hasCheckbox = innerColumnControl.checkbox;
148
+ return colIndex + (hasCheckbox ? 1 : 0);
149
+ };
150
+ const adaptedCellPosition = (col, row, innerColumnControl) => ({
151
+ col: getColFromColIndex(col, innerColumnControl),
152
+ row: getRowFromRowIndex(row)
153
+ });
154
+ const getRowFromRowIndex = (rowIndex) => rowIndex + 1;
155
+
156
+ function isValid(value) {
157
+ return value !== null && value !== void 0;
158
+ }
159
+ function getIconSizeFromFont(fontSize) {
160
+ return fontSize + 2;
161
+ }
162
+ function getFunctionalProp(name, cellStyle, col, row, _table) {
163
+ const prop = cellStyle && isValid(cellStyle[name]) ? cellStyle[name] : void 0;
164
+ if (typeof prop === "function") {
165
+ const arg = {
166
+ col,
167
+ row,
168
+ table: _table,
169
+ value: _table.getCellValue(col, row),
170
+ dataValue: _table.getCellOriginValue(col, row),
171
+ cellHeaderPaths: _table.getCellHeaderPaths(col, row)
172
+ };
173
+ return prop(arg);
174
+ }
175
+ return void 0;
176
+ }
177
+ function getProp(name, cellStyle, col, row, _table) {
178
+ const prop = cellStyle && isValid(cellStyle[name]) ? cellStyle[name] : void 0;
179
+ if (typeof prop === "function") {
180
+ const arg = {
181
+ col,
182
+ row,
183
+ table: _table,
184
+ value: _table.getCellValue(col, row),
185
+ dataValue: _table.getCellOriginValue(col, row),
186
+ cellHeaderPaths: _table.getCellHeaderPaths(col, row)
187
+ };
188
+ return prop(arg);
189
+ }
190
+ return prop ?? DEFAULT_FONT_SIZE;
191
+ }
192
+
193
+ const resolvePaddingLR = (padding) => {
194
+ if (typeof padding === "number") {
195
+ return [padding, padding];
196
+ } else {
197
+ if (padding.length === 2) {
198
+ return [padding[1], padding[1]];
199
+ } else if (padding.length === 4) {
200
+ return [padding[1], padding[3]];
201
+ }
202
+ }
203
+ throw new Error("padding \u683C\u5F0F\u9519\u8BEF");
204
+ };
205
+
206
+ function getAttribute(graphics) {
207
+ if ("attribute" in graphics) {
208
+ return graphics.attribute;
209
+ } else {
210
+ return graphics.props.attribute;
211
+ }
212
+ }
213
+ const createGraphicsWithLoading = (graphics, loading) => {
214
+ const attribute = getAttribute(graphics);
215
+ if (loading) {
216
+ const group = createGroup({
217
+ width: attribute.width,
218
+ height: attribute.height,
219
+ display: "flex",
220
+ alignItems: "center",
221
+ justifyContent: "center",
222
+ alignContent: "center"
223
+ });
224
+ const loadingImage = createImage({
225
+ width: 20,
226
+ height: 20,
227
+ image: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTAiIGhlaWdodD0iNTAiIGZpbGw9IiNjYWNhY2EiIHZpZXdCb3g9IjAgMCAxMDI0IDEwMjQiIGZvY3VzYWJsZT0iZmFsc2UiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTk4OCA1NDhjLTE5LjkgMC0zNi0xNi4xLTM2LTM2IDAtNTkuNC0xMS42LTExNy0zNC42LTE3MS4zYTQ0MC40NSA0NDAuNDUgMCAwMC05NC4zLTEzOS45IDQzNy43MSA0MzcuNzEgMCAwMC0xMzkuOS05NC4zQzYyOSA4My42IDU3MS40IDcyIDUxMiA3MmMtMTkuOSAwLTM2LTE2LjEtMzYtMzZzMTYuMS0zNiAzNi0zNmM2OS4xIDAgMTM2LjIgMTMuNSAxOTkuMyA0MC4zQzc3Mi4zIDY2IDgyNyAxMDMgODc0IDE1MGM0NyA0NyA4My45IDEwMS44IDEwOS43IDE2Mi43IDI2LjcgNjMuMSA0MC4yIDEzMC4yIDQwLjIgMTk5LjMuMSAxOS45LTE2IDM2LTM1LjkgMzZ6IiAvPjwvc3ZnPg==",
228
+ anchor: [attribute.width / 2, attribute.height / 2],
229
+ cornerRadius: 10
230
+ });
231
+ loadingImage.animate().to(
232
+ {
233
+ angle: Math.PI * 2
234
+ },
235
+ 1e3,
236
+ "linear"
237
+ ).loop(Infinity);
238
+ group.add(loadingImage);
239
+ return group;
240
+ } else {
241
+ return graphics;
242
+ }
243
+ };
244
+
245
+ const createTextGraphics = (props) => {
246
+ const { table, row, col, rect } = props;
247
+ const { height, width } = rect ?? table.getCellRect(col, row);
248
+ const value = props.value?.value ?? "";
249
+ const loading = !!props.value?.loading;
250
+ const cellStyle = table._getCellStyle(col, row);
251
+ const functionalFontSize = getProp("fontSize", cellStyle, col, row, table);
252
+ const functionalColor = getProp("color", cellStyle, col, row, table);
253
+ const parsedColor = props.color ?? functionalColor;
254
+ const padding = getProp("padding", cellStyle, col, row, table);
255
+ const paddingLR = resolvePaddingLR(padding);
256
+ const group = createGroup({
257
+ width,
258
+ height,
259
+ display: "flex",
260
+ flexWrap: "nowrap",
261
+ justifyContent: "space-around",
262
+ alignContent: "center",
263
+ alignItems: "center"
264
+ // 垂直居中
265
+ });
266
+ const text = createText({
267
+ fontSize: functionalFontSize,
268
+ fontFamily: "sans-serif",
269
+ text: value,
270
+ fill: parsedColor,
271
+ cursor: "pointer",
272
+ maxLineWidth: width - paddingLR[0] - paddingLR[1],
273
+ boundsPadding: padding
274
+ });
275
+ group.add(text);
276
+ return createGraphicsWithLoading(group, loading);
277
+ };
278
+
279
+ const createJsonGraphics = (props) => {
280
+ const value = props.value?.value ?? "";
281
+ let parsedValue = "";
282
+ try {
283
+ parsedValue = JSON.stringify(JSON.parse(value));
284
+ return createTextGraphics({
285
+ ...props,
286
+ value: {
287
+ ...props.value,
288
+ value: parsedValue
289
+ }
290
+ });
291
+ } catch {
292
+ return createTextGraphics({
293
+ ...props,
294
+ color: "red",
295
+ value: {
296
+ ...props.value,
297
+ value: "JSON Error"
298
+ }
299
+ });
300
+ }
301
+ };
302
+
303
+ const VTableEditorType = {
304
+ "TEXT_AREA": "textAreaEditor",
305
+ "INPUT": "inputEditor",
306
+ "DATE": "dateInputEditor",
307
+ "MULTI_SELECT": "multiSelectEditor",
308
+ "SINGLE_SELECT": "singleSelectEditor",
309
+ "LINK": "linkEditor",
310
+ "NUMBER": "numberEditor",
311
+ "TEXT": "textEditor",
312
+ "JSON": "jsonEditor"
313
+ };
314
+
315
+ const toolTypeToEditorMap = /* @__PURE__ */ new Map([
316
+ [29, "singleSelectEditor"],
317
+ [30, "multiSelectEditor"],
318
+ [36, "dateInputEditor"],
319
+ [33, "linkEditor"],
320
+ [34, "jsonEditor"],
321
+ [31, "numberEditor"],
322
+ [37, "numberEditor"],
323
+ // 多个 key 映射到同一个 value
324
+ [28, "textEditor"],
325
+ [35, "textEditor"]
326
+ ]);
327
+ const getEditorByToolType = (toolType) => {
328
+ return toolTypeToEditorMap.get(toolType) ?? void 0;
329
+ };
330
+
331
+ const baseCreator = (data, _innerColumnControl) => {
332
+ const { id, label, lock, width, type, options } = data;
333
+ const minWidth = 100;
334
+ const baseColumn = {
335
+ field: id,
336
+ title: label,
337
+ lock,
338
+ width: Math.max(minWidth, width ?? 0),
339
+ minWidth,
340
+ tool: type,
341
+ fmt: data.default?.format,
342
+ options,
343
+ request: data.request,
344
+ editor: getEditorByToolType(type),
345
+ // 默认表头渲染函数
346
+ headerCustomLayout: (args) => {
347
+ return {
348
+ rootContainer: createHeaderGraphics(type, args),
349
+ renderDefault: false
350
+ };
351
+ },
352
+ customLayout: (args) => {
353
+ return {
354
+ rootContainer: createTextGraphics(args),
355
+ renderDefault: false
356
+ };
357
+ }
358
+ };
359
+ return baseColumn;
360
+ };
361
+ const textColumnCreator = (data, innerColumnControl) => {
362
+ const baseColumn = baseCreator(data);
363
+ return {
364
+ ...baseColumn,
365
+ customLayout: (args) => {
366
+ return {
367
+ rootContainer: createTextGraphics(args),
368
+ renderDefault: false
369
+ };
370
+ }
371
+ };
372
+ };
373
+ const simpleSelectColumnCreator = (data, innerColumnControl) => {
374
+ const baseColumn = baseCreator(data);
375
+ return {
376
+ ...baseColumn,
377
+ editor: "singleSelectEditor",
378
+ customLayout: (args) => {
379
+ return {
380
+ rootContainer: createSingleSelectGraphic(args),
381
+ renderDefault: false
382
+ };
383
+ }
384
+ };
385
+ };
386
+ const multiSelectColumnCreator = (data, innerColumnControl) => {
387
+ const baseColumn = baseCreator(data);
388
+ return {
389
+ ...baseColumn,
390
+ editor: "multiSelectEditor",
391
+ customLayout: (args) => {
392
+ const { table, row, col, rect } = args;
393
+ const { height, width } = rect ?? table.getCellRect(col, row);
394
+ return {
395
+ rootContainer: createMultiSelectGraphic({ width, height, ...args }),
396
+ renderDefault: false
397
+ };
398
+ }
399
+ };
400
+ };
401
+ const numberColumnCreator = (data, innerColumnControl) => {
402
+ const baseColumn = baseCreator(data);
403
+ return {
404
+ ...baseColumn,
405
+ fmt: data.default?.format,
406
+ // editor: "numberEditor",
407
+ customLayout: (args) => {
408
+ return {
409
+ rootContainer: createNumberGraphics({
410
+ ...args,
411
+ textAlign: "right"
412
+ }),
413
+ renderDefault: false
414
+ };
415
+ }
416
+ };
417
+ };
418
+ const attachmentColumnCreator = (data, innerColumnControl) => {
419
+ const baseColumn = baseCreator(data);
420
+ return {
421
+ ...baseColumn,
422
+ customLayout: (args) => {
423
+ return {
424
+ rootContainer: createUploadDocumentGraphics(args),
425
+ renderDefault: false
426
+ };
427
+ }
428
+ };
429
+ };
430
+ const linkColumnCreator = (data, innerColumnControl) => {
431
+ const baseColumn = baseCreator(data);
432
+ return {
433
+ ...baseColumn,
434
+ customLayout: (args) => {
435
+ return {
436
+ rootContainer: createLinkGraphics(args),
437
+ renderDefault: false
438
+ };
439
+ }
440
+ };
441
+ };
442
+ const dateColumnCreator = (data, innerColumnControl) => {
443
+ const baseColumn = baseCreator(data);
444
+ return {
445
+ ...baseColumn,
446
+ fmt: data.default?.format,
447
+ customLayout: (args) => {
448
+ return {
449
+ rootContainer: createDateGraphics(args),
450
+ renderDefault: false
451
+ };
452
+ }
453
+ };
454
+ };
455
+ const timeLenColumnCreator = (data, innerColumnControl) => {
456
+ const baseColumn = baseCreator(data);
457
+ return {
458
+ ...baseColumn,
459
+ // NOTE: editor 为 number 输入整型
460
+ fmt: "integer",
461
+ customLayout: (args) => {
462
+ return {
463
+ rootContainer: createTimeLenGraphics(args),
464
+ renderDefault: false
465
+ };
466
+ }
467
+ };
468
+ };
469
+ const userColumnCreator = (data, innerColumnControl) => {
470
+ const baseColumn = baseCreator(data);
471
+ return {
472
+ ...baseColumn,
473
+ customLayout: (args) => {
474
+ return {
475
+ rootContainer: createPeopleGraphics(args),
476
+ renderDefault: false
477
+ };
478
+ }
479
+ };
480
+ };
481
+ const tagColumnCreator = (data, innerColumnControl) => {
482
+ const baseColumn = baseCreator(data);
483
+ return {
484
+ ...baseColumn,
485
+ customLayout: (args) => {
486
+ return {
487
+ rootContainer: createTagGraphic(args),
488
+ renderDefault: false
489
+ };
490
+ }
491
+ };
492
+ };
493
+ const jsonColumnCreator = (data, innerColumnControl) => {
494
+ const baseColumn = baseCreator(data);
495
+ return {
496
+ ...baseColumn,
497
+ customLayout: (args) => {
498
+ return {
499
+ rootContainer: createJsonGraphics(args),
500
+ renderDefault: false
501
+ };
502
+ }
503
+ };
504
+ };
505
+ const dropdownColumnCreator = (data, innerColumnControl) => {
506
+ const baseColumn = baseCreator(data);
507
+ return {
508
+ ...baseColumn,
509
+ dragHeader: false,
510
+ customLayout: (args) => {
511
+ return {
512
+ rootContainer: createMoreGraphics(args),
513
+ renderDefault: false
514
+ };
515
+ }
516
+ };
517
+ };
518
+ const checkboxColumnCreator = (data, innerColumnControl) => {
519
+ const baseColumn = baseCreator(data);
520
+ return {
521
+ ...baseColumn,
522
+ lock: true,
523
+ width: 40,
524
+ minWidth: 40,
525
+ headerType: "checkbox",
526
+ //指定表头单元格显示为复选框
527
+ cellType: "checkbox",
528
+ //指定body单元格显示为复选框
529
+ field: INNER_TABLE_COLUMN_KEY_CHECKBOX,
530
+ disable: false,
531
+ disableColumnResize: true,
532
+ disableSelect: true,
533
+ disableHeaderSelect: true,
534
+ dragHeader: false,
535
+ headerStyle: {
536
+ borderLineWidth: [1, 0, 1, 1],
537
+ // 上右下左的边框宽度
538
+ marked: false
539
+ },
540
+ style: {
541
+ borderLineWidth: [1, 0, 1, 1],
542
+ // 上右下左的边框宽度
543
+ marked: false
544
+ }
545
+ };
546
+ };
547
+ const plusColumnCreator = (data, innerColumnControl) => {
548
+ const baseColumn = baseCreator(data);
549
+ return {
550
+ ...baseColumn,
551
+ dragHeader: false,
552
+ field: INNER_TABLE_COLUMN_KEY_PLUS,
553
+ width: 50,
554
+ disable: true,
555
+ disableHover: true,
556
+ disableHeaderSelect: true,
557
+ disableColumnResize: true,
558
+ disableSelect: true,
559
+ headerCustomLayout: (args) => {
560
+ return {
561
+ rootContainer: createPlusHeaderGraphics(args),
562
+ renderDefault: false
563
+ };
564
+ },
565
+ style: {
566
+ borderLineWidth: ({ row, table }) => {
567
+ if (row === 1) return [1, 1, 0, 1];
568
+ if (row === table.rowCount - 1) return [0, 1, 1, 1];
569
+ return [0, 1, 0, 1];
570
+ }
571
+ }
572
+ };
573
+ };
574
+ const moreColumnCreator = (data, innerColumnControl) => {
575
+ const baseColumn = baseCreator(data);
576
+ return {
577
+ ...baseColumn,
578
+ field: "more",
579
+ title: "\u66F4\u591A",
580
+ dragHeader: false,
581
+ customLayout: (args) => {
582
+ return {
583
+ rootContainer: createMoreGraphics(args),
584
+ renderDefault: false
585
+ };
586
+ }
587
+ };
588
+ };
589
+ const mainColumnCreator = (data, innerColumnControl) => {
590
+ const baseColumn = baseCreator(data);
591
+ const mainKeyColumn = {
592
+ ...baseColumn,
593
+ tool: 28,
594
+ lock: true,
595
+ field: data.id,
596
+ dragHeader: false,
597
+ title: data.label,
598
+ width: 200,
599
+ headerStyle: {
600
+ borderLineWidth: [1, 1, 1, 0]
601
+ // 上右下左的边框宽度
602
+ },
603
+ style: {
604
+ borderLineWidth: [1, 1, 1, 0]
605
+ // 上右下左的边框宽度
606
+ },
607
+ headerCustomLayout: (args) => {
608
+ return {
609
+ rootContainer: createHeaderGraphics(28, args),
610
+ renderDefault: false
611
+ };
612
+ },
613
+ customLayout: (args) => {
614
+ return {
615
+ rootContainer: createMainColumnCellGraphics(args),
616
+ renderDefault: false
617
+ };
618
+ }
619
+ };
620
+ return mainKeyColumn;
621
+ };
622
+ const createColumns = (data, innerColumnControl) => {
623
+ const creator = creatorMap.get(data.type) ?? baseCreator;
624
+ return castArray(creator(data, innerColumnControl));
625
+ };
626
+ const creatorMap = /* @__PURE__ */ new Map([
627
+ [28, textColumnCreator],
628
+ [29, simpleSelectColumnCreator],
629
+ [30, multiSelectColumnCreator],
630
+ [31, numberColumnCreator],
631
+ [36, dateColumnCreator],
632
+ [33, linkColumnCreator],
633
+ [32, attachmentColumnCreator],
634
+ [37, timeLenColumnCreator],
635
+ [38, userColumnCreator],
636
+ [35, tagColumnCreator],
637
+ [34, jsonColumnCreator],
638
+ [39, dropdownColumnCreator],
639
+ /** 表格内置列 */
640
+ [-1, checkboxColumnCreator],
641
+ [-3, plusColumnCreator],
642
+ [-2, moreColumnCreator],
643
+ [-4, mainColumnCreator]
644
+ ]);
645
+
646
+ const adaptColumnStructure = (originalColumns, innerColumnControl) => {
647
+ const hasMainColumn = innerColumnControl.main;
648
+ const columnFeedbacksErrors = [];
649
+ const adaptedColumns = originalColumns.map((column, colIndex) => {
650
+ if (column.feedbacks && column.feedbacks.length > 0) {
651
+ columnFeedbacksErrors.push({
652
+ type: "column",
653
+ col: getColFromColIndex(colIndex, innerColumnControl),
654
+ info: column.feedbacks
655
+ });
656
+ }
657
+ return createColumns(
658
+ {
659
+ ...column,
660
+ // TODO: any
661
+ type: colIndex === 0 && hasMainColumn && [28, 31].includes(column.type) ? -4 : column.type
662
+ },
663
+ innerColumnControl
664
+ );
665
+ }).flat();
666
+ if (innerColumnControl.checkbox) {
667
+ adaptedColumns.unshift(
668
+ checkboxColumnCreator(
669
+ {
670
+ type: -1,
671
+ id: INNER_TABLE_COLUMN_KEY_CHECKBOX,
672
+ label: ""
673
+ })
674
+ );
675
+ }
676
+ if (innerColumnControl.plus)
677
+ adaptedColumns.push(
678
+ plusColumnCreator(
679
+ {
680
+ type: -3,
681
+ id: INNER_TABLE_COLUMN_KEY_PLUS,
682
+ label: ""
683
+ })
684
+ );
685
+ return {
686
+ columns: adaptedColumns,
687
+ feedbacksHighlight: {
688
+ [CUSTOM_CELL_STYLE_FEEDBACK_ERROR]: [...columnFeedbacksErrors]
689
+ }
690
+ };
691
+ };
692
+
693
+ const adaptRecordStructure = (record) => {
694
+ const { id, cells } = record;
695
+ const cellValues = Object.entries(cells).map(([columnId, data]) => {
696
+ const { id: id2, value, loading, feedbacks } = data ?? {};
697
+ const realId = !id2 ? columnId : id2;
698
+ return [columnId, { id: realId, value, loading, feedbacks }];
699
+ });
700
+ return {
701
+ [INNER_TABLE_RECORD_ID_KEY]: id,
702
+ ...Object.fromEntries(cellValues)
703
+ };
704
+ };
705
+ const revertRecordStructure = (tableRecord) => {
706
+ const { [INNER_TABLE_RECORD_ID_KEY]: recordId, ...cellsMap } = tableRecord;
707
+ const cells = Object.fromEntries(
708
+ Object.entries(cellsMap).map(([columnId, cellData]) => {
709
+ const { id, value, loading, feedbacks } = cellData ?? {};
710
+ const realId = !id ? columnId : id;
711
+ return [
712
+ columnId,
713
+ {
714
+ id: realId,
715
+ value,
716
+ loading,
717
+ feedbacks
718
+ }
719
+ ];
720
+ })
721
+ );
722
+ return {
723
+ id: recordId,
724
+ cells
725
+ };
726
+ };
727
+ const adaptRecordsStructure = (records, adapterColumns, innerColumnControl) => {
728
+ const fields = adapterColumns.map((column) => column.field);
729
+ const rowFeedbacksErrors = [];
730
+ const cellFeedbacksErrors = [];
731
+ const rows = records.map((record, rowIndex) => {
732
+ const { id, feedbacks, cells } = record;
733
+ const cellValues = Object.entries(cells).map(([columnId, { id: id2, value, loading, feedbacks: feedbacks2 }]) => {
734
+ if (feedbacks2 && feedbacks2.length) {
735
+ cellFeedbacksErrors.push({
736
+ type: "cell",
737
+ col: fields.indexOf(columnId),
738
+ row: getRowFromRowIndex(rowIndex),
739
+ info: feedbacks2 ?? []
740
+ });
741
+ }
742
+ const realId = !id2 ? columnId : id2;
743
+ return [columnId, { id: realId, value, loading, feedbacks: feedbacks2 }];
744
+ });
745
+ if (feedbacks && feedbacks.length) {
746
+ rowFeedbacksErrors.push({
747
+ type: "row",
748
+ col: getColFromColIndex(0, innerColumnControl),
749
+ row: getRowFromRowIndex(rowIndex),
750
+ info: feedbacks ?? []
751
+ });
752
+ }
753
+ return {
754
+ [INNER_TABLE_RECORD_ID_KEY]: id,
755
+ ...Object.fromEntries(cellValues)
756
+ };
757
+ });
758
+ return {
759
+ records: rows,
760
+ feedbacksHighlight: {
761
+ [CUSTOM_CELL_STYLE_FEEDBACK_ERROR]: [...cellFeedbacksErrors, ...rowFeedbacksErrors]
762
+ }
763
+ };
764
+ };
765
+
766
+ const { setDateEditorData, getDateEditorData, useDateEditorData } = createAtomStore(
767
+ "dateEditorData",
768
+ atom()
769
+ );
770
+ const { setDateEditorValue, getDateEditorValue, useDateEditorValue } = createAtomStore(
771
+ "dateEditorValue",
772
+ atom()
773
+ );
774
+ const initDateEditorTool = (value) => {
775
+ const { initialValue } = value;
776
+ setDateEditorData(value);
777
+ setDateEditorValue(initialValue);
778
+ };
779
+ const clearDateEditorTool = () => {
780
+ setDateEditorData(void 0);
781
+ setDateEditorValue(void 0);
782
+ };
783
+
784
+ const { getActiveEditorTable, setActiveEditorTable, useActiveEditorTable } = createAtomStore(
785
+ "activeEditorTable",
786
+ atom(
787
+ (get) => {
788
+ const map = get(editorTableStoreAtom);
789
+ const activeId = get(activeTableIdAtom);
790
+ if (!activeId) throw new Error("activeTableId is empty");
791
+ return map.get(activeId);
792
+ },
793
+ () => {
794
+ throw new Error("activeEditorTable is empty");
795
+ }
796
+ )
797
+ );
798
+ const { setActiveTableId, getActiveTableId, useActiveTableId, activeTableIdAtom } = createAtomStore(
799
+ "activeTableId",
800
+ atom()
801
+ );
802
+ const { setEditorTableStore, getEditorTableStore, useEditorTableStore, editorTableStoreAtom } = createAtomStore(
803
+ "editorTableStore",
804
+ atom(/* @__PURE__ */ new Map())
805
+ );
806
+
807
+ const dateProcess = (data, dateFormat) => {
808
+ const originalFullDayjs = dayjs(data.initialValue);
809
+ const isSpecialFormat = dateFormat === "MM-DD" || dateFormat.includes("MM") && dateFormat.includes("DD") && !dateFormat.includes("YYYY") || dateFormat === "MM/DD/YYYY" || dateFormat === "DD/MM/YYYY";
810
+ if (isSpecialFormat) {
811
+ return originalFullDayjs.isValid() ? originalFullDayjs : dayjs();
812
+ } else {
813
+ return dayjs(data.initialValue, dateFormat).isValid() ? dayjs(data.initialValue, dateFormat) : dayjs();
814
+ }
815
+ };
816
+
817
+ const DateSelect = () => {
818
+ const data = useDateEditorData();
819
+ if (!data) return null;
820
+ const tableEditor = getActiveEditorTable();
821
+ let dateFormat = "";
822
+ const column = tableEditor?.getColumnDefine(data?.cell?.col);
823
+ if (column.fmt) {
824
+ dateFormat = column.fmt || "YYYY/MM/DD HH:mm";
825
+ }
826
+ setDateEditorValue(data.initialValue);
827
+ const validDayjs = dateProcess(data, dateFormat);
828
+ const TZ_SUFFIX = " (GMT+8)";
829
+ const hasTimezone = dateFormat.endsWith(TZ_SUFFIX);
830
+ const pureFormat = hasTimezone ? dateFormat.replace(TZ_SUFFIX, "") : dateFormat;
831
+ const hasTime = /(HH|hh):mm(:ss)?/.test(pureFormat);
832
+ const formatDateDisplay = (date) => {
833
+ if (!date) return "";
834
+ const baseText = date.format(pureFormat);
835
+ return hasTimezone ? `${baseText} (GMT+8)` : baseText;
836
+ };
837
+ return /* @__PURE__ */ React.createElement(
838
+ Popover,
839
+ {
840
+ trigger: "click",
841
+ className: "popoverDate",
842
+ arrow: false,
843
+ open: true,
844
+ align: {
845
+ points: ["tl", "bl"],
846
+ offset: [0, 10]
847
+ }
848
+ },
849
+ /* @__PURE__ */ React.createElement(
850
+ DatePicker,
851
+ {
852
+ defaultValue: validDayjs,
853
+ format: formatDateDisplay,
854
+ defaultOpen: true,
855
+ showTime: hasTime ? { format: pureFormat } : false,
856
+ allowClear: false,
857
+ onChange: (value) => {
858
+ let targetDayjs;
859
+ if (Array.isArray(value)) {
860
+ targetDayjs = value[0] || null;
861
+ } else {
862
+ targetDayjs = value;
863
+ }
864
+ if (!targetDayjs) return;
865
+ const finalFormat = hasTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD";
866
+ const finalValue = targetDayjs.format(finalFormat);
867
+ if (!hasTime) {
868
+ if (data.initialValue?.value) {
869
+ const originalDayjs = dayjs(data.initialValue.value);
870
+ const hour = originalDayjs.hour();
871
+ const minute = originalDayjs.minute();
872
+ const second = originalDayjs.second();
873
+ const newDayjs = targetDayjs.hour(hour).minute(minute).second(second);
874
+ setDateEditorValue(newDayjs.format("YYYY-MM-DD HH:mm:ss"));
875
+ } else {
876
+ setDateEditorValue(targetDayjs.hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"));
877
+ }
878
+ } else {
879
+ setDateEditorValue(finalValue);
880
+ }
881
+ data.endEdit();
882
+ },
883
+ style: {
884
+ position: "absolute",
885
+ left: data.cell.position.x,
886
+ top: data.cell.position.y,
887
+ width: data.cell.width,
888
+ height: data.cell.height
889
+ }
890
+ }
891
+ )
892
+ );
893
+ };
894
+
895
+ const { setJsonEditorData, getJsonEditorData, useJsonEditorData } = createAtomStore(
896
+ "jsonEditorData",
897
+ atom()
898
+ );
899
+ const { setJsonEditorValue, getJsonEditorValue, useJsonEditorValue } = createAtomStore(
900
+ "jsonEditorValue",
901
+ atom()
902
+ );
903
+ const initJsonEditorTool = (value) => {
904
+ const { initialValue } = value;
905
+ setJsonEditorData(value);
906
+ setJsonEditorValue(initialValue);
907
+ };
908
+ const clearJsonEditorTool = () => {
909
+ setJsonEditorData(void 0);
910
+ setJsonEditorValue(void 0);
911
+ };
912
+
913
+ const TableJsonEditor = () => {
914
+ const jsonEditorData = useJsonEditorData();
915
+ const jsonEditorValue = useJsonEditorValue();
916
+ const clcHeight = useMemo(() => {
917
+ const lineCount = (jsonEditorValue ?? "").split("\n").length;
918
+ return lineCount * 20;
919
+ }, [jsonEditorValue]);
920
+ if (!jsonEditorData) return null;
921
+ const editor = getActiveEditorTable();
922
+ if (!editor) throw new Error("TableJsonEditor: editor is null");
923
+ const { cell, table } = jsonEditorData;
924
+ const { position, width } = cell;
925
+ const cellStyle = table._getCellStyle(cell.col, cell.row);
926
+ const fontSize = getProp("fontSize", cellStyle, cell.col, cell.row, table);
927
+ return /* @__PURE__ */ React.createElement(
928
+ "div",
929
+ {
930
+ className: "json-editor-modal-container",
931
+ style: {
932
+ fontSize,
933
+ width,
934
+ height: clcHeight,
935
+ minHeight: 100,
936
+ maxHeight: 300,
937
+ position: "absolute",
938
+ top: position.y,
939
+ left: position.x
940
+ }
941
+ },
942
+ /* @__PURE__ */ React.createElement(JsonEditor, { value: jsonEditorData.initialValue, onChange: (value) => setJsonEditorValue(value), dark: false })
943
+ );
944
+ };
945
+
946
+ const { setLinkEditorData, getLinkEditorData, useLinkEditorData } = createAtomStore(
947
+ "linkEditorData",
948
+ atom()
949
+ );
950
+ const { setLinkEditorValue, getLinkEditorValue, useLinkEditorValue } = createAtomStore(
951
+ "linkEditorValue",
952
+ atom()
953
+ );
954
+ const initLinkEditorTool = (value) => {
955
+ const { initialValue } = value;
956
+ setLinkEditorData(value);
957
+ setLinkEditorValue(initialValue);
958
+ };
959
+ const clearLinkEditorTool = () => {
960
+ setLinkEditorData(void 0);
961
+ setLinkEditorValue(void 0);
962
+ };
963
+
964
+ const LinkEditor = ({}) => {
965
+ const [popContentVisible, setPopContentVisible] = useState(false);
966
+ const data = useLinkEditorData();
967
+ const linkValue = useLinkEditorValue();
968
+ const cancel = useCallback(() => {
969
+ setLinkEditorValue({ ...data?.initialValue ?? {} });
970
+ data?.endEdit();
971
+ }, [data]);
972
+ const confirm = useCallback(() => data?.endEdit(), [data]);
973
+ if (!data) return null;
974
+ return /* @__PURE__ */ React.createElement(
975
+ Popover,
976
+ {
977
+ open: popContentVisible,
978
+ content: /* @__PURE__ */ React.createElement(Form, { className: "link-editor-form", layout: "vertical", style: { width: 300 } }, /* @__PURE__ */ React.createElement(Form.Item, { label: "\u6587\u672C", className: "link-editor-form-item" }, /* @__PURE__ */ React.createElement(
979
+ Input,
980
+ {
981
+ defaultValue: linkValue?.title ?? "",
982
+ onChange: (e) => setLinkEditorValue((v) => ({ ...v ?? {}, title: e.target.value }))
983
+ }
984
+ )), /* @__PURE__ */ React.createElement(Form.Item, { label: "\u8D85\u94FE\u63A5", className: "link-editor-form-item" }, /* @__PURE__ */ React.createElement(
985
+ Input,
986
+ {
987
+ defaultValue: linkValue?.url ?? "",
988
+ onChange: (e) => setLinkEditorValue((v) => ({ ...v ?? {}, url: e.target.value }))
989
+ }
990
+ )), /* @__PURE__ */ React.createElement(Flex, { justify: "flex-end" }, /* @__PURE__ */ React.createElement(Space, null, /* @__PURE__ */ React.createElement(Button, { onClick: cancel }, "\u53D6\u6D88"), /* @__PURE__ */ React.createElement(Button, { type: "primary", onClick: confirm }, "\u786E\u5B9A")))),
991
+ rootClassName: "link-editor-popover-root",
992
+ className: "link-editor-popover",
993
+ arrow: false,
994
+ align: {
995
+ points: ["tl", "bl"],
996
+ // tl(top-left): 将【pop弹窗】左上角 与 Input bl(bottom-left) 左下角对齐
997
+ offset: [0, 10]
998
+ }
999
+ },
1000
+ /* @__PURE__ */ React.createElement(
1001
+ Flex,
1002
+ {
1003
+ style: {
1004
+ border: `2px solid ${COLOR_BORDER_SELECTION}`,
1005
+ backgroundColor: COLOR_BG_CELL,
1006
+ borderRadius: 0,
1007
+ position: "absolute",
1008
+ left: data.cell.position.x - 2,
1009
+ top: data.cell.position.y - 2,
1010
+ width: data.cell.width + 4,
1011
+ height: data.cell.height + 4
1012
+ },
1013
+ align: "center"
1014
+ },
1015
+ /* @__PURE__ */ React.createElement(
1016
+ Input,
1017
+ {
1018
+ className: "link-editor-input",
1019
+ style: {
1020
+ color: COLOR_LINK,
1021
+ border: "unset",
1022
+ borderRadius: 0,
1023
+ height: data.cell.height - 2,
1024
+ boxSizing: "border-box"
1025
+ },
1026
+ defaultValue: linkValue?.title ?? "",
1027
+ onChange: (e) => setLinkEditorValue((v) => ({ ...v ?? {}, title: e.target.value })),
1028
+ onFocus: () => setPopContentVisible(false)
1029
+ }
1030
+ ),
1031
+ /* @__PURE__ */ React.createElement(
1032
+ Button,
1033
+ {
1034
+ type: "text",
1035
+ icon: /* @__PURE__ */ React.createElement(LinkOutlined, null),
1036
+ style: {
1037
+ border: "unset",
1038
+ borderRadius: 0,
1039
+ height: data.cell.height - 2,
1040
+ boxSizing: "border-box"
1041
+ },
1042
+ onClick: () => setPopContentVisible(!popContentVisible)
1043
+ }
1044
+ )
1045
+ )
1046
+ );
1047
+ };
1048
+
1049
+ const { setNumEditorValue, getNumEditorValue, useNumEditorValue } = createAtomStore(
1050
+ "numEditorValue",
1051
+ atom()
1052
+ );
1053
+ const { setNumEditorData, getNumEditorData, useNumEditorData } = createAtomStore(
1054
+ "numEditorData",
1055
+ atom()
1056
+ );
1057
+ const initNumEditorTool = (data) => {
1058
+ setNumEditorData(data);
1059
+ setNumEditorValue(data.initialValue);
1060
+ };
1061
+ const clearNumEditorTool = () => {
1062
+ setNumEditorData(void 0);
1063
+ setNumEditorValue(void 0);
1064
+ };
1065
+
1066
+ const NumEditor = () => {
1067
+ const numEditorData = useNumEditorData();
1068
+ if (!numEditorData) return null;
1069
+ const { cell, table } = numEditorData;
1070
+ const { position, width, height } = cell;
1071
+ const cellStyle = table._getCellStyle(cell.col, cell.row);
1072
+ const fontSize = getProp("fontSize", cellStyle, cell.col, cell.row, table);
1073
+ const bgColor = getProp("bgColor", cellStyle, cell.col, cell.row, table);
1074
+ return /* @__PURE__ */ React.createElement(
1075
+ InputNumber,
1076
+ {
1077
+ defaultValue: numEditorData.initialValue,
1078
+ onChange: (value) => setNumEditorValue(value),
1079
+ controls: false,
1080
+ className: "num-editor-input",
1081
+ style: {
1082
+ position: "absolute",
1083
+ left: position.x - 2,
1084
+ top: position.y - 2,
1085
+ width: width + 4,
1086
+ minHeight: height + 4,
1087
+ background: bgColor,
1088
+ border: "2px solid #0404ff",
1089
+ borderRadius: 0,
1090
+ fontSize,
1091
+ scrollbarWidth: "none"
1092
+ }
1093
+ }
1094
+ );
1095
+ };
1096
+
1097
+ const { setSelectEditorData, getSelectEditorData, useSelectEditorData } = createAtomStore(
1098
+ "selectEditorData",
1099
+ atom(null)
1100
+ );
1101
+ const { setSelectEditorValue, getSelectEditorValue, useSelectEditorValue } = createAtomStore(
1102
+ "selectEditorValue",
1103
+ atom([])
1104
+ );
1105
+ const initSelectEditorTool = (value) => {
1106
+ const { initialValue } = value;
1107
+ setSelectEditorData(value);
1108
+ setSelectEditorValue(initialValue);
1109
+ };
1110
+ const clearSelectEditorTool = () => {
1111
+ setSelectEditorData(null);
1112
+ setSelectEditorValue([]);
1113
+ };
1114
+
1115
+ const TableChangeEvent = {
1116
+ "CELLS_UPDATE": "cells::update",
1117
+ "CELL_CLICK": "cell::click",
1118
+ "CELL_ATTACHMENT_PREVIEW": "cell::attachment::preview",
1119
+ "SELECT_OPTIONS_UPDATE": "select::options::update",
1120
+ "RECORD_OPEN_DETAIL_DRAWER": "record::open::detail::drawer",
1121
+ "RECORD_CHECKBOX_STATE_CHANGE": "record::checkbox::state::change",
1122
+ "COLUMN_WIDTH_UPDATE": "column::width::update",
1123
+ "COLUMN_DRAG_MOVE_END": "column::drag::move::end",
1124
+ "COLUMN_ADD_FROM_INNER_PLUS": "column::add::from::inner::plus"
1125
+ };
1126
+
1127
+ const EDIT_TABLE_CELL_DATA_COMMAND_ID = Symbol("EDIT_TABLE_CELL_DATA_COMMAND_ID");
1128
+ const EDIT_TABLE_CELL_DATA_COMMAND_LABEL = "\u7F16\u8F91\u8868\u683C\u5355\u5143\u683C\u6570\u636E\u901A\u7528\u547D\u4EE4";
1129
+ class EditTableCellDataCommand extends Command {
1130
+ constructor(editor, col, row, value, oldValue) {
1131
+ super();
1132
+ this.editor = editor;
1133
+ this.col = col;
1134
+ this.row = row;
1135
+ this.value = value;
1136
+ this.oldValue = oldValue;
1137
+ this.id = EDIT_TABLE_CELL_DATA_COMMAND_ID;
1138
+ this.label = EDIT_TABLE_CELL_DATA_COMMAND_LABEL;
1139
+ }
1140
+ get readonly() {
1141
+ const columnDefine = this.editor.getColumnDefine(this.col);
1142
+ return this.editor.lock || !!columnDefine?.lock;
1143
+ }
1144
+ execute(_props) {
1145
+ this.editor.updateCellData(this.col, this.row, this.value);
1146
+ const { columnId, recordId } = this.editor.getInteractiveCell(this.col, this.row);
1147
+ this.editor.dispatch("cells::update", {
1148
+ editor: this.editor,
1149
+ data: [
1150
+ {
1151
+ recordId,
1152
+ columnId,
1153
+ previous: this.oldValue,
1154
+ current: this.value
1155
+ }
1156
+ ]
1157
+ });
1158
+ }
1159
+ undo(_props) {
1160
+ this.editor.updateCellData(this.col, this.row, this.oldValue);
1161
+ const { columnId, recordId } = this.editor.getInteractiveCell(this.col, this.row);
1162
+ this.editor.dispatch("cells::update", {
1163
+ editor: this.editor,
1164
+ data: [
1165
+ {
1166
+ recordId,
1167
+ columnId,
1168
+ previous: this.value,
1169
+ current: this.oldValue
1170
+ }
1171
+ ]
1172
+ });
1173
+ }
1174
+ }
1175
+ const EDIT_TABLE_CELL_DATA_FUNCTIONAL_COMMAND_ID = Symbol("EDIT_TABLE_CELL_DATA_FUNCTIONAL_COMMAND_ID");
1176
+ const EDIT_TABLE_CELL_DATA_FUNCTIONAL_COMMAND_LABEL = "\u7F16\u8F91\u8868\u683C\u5355\u5143\u683C\u6570\u636E\u901A\u7528\u51FD\u6570\u5F0F\u547D\u4EE4";
1177
+ class EditTableCellDataFunctionalCommand extends Command {
1178
+ constructor(table, fn) {
1179
+ super();
1180
+ this.table = table;
1181
+ this.fn = fn;
1182
+ this.id = EDIT_TABLE_CELL_DATA_FUNCTIONAL_COMMAND_ID;
1183
+ this.label = EDIT_TABLE_CELL_DATA_FUNCTIONAL_COMMAND_LABEL;
1184
+ }
1185
+ get readonly() {
1186
+ return this.table.lock;
1187
+ }
1188
+ execute(_props) {
1189
+ const { restore } = this.fn();
1190
+ this._restore = restore;
1191
+ }
1192
+ undo(_props) {
1193
+ if (!this._restore) {
1194
+ throw new Error(`[EditTableCellCommand id: ${this.id.toString()} label: ${this.label}] \u672A\u6307\u5B9A\u6062\u590D\u51FD\u6570`);
1195
+ }
1196
+ this._restore();
1197
+ this._restore = void 0;
1198
+ }
1199
+ }
1200
+
1201
+ const updateSelectEditorCell = (col, row, value, oldValue) => {
1202
+ const commandManager = getCommandManager();
1203
+ const tableEditor = getActiveEditorTable();
1204
+ if (!commandManager) return;
1205
+ if (!tableEditor) return;
1206
+ startTransaction((command2) => command2 instanceof EditTableCellDataFunctionalCommand);
1207
+ const run = () => {
1208
+ const previous = tableEditor.getCellOriginValue(col, row);
1209
+ const current = { ...previous, value };
1210
+ tableEditor.updateCellData(col, row, current);
1211
+ const position = tableEditor.getInteractiveCell(col, row);
1212
+ tableEditor.dispatch("cells::update", {
1213
+ editor: tableEditor,
1214
+ data: [
1215
+ {
1216
+ previous,
1217
+ current,
1218
+ ...position
1219
+ }
1220
+ ]
1221
+ });
1222
+ const curData = getSelectEditorData();
1223
+ if (curData && curData.cell.col === col && curData.cell.row === row) {
1224
+ setSelectEditorValue(value);
1225
+ }
1226
+ return {
1227
+ restore: () => {
1228
+ const curData2 = getSelectEditorData();
1229
+ tableEditor.updateCellData(col, row, previous);
1230
+ tableEditor.dispatch("cells::update", {
1231
+ editor: tableEditor,
1232
+ data: [
1233
+ {
1234
+ previous: current,
1235
+ current: previous,
1236
+ ...position
1237
+ }
1238
+ ]
1239
+ });
1240
+ if (!curData2) return;
1241
+ if (curData2.cell.col !== col || curData2.cell.row !== row) return;
1242
+ setSelectEditorValue(oldValue);
1243
+ }
1244
+ };
1245
+ };
1246
+ const command = new EditTableCellDataFunctionalCommand(tableEditor, run);
1247
+ commandManager.execute(command);
1248
+ stopTransaction();
1249
+ };
1250
+
1251
+ const SelectEditor = ({
1252
+ listHeight = 300,
1253
+ itemHeight = 32,
1254
+ emptyText = "\u6682\u65E0\u6570\u636E",
1255
+ multiple = true
1256
+ }) => {
1257
+ const data = useSelectEditorData();
1258
+ const table = useActiveEditorTable();
1259
+ if (!data) throw new Error("SelectEditorData is null in SelectEditor");
1260
+ if (!table) throw new Error("Table is null in SelectEditor");
1261
+ const editValue = useSelectEditorValue();
1262
+ const updateOption = (option) => {
1263
+ setSelectEditorData((prev) => {
1264
+ if (!prev) {
1265
+ return prev;
1266
+ }
1267
+ const newOptions = prev.options.map((opt) => {
1268
+ if (opt.value === option.value) {
1269
+ return { ...opt, label: option.label, color: option.color || opt.color };
1270
+ }
1271
+ return opt;
1272
+ });
1273
+ const col = data.cell.col;
1274
+ table.updateColumn(col, { options: newOptions });
1275
+ table.dispatch("select::options::update", {
1276
+ editor: table,
1277
+ columnId: String(table.getColumnDefine(data.cell.col)?.field || ""),
1278
+ options: newOptions
1279
+ });
1280
+ return { ...prev, options: newOptions };
1281
+ });
1282
+ setSelectEditorValue((prev) => {
1283
+ if (!prev) {
1284
+ return prev;
1285
+ }
1286
+ return prev.map((item) => item.value === option.value ? option : item);
1287
+ });
1288
+ };
1289
+ const createOption = async (option) => {
1290
+ const column = table.getColumnDefine(data.cell.col);
1291
+ if (!column) throw new Error("SelectEditor createOption column is null");
1292
+ if (column.request) option = await column.request(option).catch(() => option);
1293
+ setSelectEditorData((prev) => {
1294
+ if (!prev) return prev;
1295
+ const col = data.cell.col;
1296
+ table.updateColumn(col, { options: [...prev.options, option] });
1297
+ table.dispatch("select::options::update", {
1298
+ editor: table,
1299
+ columnId: String(table.getColumnDefine(data.cell.col)?.field || ""),
1300
+ options: [...prev.options, option]
1301
+ });
1302
+ return { ...prev, options: [...prev.options, option] };
1303
+ });
1304
+ return option;
1305
+ };
1306
+ const selectOption = (option) => {
1307
+ const newEditValue = editValue || [];
1308
+ if (multiple) {
1309
+ const newSelected = newEditValue.map((item) => item.value).includes(option.value) ? newEditValue : [...newEditValue, option];
1310
+ updateSelectEditorCell(data.cell.col, data.cell.row, newSelected, newEditValue);
1311
+ } else {
1312
+ updateSelectEditorCell(data.cell.col, data.cell.row, [option], newEditValue);
1313
+ }
1314
+ };
1315
+ const deselectOption = (option) => {
1316
+ const newEditValue = editValue || [];
1317
+ const filterValue = newEditValue.filter((item) => item.value !== option.value);
1318
+ updateSelectEditorCell(data.cell.col, data.cell.row, filterValue, newEditValue);
1319
+ };
1320
+ const handleChange = (value) => {
1321
+ setSelectEditorValue(value);
1322
+ };
1323
+ return /* @__PURE__ */ React.createElement(
1324
+ Select,
1325
+ {
1326
+ listHeight,
1327
+ itemHeight,
1328
+ emptyText,
1329
+ multiple,
1330
+ options: data.options,
1331
+ value: editValue || [],
1332
+ onChange: handleChange,
1333
+ onOptionChange: updateOption,
1334
+ onOptionCreate: createOption,
1335
+ onSelect: selectOption,
1336
+ onDeselect: deselectOption,
1337
+ tagAreaStyle: {
1338
+ position: "absolute",
1339
+ left: data.cell.position.x - 2,
1340
+ top: data.cell.position.y - 2,
1341
+ width: data.cell.width + 4,
1342
+ minHeight: data.cell.height + 4,
1343
+ border: `2px solid ${COLOR_BORDER_SELECTION}`,
1344
+ backgroundColor: COLOR_BG_CELL
1345
+ },
1346
+ width: data.cell.width + 4,
1347
+ tagAreaHeight: data.cell.height,
1348
+ showDownIcon: true
1349
+ }
1350
+ );
1351
+ };
1352
+
1353
+ const TableSelectEditor = () => {
1354
+ const data = useSelectEditorData();
1355
+ if (!data) {
1356
+ return null;
1357
+ }
1358
+ return /* @__PURE__ */ React.createElement(SelectEditor, { multiple: data.isMultiple });
1359
+ };
1360
+
1361
+ const { setTextEditorValue, getTextEditorValue, useTextEditorValue } = createAtomStore(
1362
+ "textEditorValue",
1363
+ atom()
1364
+ );
1365
+ const { setTextEditorData, getTextEditorData, useTextEditorData } = createAtomStore(
1366
+ "textEditorData",
1367
+ atom()
1368
+ );
1369
+ const initTextEditorTool = (data) => {
1370
+ setTextEditorData(data);
1371
+ setTextEditorValue(data.initialValue);
1372
+ };
1373
+ const clearTextEditorTool = () => {
1374
+ setTextEditorData(void 0);
1375
+ setTextEditorValue(void 0);
1376
+ };
1377
+
1378
+ const TextEditor = () => {
1379
+ const textEditorData = useTextEditorData();
1380
+ if (!textEditorData) return null;
1381
+ const { cell, table } = textEditorData;
1382
+ const { position, width, height } = cell;
1383
+ const cellStyle = table._getCellStyle(cell.col, cell.row);
1384
+ const fontSize = getProp("fontSize", cellStyle, cell.col, cell.row, table);
1385
+ const bgColor = getProp("bgColor", cellStyle, cell.col, cell.row, table);
1386
+ return /* @__PURE__ */ React.createElement(
1387
+ Input.TextArea,
1388
+ {
1389
+ defaultValue: textEditorData.initialValue,
1390
+ onChange: (e) => setTextEditorValue(e.target.value),
1391
+ autoSize: { minRows: 1 },
1392
+ style: {
1393
+ position: "absolute",
1394
+ left: position.x - 2,
1395
+ top: position.y - 2,
1396
+ width: width + 4,
1397
+ height: height + 4,
1398
+ padding: 5,
1399
+ background: bgColor,
1400
+ fontSize,
1401
+ border: "2px solid #0404ff",
1402
+ borderRadius: 0,
1403
+ scrollbarWidth: "none"
1404
+ }
1405
+ }
1406
+ );
1407
+ };
1408
+
1409
+ const { setUserShowId, getUserShowId, useUserShowId } = createAtomStore("userShowId", atom(null));
1410
+ const { setUserShowData, getUserShowData, useUserShowData } = createAtomStore(
1411
+ "userShowData",
1412
+ atom(null)
1413
+ );
1414
+ const initUserShowTool = (data) => {
1415
+ setUserShowData(data);
1416
+ setUserShowId(data.id);
1417
+ };
1418
+ const clearUserShowTool = () => {
1419
+ setUserShowData(null);
1420
+ setUserShowId(null);
1421
+ };
1422
+
1423
+ const UserShow = () => {
1424
+ const data = useUserShowData();
1425
+ const valueId = useUserShowId();
1426
+ const col = data?.cell?.col;
1427
+ const row = data?.cell?.row;
1428
+ const table = data?.table;
1429
+ let cellStyle, fontSize;
1430
+ if (table && typeof col === "number" && typeof row === "number") {
1431
+ cellStyle = table._getCellStyle(col, row);
1432
+ fontSize = getProp("fontSize", cellStyle, col, row, table);
1433
+ } else {
1434
+ cellStyle = void 0;
1435
+ fontSize = 14;
1436
+ }
1437
+ const [popoverKey, setPopoverKey] = useState(0);
1438
+ const [open, setOpen] = useState(false);
1439
+ useEffect(() => {
1440
+ if (data) {
1441
+ setPopoverKey((prev) => prev + 1);
1442
+ setOpen(true);
1443
+ }
1444
+ }, [data, valueId, col]);
1445
+ if (!data || valueId === "-2") return null;
1446
+ return /* @__PURE__ */ React.createElement(
1447
+ "div",
1448
+ {
1449
+ style: {
1450
+ position: "absolute",
1451
+ left: data.cell.position.x,
1452
+ top: data.cell.position.y - 8,
1453
+ zIndex: 9999,
1454
+ pointerEvents: "auto",
1455
+ transform: "translateX(-50%) translateY(-100%)"
1456
+ }
1457
+ },
1458
+ /* @__PURE__ */ React.createElement(
1459
+ Popover,
1460
+ {
1461
+ key: popoverKey,
1462
+ content: /* @__PURE__ */ React.createElement(
1463
+ AvatarPopoverContent,
1464
+ {
1465
+ id: valueId,
1466
+ column: getActiveEditorTable()?.getColumnDefine(col),
1467
+ fontSize
1468
+ }
1469
+ ),
1470
+ open,
1471
+ onOpenChange: setOpen,
1472
+ placement: "top",
1473
+ autoAdjustOverflow: false,
1474
+ mouseEnterDelay: 0.3,
1475
+ mouseLeaveDelay: 0.3,
1476
+ arrow: { pointAtCenter: true },
1477
+ getPopupContainer: () => document.body
1478
+ }
1479
+ )
1480
+ );
1481
+ };
1482
+
1483
+ const { setMenu, getMenu, useMenu } = createAtomStore("menu", atom(null));
1484
+
1485
+ const CellContextMenu = ({}) => {
1486
+ const menu = useMenu();
1487
+ const { cellMenus } = useComponentVTable();
1488
+ const tableEditor = useActiveEditorTable();
1489
+ if (!tableEditor) return null;
1490
+ if (!menu || menu.type !== "cell" || !cellMenus?.length) return null;
1491
+ return /* @__PURE__ */ React.createElement(
1492
+ CellContextMenuInner,
1493
+ {
1494
+ menu,
1495
+ cellMenus,
1496
+ tableEditor,
1497
+ offsetParent: tableEditor.container
1498
+ }
1499
+ );
1500
+ };
1501
+ const CellContextMenuInner = ({ menu, cellMenus, tableEditor, offsetParent = null }) => {
1502
+ const menuItems = useMemo(() => {
1503
+ if (!menu || menu.type !== "cell") return;
1504
+ return cellMenus?.map((item) => {
1505
+ const { onCellClick, ...rest } = item;
1506
+ return {
1507
+ ...rest,
1508
+ onClick: (info) => {
1509
+ onCellClick?.({
1510
+ menuInfo: info,
1511
+ tableEditor,
1512
+ cell: {
1513
+ col: menu.cell.col,
1514
+ row: menu.cell.row,
1515
+ recordId: menu.cell.recordId,
1516
+ columnId: menu.cell.columnId,
1517
+ rect: menu.cell.rect
1518
+ }
1519
+ });
1520
+ setMenu(null);
1521
+ }
1522
+ };
1523
+ });
1524
+ }, [cellMenus, menu, tableEditor]);
1525
+ return /* @__PURE__ */ React.createElement(
1526
+ TableMenuModal,
1527
+ {
1528
+ isOpen: menu?.open,
1529
+ offsetParent,
1530
+ position: menu.position,
1531
+ menuItems: menuItems ?? [],
1532
+ onPositionChange: (position) => setMenu(
1533
+ (prev) => prev ? { ...prev, position: typeof position === "function" ? position(prev.position) : position } : null
1534
+ )
1535
+ }
1536
+ );
1537
+ };
1538
+
1539
+ const HeaderContextMenu = ({}) => {
1540
+ const { headerMenus } = useComponentVTable();
1541
+ const menu = useMenu();
1542
+ const tableEditor = useActiveEditorTable();
1543
+ if (!tableEditor) return null;
1544
+ if (!menu || menu.type !== "header" || !headerMenus?.length) return null;
1545
+ return /* @__PURE__ */ React.createElement(
1546
+ HeaderContextMenuInner,
1547
+ {
1548
+ menu,
1549
+ headerMenus,
1550
+ tableEditor,
1551
+ offsetParent: tableEditor.container
1552
+ }
1553
+ );
1554
+ };
1555
+ const HeaderContextMenuInner = ({
1556
+ menu,
1557
+ headerMenus,
1558
+ tableEditor,
1559
+ offsetParent = null
1560
+ }) => {
1561
+ const menuItems = useMemo(() => {
1562
+ if (!menu || menu.type !== "header") return;
1563
+ return headerMenus?.map((item) => {
1564
+ const { onCellClick, ...rest } = item;
1565
+ return {
1566
+ ...rest,
1567
+ onClick: (info) => {
1568
+ onCellClick?.({
1569
+ menuInfo: info,
1570
+ tableEditor,
1571
+ cell: {
1572
+ col: menu.cell.col,
1573
+ row: menu.cell.row,
1574
+ recordId: menu.cell.recordId,
1575
+ columnId: menu.cell.columnId,
1576
+ rect: menu.cell.rect
1577
+ }
1578
+ });
1579
+ setMenu(null);
1580
+ }
1581
+ };
1582
+ });
1583
+ }, [headerMenus, menu, tableEditor]);
1584
+ return /* @__PURE__ */ React.createElement(
1585
+ TableMenuModal,
1586
+ {
1587
+ isOpen: menu?.open,
1588
+ width: 210,
1589
+ offsetParent,
1590
+ position: menu.position,
1591
+ menuItems: menuItems ?? [],
1592
+ onPositionChange: (position) => setMenu(
1593
+ (prev) => prev ? { ...prev, position: typeof position === "function" ? position(prev.position) : position } : null
1594
+ )
1595
+ }
1596
+ );
1597
+ };
1598
+
1599
+ const MoreContextMenu = () => {
1600
+ const menu = useMenu();
1601
+ const tableEditor = useActiveEditorTable();
1602
+ const { moreMenus } = useComponentVTable();
1603
+ if (!tableEditor) return null;
1604
+ if (!menu || menu.type !== "more") return null;
1605
+ return /* @__PURE__ */ React.createElement(MoreContextMenuInner, { menu, moreMenus, tableEditor });
1606
+ };
1607
+ const MoreContextMenuInner = ({ menu, moreMenus, tableEditor }) => {
1608
+ const menuItems = useMemo(() => {
1609
+ if (!menu || menu.type !== "more") return;
1610
+ return moreMenus?.map((item) => {
1611
+ const { onCellClick, ...rest } = item;
1612
+ return {
1613
+ ...rest,
1614
+ onClick: (info) => {
1615
+ onCellClick?.({
1616
+ menuInfo: info,
1617
+ tableEditor,
1618
+ cell: {
1619
+ col: menu.cell.col,
1620
+ row: menu.cell.row,
1621
+ recordId: menu.cell.recordId,
1622
+ columnId: menu.cell.columnId,
1623
+ rect: menu.cell.rect
1624
+ }
1625
+ });
1626
+ }
1627
+ };
1628
+ });
1629
+ }, [moreMenus, menu, tableEditor]);
1630
+ return /* @__PURE__ */ React.createElement(
1631
+ "div",
1632
+ {
1633
+ onMouseMove: () => setMenu({ ...menu }),
1634
+ onMouseLeave: () => setMenu(null),
1635
+ onMouseEnter: () => setMenu({ ...menu })
1636
+ },
1637
+ /* @__PURE__ */ React.createElement(
1638
+ Dropdown,
1639
+ {
1640
+ menu: {
1641
+ items: menuItems ?? []
1642
+ },
1643
+ align: {
1644
+ offset: [0, -4]
1645
+ },
1646
+ placement: "bottom",
1647
+ arrow: false
1648
+ },
1649
+ /* @__PURE__ */ React.createElement(
1650
+ "div",
1651
+ {
1652
+ style: {
1653
+ left: menu.position.x,
1654
+ top: menu.position.y,
1655
+ position: "absolute",
1656
+ width: 16,
1657
+ height: 20,
1658
+ cursor: "pointer"
1659
+ }
1660
+ }
1661
+ )
1662
+ )
1663
+ );
1664
+ };
1665
+
1666
+ const Delete = () => {
1667
+ useHotkeys(
1668
+ ["backspace", "delete"],
1669
+ () => {
1670
+ const editor = getActiveEditorTable();
1671
+ if (!editor) return;
1672
+ const run = () => {
1673
+ const selectedCellGroup = editor.getSelectedCell() ?? [];
1674
+ const selectedCells = selectedCellGroup.flat().map((item) => ({
1675
+ col: item.col,
1676
+ row: item.row
1677
+ })).filter((item) => item.row !== 0);
1678
+ const payload = selectedCells.map((item) => {
1679
+ return {
1680
+ ...editor.getInteractiveCell(item.col, item.row),
1681
+ previous: editor.getCellOriginValue(item.col, item.row)
1682
+ };
1683
+ });
1684
+ const { recordsForRestore } = editor.removeSelectedCell();
1685
+ const selectedCellNewValues = selectedCells.map((item) => editor.getCellOriginValue(item.col, item.row));
1686
+ const data = payload.map((item, index) => {
1687
+ return {
1688
+ ...item,
1689
+ current: selectedCellNewValues[index]
1690
+ };
1691
+ });
1692
+ editor.dispatch("cells::update", {
1693
+ editor,
1694
+ data
1695
+ });
1696
+ return {
1697
+ restore() {
1698
+ const payload2 = selectedCells.map((item) => {
1699
+ return {
1700
+ ...editor.getInteractiveCell(item.col, item.row),
1701
+ previous: editor.getCellOriginValue(item.col, item.row)
1702
+ };
1703
+ });
1704
+ editor.updateRecordsData(recordsForRestore);
1705
+ const selectedCellNewValues2 = selectedCells.map((item) => editor.getCellOriginValue(item.col, item.row));
1706
+ const data2 = payload2.map((item, index) => {
1707
+ return {
1708
+ ...item,
1709
+ current: selectedCellNewValues2[index]
1710
+ };
1711
+ });
1712
+ editor.dispatch("cells::update", {
1713
+ editor,
1714
+ data: data2
1715
+ });
1716
+ }
1717
+ };
1718
+ };
1719
+ const command = new EditTableCellDataFunctionalCommand(editor, run);
1720
+ editor.commandManager.execute(command);
1721
+ },
1722
+ [],
1723
+ {
1724
+ enabled: true,
1725
+ enableOnContentEditable: false
1726
+ }
1727
+ );
1728
+ return null;
1729
+ };
1730
+
1731
+ const History = () => {
1732
+ const tableEditor = useActiveEditorTable();
1733
+ const commandManager = tableEditor?.commandManager;
1734
+ useHotkeys(
1735
+ "mod+z",
1736
+ () => {
1737
+ if (!commandManager) return;
1738
+ commandManager.undo();
1739
+ },
1740
+ [commandManager],
1741
+ {
1742
+ enabled: !!commandManager,
1743
+ enableOnContentEditable: false,
1744
+ enableOnFormTags: true
1745
+ }
1746
+ );
1747
+ useHotkeys(
1748
+ "mod+shift+z",
1749
+ () => {
1750
+ if (!commandManager) return;
1751
+ commandManager.redo();
1752
+ },
1753
+ [commandManager],
1754
+ {
1755
+ enabled: !!commandManager,
1756
+ enableOnContentEditable: false,
1757
+ enableOnFormTags: true
1758
+ }
1759
+ );
1760
+ return null;
1761
+ };
1762
+
1763
+ const Hotkeys = () => {
1764
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Delete, null), /* @__PURE__ */ React.createElement(History, null));
1765
+ };
1766
+
1767
+ class TableEditorEvent extends DestroyClass {
1768
+ constructor(editor, instance, container) {
1769
+ super();
1770
+ this.editor = editor;
1771
+ this.instance = instance;
1772
+ this.container = container;
1773
+ this.cancels.push(this.initEvent());
1774
+ }
1775
+ initEvent() {
1776
+ const handleContextMenuCell = (e) => {
1777
+ const event = e.event;
1778
+ const { recordId, columnId } = this.editor.getInteractiveCell(e.col, e.row);
1779
+ const columnDefine = this.instance.getBodyColumnDefine(e.col, e.row);
1780
+ if (columnDefine.disableSelect) {
1781
+ if (this.editor.debug) console.info("\u5F53\u524D\u5217\u7981\u7528\u9009\u4E2D\uFF0C\u65E0\u6CD5\u89E6\u53D1\u4E0A\u4E0B\u6587\u83DC\u5355", e, columnDefine);
1782
+ return;
1783
+ }
1784
+ const containerRect = this.instance.getContainer().getBoundingClientRect();
1785
+ const rect = this.instance.getCellRelativeRect(e.col, e.row);
1786
+ rect.left += containerRect.left;
1787
+ rect.top += containerRect.top;
1788
+ rect.right += containerRect.left;
1789
+ rect.bottom += containerRect.top;
1790
+ const isHeader = e.row === 0;
1791
+ const position = !isHeader ? { x: event.clientX, y: event.clientY } : { x: rect.left, y: rect.bottom + 1 };
1792
+ setMenu({
1793
+ open: true,
1794
+ type: isHeader ? "header" : "cell",
1795
+ position,
1796
+ cell: { col: e.col, row: e.row, recordId, columnId, rect }
1797
+ });
1798
+ };
1799
+ const hideMenu = (e) => {
1800
+ if (e.target.closest(".menu-container")) return;
1801
+ else setMenu(null);
1802
+ };
1803
+ const handleContextMenu = (e) => {
1804
+ e.preventDefault();
1805
+ e.stopPropagation();
1806
+ };
1807
+ const dbClickHandler = ({ col, row }) => {
1808
+ const columnDefine = this.instance.getBodyColumnDefine(col, row);
1809
+ const cellValue = this.instance.getCellValue(col, row);
1810
+ if (this.editor.debug) {
1811
+ console.group("\u53CC\u51FB\u5355\u5143\u683C\u4E8B\u4EF6");
1812
+ console.log("\u5F53\u524D\u5355\u5143\u683C\u9501\u5B9A&loading\u72B6\u6001:", {
1813
+ editor: this.editor.lock,
1814
+ column: columnDefine.lock,
1815
+ loading: cellValue?.loading
1816
+ });
1817
+ console.log("\u5F53\u524D\u5355\u5143\u683C\u5DF2\u9501\u5B9A\u6216\u6B63\u5728\u52A0\u8F7D\u4E2D\uFF0C\u65E0\u6CD5\u7F16\u8F91");
1818
+ console.groupEnd();
1819
+ }
1820
+ if (this.editor.lock || columnDefine?.lock || cellValue?.loading) return;
1821
+ this.instance.startEditCell(col, row);
1822
+ };
1823
+ const cellClickHandler = ({ col, row }) => {
1824
+ const columnDefine = this.instance.getBodyColumnDefine(col, row);
1825
+ const record = this.instance.getRecordByCell(col, row);
1826
+ const recordData = record ? revertRecordStructure(record) : void 0;
1827
+ const cellValue = this.instance.getCellValue(col, row);
1828
+ if (this.editor.debug) {
1829
+ console.group("\u5355\u51FB\u5355\u5143\u683C\u4E8B\u4EF6");
1830
+ console.log("\u5F53\u524D\u5217\u914D\u7F6E:", columnDefine);
1831
+ console.log("\u5355\u5143\u683C\u6570\u636E:", cellValue);
1832
+ console.log("\u5F53\u524D\u884C\u6570\u636E:", recordData);
1833
+ console.log("\u5F53\u524D\u5355\u5143\u683C\u9501\u5B9A\u72B6\u6001:", {
1834
+ editor: this.editor.lock,
1835
+ column: columnDefine.lock
1836
+ });
1837
+ console.log("\u5F53\u524D\u8868\u683C\u6570\u636E\u7BA1\u7406\u5668:", this.editor.recordsManager);
1838
+ console.groupEnd();
1839
+ }
1840
+ this.editor.dispatch("cell::click", {
1841
+ editor: this.editor,
1842
+ columnDefine,
1843
+ record,
1844
+ cellValue
1845
+ });
1846
+ };
1847
+ const changeHeaderPositionHandler = () => {
1848
+ this.editor.dispatch("column::drag::move::end", {
1849
+ editor: this.editor,
1850
+ columnIds: this.editor.columnIds.filter((item) => !INNER_TABLE_COLUMN_KEY_LIST.includes(item))
1851
+ });
1852
+ };
1853
+ const resizeColumnEndHandler = ({ col, colWidths }) => {
1854
+ const define = this.editor.getColumnDefine(col);
1855
+ if (INNER_TABLE_COLUMN_KEY_LIST.includes(define.field)) return;
1856
+ this.editor.dispatch("column::width::update", {
1857
+ editor: this.editor,
1858
+ columnId: define.field,
1859
+ width: colWidths[col]
1860
+ });
1861
+ };
1862
+ const checkboxStateChangeHandler = (event) => {
1863
+ const { field } = event;
1864
+ if (field != INNER_TABLE_COLUMN_KEY_CHECKBOX) return;
1865
+ const recordIds = this.editor.interactive.selectedRecords;
1866
+ this.editor.dispatch("record::checkbox::state::change", {
1867
+ editor: this.editor,
1868
+ selectedRecordIds: recordIds
1869
+ });
1870
+ };
1871
+ this.instance.on("contextmenu_cell", handleContextMenuCell);
1872
+ this.instance.on("dblclick_cell", dbClickHandler);
1873
+ this.instance.on("click_cell", cellClickHandler);
1874
+ this.instance.on("change_header_position", changeHeaderPositionHandler);
1875
+ this.instance.on("resize_column_end", resizeColumnEndHandler);
1876
+ this.instance.on("checkbox_state_change", checkboxStateChangeHandler);
1877
+ const parent = this.container.parentElement;
1878
+ parent.addEventListener("contextmenu", handleContextMenu);
1879
+ window.addEventListener("click", hideMenu);
1880
+ return () => {
1881
+ this.instance.off("contextmenu_cell", handleContextMenuCell);
1882
+ this.instance.off("dblclick_cell", dbClickHandler);
1883
+ this.instance.off("click_cell", cellClickHandler);
1884
+ this.instance.off("change_header_position", changeHeaderPositionHandler);
1885
+ this.instance.off("resize_column_end", resizeColumnEndHandler);
1886
+ this.instance.off("checkbox_state_change", checkboxStateChangeHandler);
1887
+ parent.removeEventListener("contextmenu", handleContextMenu);
1888
+ window.removeEventListener("click", hideMenu);
1889
+ this.instance.eventManager.release();
1890
+ };
1891
+ }
1892
+ destroy() {
1893
+ super.destroy();
1894
+ }
1895
+ }
1896
+
1897
+ class BaseTableInteractive {
1898
+ constructor(editor) {
1899
+ this.editor = editor;
1900
+ }
1901
+ get selectedRecords() {
1902
+ const records = this.editor.getSelectedRecord();
1903
+ return records.map((record) => record[INNER_TABLE_RECORD_ID_KEY]);
1904
+ }
1905
+ get selectedCells() {
1906
+ const selectedCells = this.editor.getSelectedCell() ?? [];
1907
+ const cellGroups = selectedCells.filter((group) => group[0].row !== 0);
1908
+ const cells = cellGroups.flat();
1909
+ return cells.map((item) => {
1910
+ const { col, row } = item;
1911
+ const { columnId, recordId } = this.editor.getInteractiveCell(col, row);
1912
+ return {
1913
+ cell: { columnId, recordId },
1914
+ data: item.value
1915
+ };
1916
+ });
1917
+ }
1918
+ getRecordsManagerStore() {
1919
+ return this.editor.recordsManager.store;
1920
+ }
1921
+ filters(recordIds) {
1922
+ this.editor.recordsManager.filters = recordIds;
1923
+ }
1924
+ selectCell(cell) {
1925
+ const { col, row } = this.editor.getInteractiveCellPosition(cell);
1926
+ this.editor.selectCell(col, row);
1927
+ }
1928
+ selectColumnByIndex(columnIndex) {
1929
+ const col = getColFromColIndex(columnIndex, this.editor.innerColumnControl);
1930
+ this.editor.instance.selectCol(col);
1931
+ }
1932
+ selectRowByIndex(recordIndex) {
1933
+ const row = getRowFromRowIndex(recordIndex);
1934
+ this.editor.instance.selectRow(row);
1935
+ }
1936
+ selectCellByIndex(cell) {
1937
+ const [recordIndex, colIndex] = cell;
1938
+ const col = getColFromColIndex(colIndex, this.editor.innerColumnControl);
1939
+ const row = getRowFromRowIndex(recordIndex);
1940
+ this.editor.selectCell(col, row);
1941
+ }
1942
+ clearSelect() {
1943
+ this.editor.instance.clearSelected();
1944
+ }
1945
+ setRecordsSelectState(records, state = true) {
1946
+ const columnIndex = 0;
1947
+ const columnDefine = this.editor.getColumnDefine(0);
1948
+ if (columnDefine.field !== INNER_TABLE_COLUMN_KEY_CHECKBOX) throw new Error("\u5F53\u524D\u8868\u683C\u7B2C\u4E00\u5217\u4E0D\u662F\u590D\u9009\u6846\u5217");
1949
+ if (records === "all") {
1950
+ const rowCount = this.editor.rowCount;
1951
+ for (let row = 0; row < rowCount; row++) {
1952
+ this.editor.setCheckboxState(columnIndex, row, state);
1953
+ }
1954
+ } else {
1955
+ const cellPositions = records.map(
1956
+ (item) => this.editor.getInteractiveCellPosition({ recordId: item, columnId: INNER_TABLE_COLUMN_KEY_CHECKBOX })
1957
+ );
1958
+ cellPositions.forEach((item) => this.editor.setCheckboxState(columnIndex, item.row, state));
1959
+ }
1960
+ }
1961
+ getRecord(recordId) {
1962
+ const { row } = this.editor.getInteractiveCellPosition({ recordId, columnId: INNER_TABLE_COLUMN_KEY_CHECKBOX });
1963
+ const data = this.editor.getRecordByCell(0, row);
1964
+ const record = data ? revertRecordStructure(data) : void 0;
1965
+ return record;
1966
+ }
1967
+ getRecords(recordIds) {
1968
+ const records = recordIds.map((item) => this.getRecord(item));
1969
+ return records;
1970
+ }
1971
+ updateRecords(records) {
1972
+ this.editor.recordsManager.updateRecords(records);
1973
+ }
1974
+ addRecords(_records) {
1975
+ this.editor.recordsManager.addRecords(_records);
1976
+ }
1977
+ removeRecords(_recordIds) {
1978
+ this.editor.recordsManager.removeRecords(_recordIds);
1979
+ }
1980
+ focus(cell) {
1981
+ const { col, row } = this.editor.getInteractiveCellPosition(cell);
1982
+ this.editor.focusCell(col, row);
1983
+ }
1984
+ focusByIndex(cell) {
1985
+ const [recordIndex, colIndex] = cell;
1986
+ const col = getColFromColIndex(colIndex, this.editor.innerColumnControl);
1987
+ const row = getRowFromRowIndex(recordIndex);
1988
+ this.editor.focusCell(col, row);
1989
+ }
1990
+ highlight(cells, _key = CUSTOM_CELL_STYLE_HIGHLIGHT_FOCUS) {
1991
+ const positions = cells.map((cell) => this.editor.getInteractiveCellPosition(cell));
1992
+ this.editor.highlightCells(_key, positions);
1993
+ }
1994
+ highlightByIndex(cells, _key = CUSTOM_CELL_STYLE_HIGHLIGHT_FOCUS) {
1995
+ const positions = cells.map(([recordIndex, colIndex]) => ({
1996
+ row: getRowFromRowIndex(recordIndex),
1997
+ col: getColFromColIndex(colIndex, this.editor.innerColumnControl)
1998
+ }));
1999
+ this.editor.highlightCells(_key, positions);
2000
+ }
2001
+ clearHighlight(key = CUSTOM_CELL_STYLE_HIGHLIGHT_FOCUS) {
2002
+ this.editor.clearHighlight(key);
2003
+ }
2004
+ // 冻结列的数量,此处count是用户可感知列的数量
2005
+ setFreezeCount(count) {
2006
+ const { checkbox } = this.editor.innerColumnControl;
2007
+ const realCount = count + (checkbox ? 1 : 0);
2008
+ this.editor.setFreezeCount(realCount);
2009
+ }
2010
+ }
2011
+
2012
+ class RecordsManager extends DestroyClass {
2013
+ constructor(editor) {
2014
+ super();
2015
+ this.editor = editor;
2016
+ this.recordsMap = atom(/* @__PURE__ */ new Map());
2017
+ this._filters = atom(void 0);
2018
+ /**
2019
+ * 当前表格records数量
2020
+ *
2021
+ * 如果filters不存在,直接返回 recordsMap 数量,
2022
+ *
2023
+ * 如果filters存在,则返回 filters 中存在的 records 数量
2024
+ */
2025
+ this.size = atom((get) => {
2026
+ const records = get(this.recordsMap);
2027
+ const filters = get(this._filters);
2028
+ let count = 0;
2029
+ if (isNil(filters)) count = records.size;
2030
+ else {
2031
+ count = filters.map((recordId) => records.get(recordId)).filter(Boolean).length;
2032
+ }
2033
+ return count;
2034
+ });
2035
+ }
2036
+ get filters() {
2037
+ return window.store.get(this._filters);
2038
+ }
2039
+ set filters(value) {
2040
+ window.store.set(this._filters, value);
2041
+ this.renderRecords();
2042
+ }
2043
+ initRecords(records) {
2044
+ const recordsMap = window.store.get(this.recordsMap);
2045
+ records.forEach((item) => recordsMap.set(item.INNER_PRESET_RECORD_ID, item));
2046
+ window.store.set(this.recordsMap, new Map(recordsMap));
2047
+ }
2048
+ renderRecords() {
2049
+ const recordsMap = window.store.get(this.recordsMap);
2050
+ const records = this.filters ? this.filters.map((filter) => recordsMap.get(filter)) : [...recordsMap.values()];
2051
+ const data = records.filter(Boolean);
2052
+ this.editor.setRecords(data);
2053
+ }
2054
+ updateRecords(records) {
2055
+ const recordsMap = window.store.get(this.recordsMap);
2056
+ records.forEach((item) => {
2057
+ const adaptedRecord = adaptRecordStructure(item);
2058
+ recordsMap.set(adaptedRecord[INNER_TABLE_RECORD_ID_KEY], adaptedRecord);
2059
+ });
2060
+ const ids = records.map((item) => item.id);
2061
+ const values = [...recordsMap.values()];
2062
+ const data = values.map((item, index) => {
2063
+ return ids.includes(item.INNER_PRESET_RECORD_ID) ? [item, index] : void 0;
2064
+ }).filter(Boolean);
2065
+ this.editor.instance.updateRecords(
2066
+ data.map(([record]) => record),
2067
+ data.map(([, rowIndex]) => rowIndex)
2068
+ );
2069
+ window.store.set(this.recordsMap, new Map(recordsMap));
2070
+ }
2071
+ addRecords(records) {
2072
+ const recordsMap = window.store.get(this.recordsMap);
2073
+ records.forEach((item) => {
2074
+ const adaptedRecord = adaptRecordStructure(item);
2075
+ if (recordsMap.has(adaptedRecord[INNER_TABLE_RECORD_ID_KEY])) {
2076
+ throw new Error("record id already exists " + adaptedRecord[INNER_TABLE_RECORD_ID_KEY]);
2077
+ }
2078
+ recordsMap.set(adaptedRecord[INNER_TABLE_RECORD_ID_KEY], adaptedRecord);
2079
+ });
2080
+ const recordsAdapted = records.map((item) => recordsMap.get(item.id)).filter(Boolean);
2081
+ this.editor.addRecords(recordsAdapted);
2082
+ window.store.set(this.recordsMap, new Map(recordsMap));
2083
+ }
2084
+ removeRecords(recordIds) {
2085
+ const recordsMap = window.store.get(this.recordsMap);
2086
+ recordIds.forEach((id) => recordsMap.delete(id));
2087
+ const indexes = recordIds.map((id) => this.editor.getInteractiveRecordIndex(id)).filter((i) => i !== -1);
2088
+ this.editor.deleteRecords(indexes);
2089
+ window.store.set(this.recordsMap, new Map(recordsMap));
2090
+ }
2091
+ get store() {
2092
+ return window.store.get(this.recordsMap);
2093
+ }
2094
+ get(recordId) {
2095
+ const recordsMap = window.store.get(this.recordsMap);
2096
+ return recordsMap.get(recordId);
2097
+ }
2098
+ set(recordId, record) {
2099
+ const recordsMap = window.store.get(this.recordsMap);
2100
+ recordsMap.set(recordId, record);
2101
+ window.store.set(this.recordsMap, new Map(recordsMap));
2102
+ }
2103
+ }
2104
+
2105
+ const createHeaderGraphics = (type, props) => {
2106
+ const { table, row, col, rect, value } = props;
2107
+ const headerStyle = table._getCellStyle(col, row);
2108
+ const columnDefine = table.getBodyColumnDefine(col, row);
2109
+ const lock = !!columnDefine.lock;
2110
+ const functionalFontSize = getProp("fontSize", headerStyle, col, row, table);
2111
+ const functionalColor = getProp("color", headerStyle, col, row, table);
2112
+ const { height, width } = rect ?? table.getCellRect(col, row);
2113
+ const headerIconSize = getIconSizeFromFont(functionalFontSize);
2114
+ const g = createGroup({
2115
+ width,
2116
+ height,
2117
+ display: "flex",
2118
+ flexWrap: "nowrap",
2119
+ justifyContent: "space-between",
2120
+ alignContent: "center",
2121
+ alignItems: "center"
2122
+ // 垂直居中
2123
+ });
2124
+ const lg = createGroup({
2125
+ width: width - 30,
2126
+ height,
2127
+ display: "flex",
2128
+ flexWrap: "nowrap",
2129
+ justifyContent: "flex-start",
2130
+ alignContent: "center",
2131
+ alignItems: "center"
2132
+ });
2133
+ const rg = createGroup({
2134
+ display: "flex",
2135
+ width: 30,
2136
+ height,
2137
+ alignContent: "center",
2138
+ alignItems: "center"
2139
+ });
2140
+ const arrowIcon = createIconGraphics("arrow_down", {
2141
+ size: headerIconSize,
2142
+ color: functionalColor,
2143
+ cursor: "pointer",
2144
+ hoverBg: true
2145
+ });
2146
+ rg.add(arrowIcon);
2147
+ const toolContainer = createGroup({
2148
+ display: "flex",
2149
+ width: 30,
2150
+ height,
2151
+ alignContent: "center",
2152
+ alignItems: "center"
2153
+ });
2154
+ const toolIcon = createIconGraphics(lock ? "lock" : TOOL_ICON[type], {
2155
+ size: headerIconSize,
2156
+ color: functionalColor
2157
+ });
2158
+ toolContainer.add(toolIcon);
2159
+ const titleContainer = createGroup({
2160
+ display: "flex",
2161
+ width: width - 60,
2162
+ height,
2163
+ alignContent: "center",
2164
+ alignItems: "center"
2165
+ });
2166
+ const title = createText({
2167
+ text: value,
2168
+ dy: -functionalFontSize / 2,
2169
+ fontSize: functionalFontSize,
2170
+ fill: functionalColor,
2171
+ lineHeight: height,
2172
+ maxLineWidth: width - 30,
2173
+ textAlign: "left",
2174
+ textBaseline: "middle"
2175
+ });
2176
+ titleContainer.add(title);
2177
+ lg.add(toolContainer);
2178
+ lg.add(titleContainer);
2179
+ g.add(lg);
2180
+ g.add(rg);
2181
+ arrowIcon.addEventListener("click", () => {
2182
+ });
2183
+ return g;
2184
+ };
2185
+
2186
+ const isColumnEditable = (table, col) => {
2187
+ const column = table.getBodyColumnDefine(col, 0);
2188
+ const lock = column.lock;
2189
+ return !lock;
2190
+ };
2191
+ const createDefaultColumnConfig = () => {
2192
+ return {
2193
+ field: getUUID(),
2194
+ title: "\u6587\u672C",
2195
+ tool: 28,
2196
+ // TODO: 改成自定义的文本输入组件
2197
+ editor: "textEditor",
2198
+ headerCustomLayout: (args) => {
2199
+ return {
2200
+ rootContainer: createHeaderGraphics(28, args),
2201
+ renderDefault: false
2202
+ };
2203
+ }
2204
+ };
2205
+ };
2206
+
2207
+ class TableEditor extends DestroyClass {
2208
+ constructor(id, container, onChange, config, commandManager, uploadFile, innerColumnControl) {
2209
+ super();
2210
+ this.id = id;
2211
+ this.container = container;
2212
+ this.commandManager = commandManager;
2213
+ this.uploadFile = uploadFile;
2214
+ this.innerColumnControl = innerColumnControl;
2215
+ this._lock = atom(false);
2216
+ this.customCellStyleArrangement = /* @__PURE__ */ new Map();
2217
+ this.recordsManager = new RecordsManager(this);
2218
+ /**
2219
+ * 当前是否处于debug状态,一般开发的时候为true,会打印一些需要调试的日志
2220
+ */
2221
+ this.debug = false;
2222
+ /**
2223
+ * 供外部调用的接口
2224
+ */
2225
+ this.interactive = new BaseTableInteractive(this);
2226
+ this.dispatch = onChange;
2227
+ const editorMap = getEditorTableStore();
2228
+ const editor = editorMap.get(this.id);
2229
+ if (editor) throw new Error("TableEditor \u5DF2\u5B58\u5728");
2230
+ this.instance = new ListTable(container, config);
2231
+ editorMap.set(id, this);
2232
+ setEditorTableStore(new Map([...editorMap]));
2233
+ this.event = new TableEditorEvent(this, this.instance, this.container);
2234
+ this.recordsManager.initRecords(this.instance.records);
2235
+ this.load();
2236
+ }
2237
+ get lock() {
2238
+ return window.store.get(this._lock);
2239
+ }
2240
+ set lock(value) {
2241
+ window.store.set(this._lock, value);
2242
+ }
2243
+ get colCount() {
2244
+ return this.instance.colCount;
2245
+ }
2246
+ get rowCount() {
2247
+ return this.instance.rowCount;
2248
+ }
2249
+ /**
2250
+ * 用于限定列的操作范围
2251
+ * 前两列 0 1 是关键主列,不进行任何操作,并且顺序永远在最前面
2252
+ * 最后两列 colCount - 1:Plus列, colCount - 2:more列 同样是固定列
2253
+ */
2254
+ get colActionLimit() {
2255
+ return {
2256
+ min: 2,
2257
+ max: this.instance.colCount - 2
2258
+ };
2259
+ }
2260
+ get columnIds() {
2261
+ return this.columns.map((item) => item.field);
2262
+ }
2263
+ get columns() {
2264
+ return this.instance.columns;
2265
+ }
2266
+ updateCellData(col, row, value) {
2267
+ const { field } = this.getColumnDefine(col);
2268
+ const record = this.instance.getRecordByCell(col, row);
2269
+ const recordId = record[INNER_TABLE_RECORD_ID_KEY];
2270
+ const recordData = this.recordsManager.get(recordId);
2271
+ if (!recordData) throw new Error(`[updateCellData] recordId ${recordId} \u4E0D\u5B58\u5728`);
2272
+ recordData[field] = value;
2273
+ this.updateCell(col, row, value);
2274
+ }
2275
+ getRecordByCell(col, row) {
2276
+ return this.instance.getRecordByCell(col, row);
2277
+ }
2278
+ getInteractiveCell(col, row) {
2279
+ const record = this.instance.getRecordByCell(col, row) ?? {};
2280
+ const columnDefine = this.instance.getBodyColumnDefine(col, row);
2281
+ const recordId = record[INNER_TABLE_RECORD_ID_KEY];
2282
+ const columnId = columnDefine.field;
2283
+ return { columnId, recordId };
2284
+ }
2285
+ getInteractiveRecordIndex(recordId) {
2286
+ return this.instance.records.findIndex((record) => record[INNER_TABLE_RECORD_ID_KEY] === recordId);
2287
+ }
2288
+ getInteractiveCellPosition(cell) {
2289
+ const { recordId, columnId } = cell;
2290
+ const fields = this.instance.columns.map((item) => item.field);
2291
+ const rowIndex = this.instance.records.findIndex((record) => record[INNER_TABLE_RECORD_ID_KEY] === recordId);
2292
+ const col = fields.findIndex((field) => field === columnId);
2293
+ return { col, row: getRowFromRowIndex(rowIndex) };
2294
+ }
2295
+ updateColumns(columns) {
2296
+ this.instance.updateColumns(columns);
2297
+ }
2298
+ getSelectedRecord() {
2299
+ const selected = this.instance.getCheckboxState(INNER_TABLE_COLUMN_KEY_CHECKBOX);
2300
+ return this.instance.records.filter((_, index) => selected[index]);
2301
+ }
2302
+ setCheckboxState(col, row, state) {
2303
+ this.instance.setCellCheckboxState(col, row, state);
2304
+ }
2305
+ getColumnDefine(col) {
2306
+ return this.instance.getBodyColumnDefine(col, 0);
2307
+ }
2308
+ getSelectedCell() {
2309
+ return this.instance.getSelectedCellInfos();
2310
+ }
2311
+ focusCell(col, row) {
2312
+ this.instance.scrollToCell({ col, row });
2313
+ }
2314
+ highlightCells(customCellStyleKey, cells) {
2315
+ this.clearHighlight(customCellStyleKey);
2316
+ cells.forEach((cell) => this.instance.arrangeCustomCellStyle(cell, customCellStyleKey));
2317
+ this.customCellStyleArrangement.set(customCellStyleKey, cells);
2318
+ }
2319
+ clearHighlight(customCellStyleKey) {
2320
+ if (!customCellStyleKey) {
2321
+ this.instance.arrangeCustomCellStyle(
2322
+ {
2323
+ range: {
2324
+ start: { col: 0, row: 0 },
2325
+ end: { col: this.instance.colCount - 1, row: this.instance.rowCount - 1 }
2326
+ }
2327
+ },
2328
+ "",
2329
+ true
2330
+ );
2331
+ this.customCellStyleArrangement.clear();
2332
+ } else {
2333
+ const cells = this.customCellStyleArrangement.get(customCellStyleKey) || [];
2334
+ cells.forEach((cell) => this.instance.arrangeCustomCellStyle(cell, ""));
2335
+ this.customCellStyleArrangement.delete(customCellStyleKey);
2336
+ }
2337
+ }
2338
+ updateColumn(col, options) {
2339
+ const newColumns = [...this.instance.columns];
2340
+ newColumns[col] = {
2341
+ ...newColumns[col],
2342
+ ...options
2343
+ };
2344
+ this.instance.updateColumns(newColumns);
2345
+ }
2346
+ setRecords(_records) {
2347
+ this.instance.setRecords(_records, { sortState: null });
2348
+ }
2349
+ addRecords(_records) {
2350
+ this.instance.addRecords(_records);
2351
+ }
2352
+ selectCell(col, row) {
2353
+ this.instance.selectCell(col, row);
2354
+ }
2355
+ /**
2356
+ * 删除选中单元格
2357
+ * 1. 过滤表头行
2358
+ * 2. 只修改没有被锁定的列内容
2359
+ */
2360
+ removeSelectedCell() {
2361
+ const selectedCells = this.getSelectedCell() ?? [];
2362
+ const cellGroups = selectedCells.filter((group) => group[0].row !== 0);
2363
+ const rows = cellGroups.map((cells) => this.instance.getRecordShowIndexByCell(cells[0].col, cells[0].row));
2364
+ const row1 = cellGroups[0];
2365
+ const cols = row1.map((cell) => cell.col).filter((col) => isColumnEditable(this.instance, col));
2366
+ const fields = cols.map((col) => this.instance.columns[col].field).filter(Boolean);
2367
+ const recordsForRestore = rows.map((row) => [
2368
+ structuredClone(this.instance.records[row]),
2369
+ row
2370
+ ]);
2371
+ const records = rows.map((row) => {
2372
+ const record = structuredClone(this.instance.records[row]);
2373
+ fields.forEach((field) => record[field].value = void 0);
2374
+ return [record, row];
2375
+ });
2376
+ this.instance.updateRecords(
2377
+ records.map(([record]) => record),
2378
+ records.map(([, row]) => row)
2379
+ );
2380
+ return {
2381
+ records,
2382
+ recordsForRestore
2383
+ };
2384
+ }
2385
+ /**
2386
+ * 添加数据 支持多条数据
2387
+ * @param records 多条数据
2388
+ * @param recordIndex 向数据源中要插入的位置,从0开始。不设置recordIndex的话 默认追加到最后。
2389
+ * 如果设置了排序规则recordIndex无效,会自动适应排序逻辑确定插入顺序。
2390
+ * recordIndex 可以通过接口getRecordShowIndexByCell获取
2391
+ */
2392
+ createRows(count, recordBodyIndex) {
2393
+ const index = recordBodyIndex ?? this.instance.recordsCount;
2394
+ const columns = this.instance.columns.map((item) => [item.field, item.defaultValue ?? void 0]);
2395
+ const defaultRecord = Object.fromEntries(columns);
2396
+ const newRecords = Array.from({ length: count }).map(() => clone(defaultRecord));
2397
+ this.instance.addRecords([...newRecords], index);
2398
+ const indexes = [];
2399
+ for (let i = 0; i < count; i++) {
2400
+ indexes.push(index + i);
2401
+ }
2402
+ return {
2403
+ newRecords,
2404
+ newRecordIndexes: indexes
2405
+ };
2406
+ }
2407
+ deleteRecords(recordIndexes) {
2408
+ this.instance.deleteRecords(recordIndexes);
2409
+ }
2410
+ /**
2411
+ * 删除行
2412
+ */
2413
+ removeRow(count, recordBodyIndex) {
2414
+ if (this.lock) throw new Error("\u9501\u5B9A\u8868\u683C\uFF0C\u4E0D\u5141\u8BB8\u5220\u9664\u884C");
2415
+ const index = recordBodyIndex;
2416
+ const indexes = [];
2417
+ for (let i = 0; i < count; i++) {
2418
+ indexes.push(index + i);
2419
+ }
2420
+ const records = structuredClone(indexes.map((index2) => this.instance.records[index2]));
2421
+ this.instance.deleteRecords(clone(indexes));
2422
+ return {
2423
+ oldRecords: records,
2424
+ oldRecordIndexes: indexes
2425
+ };
2426
+ }
2427
+ /**
2428
+ * 新增默认列
2429
+ * @param count 新增列数
2430
+ * @param insertIndex 新增列索引,默认在最后一列前新增
2431
+ */
2432
+ createColumn(count, insertIndex) {
2433
+ if (this.lock) throw new Error("\u9501\u5B9A\u8868\u683C\uFF0C\u4E0D\u5141\u8BB8\u65B0\u589E\u5217");
2434
+ const columns = [...this.instance.columns];
2435
+ const newColumns = [...this.instance.columns];
2436
+ const index = clamp(insertIndex, this.colActionLimit.min, this.colActionLimit.max);
2437
+ const createdColumns = Array.from({ length: count }).map(() => createDefaultColumnConfig());
2438
+ newColumns.splice(index, 0, ...createdColumns);
2439
+ this.instance.updateColumns(newColumns);
2440
+ return {
2441
+ oldColumns: columns,
2442
+ newColumns
2443
+ };
2444
+ }
2445
+ /**
2446
+ * 删除列
2447
+ */
2448
+ removeColumn(count, startIndex) {
2449
+ if (this.lock) throw new Error("\u9501\u5B9A\u8868\u683C\uFF0C\u4E0D\u5141\u8BB8\u5220\u9664\u5217");
2450
+ const oldColumns = [...this.instance.columns];
2451
+ const newColumns = [...this.instance.columns];
2452
+ const min = this.colActionLimit.min;
2453
+ const max = this.colActionLimit.max;
2454
+ const plannedIndexes = Array.from({ length: count }, (_, i) => startIndex + i);
2455
+ const inRangeIndexes = plannedIndexes.filter((i) => i >= min && i <= max);
2456
+ if (inRangeIndexes.length === 0) {
2457
+ console.warn(
2458
+ `[removeColumn] \u6CA1\u6709\u4EFB\u4F55\u5217\u843D\u5728\u53EF\u64CD\u4F5C\u8303\u56F4\u5185\u3002startIndex=${startIndex}, count=${count}, \u8303\u56F4=[${min}, ${max}]`
2459
+ );
2460
+ return;
2461
+ }
2462
+ const removableIndexes = inRangeIndexes.filter((i) => isColumnEditable(this.instance, i));
2463
+ const notEditableIndexes = inRangeIndexes.filter((i) => !isColumnEditable(this.instance, i));
2464
+ if (notEditableIndexes.length > 0) {
2465
+ console.warn(`[removeColumn] \u4EE5\u4E0B\u5217\u88AB\u9501\u5B9A\uFF0C\u4E0D\u53C2\u4E0E\u5220\u9664: ${notEditableIndexes.join(", ")}`);
2466
+ }
2467
+ if (removableIndexes.length === 0) {
2468
+ console.warn(`[removeColumn] \u53EF\u64CD\u4F5C\u8303\u56F4\u5185\u7684\u5217\u5747\u4E0D\u53EF\u5220\u9664\u3002startIndex=${startIndex}, count=${count}`);
2469
+ return;
2470
+ }
2471
+ removableIndexes.sort((a, b) => b - a);
2472
+ removableIndexes.forEach((index) => {
2473
+ newColumns.splice(index, 1);
2474
+ });
2475
+ this.instance.updateColumns(newColumns);
2476
+ return {
2477
+ oldColumns,
2478
+ newColumns
2479
+ };
2480
+ }
2481
+ /**
2482
+ * 复制列
2483
+ * @param sourceIndex 要复制的列索引
2484
+ * @param insertIndex 插入位置,默认在源列之后
2485
+ */
2486
+ copyColumn(sourceIndex, insertIndex = sourceIndex + 1) {
2487
+ if (this.lock) throw new Error("\u9501\u5B9A\u8868\u683C\uFF0C\u4E0D\u5141\u8BB8\u590D\u5236\u5217");
2488
+ const oldColumns = [...this.instance.columns];
2489
+ const newColumns = [...this.instance.columns];
2490
+ const sourceColumn = oldColumns[sourceIndex];
2491
+ const newColumn = clone(sourceColumn);
2492
+ newColumn.field = getUUID();
2493
+ newColumn.title = `${newColumn.title} \u590D\u5236`;
2494
+ newColumns.splice(insertIndex, 0, newColumn);
2495
+ this.instance.updateColumns(newColumns);
2496
+ return {
2497
+ oldColumns,
2498
+ newColumns
2499
+ };
2500
+ }
2501
+ /**
2502
+ * 冻结列
2503
+ * @param count 要冻结的列数
2504
+ * 注意:count >= 2,否则会将关键主列于checkbox拆分
2505
+ */
2506
+ setFreezeCount(count) {
2507
+ if (this.lock) throw new Error("\u9501\u5B9A\u8868\u683C\uFF0C\u4E0D\u5141\u8BB8\u51BB\u7ED3\u5217");
2508
+ const oldFrozenColCount = this.instance.frozenColCount;
2509
+ const newFrozenColCount = count;
2510
+ this.instance.setFrozenColCount(count);
2511
+ return {
2512
+ oldValue: oldFrozenColCount,
2513
+ newValue: newFrozenColCount
2514
+ };
2515
+ }
2516
+ getCellOriginValue(col, row) {
2517
+ return this.instance.getCellOriginValue(col, row);
2518
+ }
2519
+ updateCell(col, row, value) {
2520
+ if (this.lock) throw new Error("\u9501\u5B9A\u8868\u683C\uFF0C\u4E0D\u5141\u8BB8\u4FEE\u6539\u5355\u5143\u683C\u6570\u636E");
2521
+ if (!isColumnEditable(this.instance, col)) throw new Error(`\u9501\u5B9A\u5217\uFF0C\u4E0D\u5141\u8BB8\u4FEE\u6539 column: ${col}`);
2522
+ const rowIndex = this.instance.getRecordShowIndexByCell(col, row);
2523
+ const oldValue = this.instance.getCellValue(col, row);
2524
+ const field = this.instance.columns[col].field;
2525
+ const record = this.instance.records[rowIndex];
2526
+ this.instance.updateRecords([{ ...record, [field]: value }], [rowIndex]);
2527
+ return {
2528
+ record,
2529
+ rowIndex,
2530
+ oldValue,
2531
+ newValue: value
2532
+ };
2533
+ }
2534
+ updateRecordsData(records) {
2535
+ records.forEach(([item]) => {
2536
+ const recordId = item[INNER_TABLE_RECORD_ID_KEY];
2537
+ this.recordsManager.set(recordId, item);
2538
+ });
2539
+ this.instance.updateRecords(
2540
+ records.map(([record]) => record),
2541
+ records.map(([, rowIndex]) => rowIndex)
2542
+ );
2543
+ }
2544
+ getCellStyle(col, row) {
2545
+ return this.instance.getCellStyle(col, row);
2546
+ }
2547
+ load() {
2548
+ this.cancels.push(
2549
+ ...[
2550
+ () => this.event.destroy(),
2551
+ () => this.instance.release(),
2552
+ () => {
2553
+ const editorMap = getEditorTableStore();
2554
+ editorMap.delete(this.id);
2555
+ setEditorTableStore(new Map([...editorMap]));
2556
+ }
2557
+ ]
2558
+ );
2559
+ }
2560
+ unload() {
2561
+ this.cancels.forEach((cancel) => cancel());
2562
+ }
2563
+ destroy() {
2564
+ super.destroy();
2565
+ this.unload();
2566
+ }
2567
+ }
2568
+ const createBaseTableEditor = (config) => {
2569
+ const {
2570
+ id,
2571
+ onChange,
2572
+ columns,
2573
+ records,
2574
+ theme,
2575
+ commandManager,
2576
+ container,
2577
+ uploadFileFn,
2578
+ innerColumnControl,
2579
+ ...rest
2580
+ } = config;
2581
+ const defaultChangeHandler = (e, payload) => {
2582
+ console.info("\u9ED8\u8BA4\u53D8\u66F4\u5904\u7406\u51FD\u6570", e, payload);
2583
+ };
2584
+ const dispatch = onChange ?? defaultChangeHandler;
2585
+ const frozenColCount = [innerColumnControl.checkbox, innerColumnControl.main].filter(Boolean).length;
2586
+ const options = {
2587
+ columns,
2588
+ records,
2589
+ theme,
2590
+ hover: {
2591
+ highlightMode: "cross"
2592
+ },
2593
+ customCellStyle: [CUSTOM_CELL_STYLE, CUSTOM_CELL_FOCUS_STYLE, CUSTOM_CELL_FEEDBACK_ERROR_STYLE],
2594
+ dragOrder: {
2595
+ dragHeaderMode: "column",
2596
+ // 仅启用列表头拖拽换位
2597
+ validateDragOrderOnEnd: (source, target) => {
2598
+ const tableEditor = getActiveEditorTable();
2599
+ if (!tableEditor) throw new Error("\u672A\u6FC0\u6D3B\u7684\u8868\u683C\u7F16\u8F91\u5668");
2600
+ if (source.col <= 1) return false;
2601
+ if (target.col <= 1) return false;
2602
+ if (target.col >= tableEditor.colCount - 2) return false;
2603
+ return true;
2604
+ }
2605
+ },
2606
+ // 如果有checkbox,前两列默认固定 一列checkbox 一列题目名称
2607
+ // 如果没有checkbox,第一列默认固定
2608
+ frozenColCount,
2609
+ defaultColWidth: 150,
2610
+ defaultRowHeight: TABLE_DEFAULT_ROW_HEIGHT,
2611
+ enableLineBreak: true,
2612
+ heightMode: "standard",
2613
+ editCellTrigger: "api",
2614
+ showHeader: true,
2615
+ ...rest
2616
+ };
2617
+ const editor = new TableEditor(id, container, dispatch, options, commandManager, uploadFileFn, innerColumnControl);
2618
+ return editor;
2619
+ };
2620
+
2621
+ const TableView = ({}) => {
2622
+ const {
2623
+ id,
2624
+ editorRef,
2625
+ containerRef,
2626
+ records,
2627
+ columns,
2628
+ theme,
2629
+ commandManager,
2630
+ onChange,
2631
+ uploadFileFn,
2632
+ innerColumnControl,
2633
+ disabled,
2634
+ maxHeight,
2635
+ debug,
2636
+ ...rest
2637
+ } = useComponentVTable();
2638
+ const [recordsState] = useState(records);
2639
+ const innerColumnControlState = useMemo(() => {
2640
+ return {
2641
+ main: innerColumnControl.main,
2642
+ plus: innerColumnControl.plus,
2643
+ checkbox: innerColumnControl.checkbox
2644
+ };
2645
+ }, [innerColumnControl.checkbox, innerColumnControl.main, innerColumnControl.plus]);
2646
+ useEffect(() => {
2647
+ if (!editorRef.current) return;
2648
+ editorRef.current.lock = disabled;
2649
+ editorRef.current.debug = debug;
2650
+ }, [disabled, editorRef, debug]);
2651
+ const adaptedData = useMemo(() => {
2652
+ const adaptedColumns = adaptColumnStructure(columns, innerColumnControlState);
2653
+ const adaptedRecords = adaptRecordsStructure(recordsState, adaptedColumns.columns, innerColumnControlState);
2654
+ return {
2655
+ columns: adaptedColumns.columns,
2656
+ records: adaptedRecords.records,
2657
+ highlight: highlightMerge(adaptedRecords.feedbacksHighlight, adaptedColumns.feedbacksHighlight)
2658
+ };
2659
+ }, [columns, innerColumnControlState, recordsState]);
2660
+ useEffect(() => {
2661
+ if (!containerRef.current) return;
2662
+ if (editorRef.current) {
2663
+ editorRef.current.updateColumns(adaptedData.columns);
2664
+ editorRef.current.innerColumnControl = innerColumnControl;
2665
+ return;
2666
+ }
2667
+ editorRef.current = createBaseTableEditor({
2668
+ id,
2669
+ onChange,
2670
+ container: containerRef.current,
2671
+ columns: adaptedData.columns,
2672
+ records: adaptedData.records,
2673
+ theme,
2674
+ commandManager,
2675
+ uploadFileFn,
2676
+ innerColumnControl,
2677
+ ...rest
2678
+ });
2679
+ editorRef.current.lock = disabled;
2680
+ editorRef.current.debug = debug;
2681
+ return () => {
2682
+ editorRef.current?.destroy();
2683
+ editorRef.current = null;
2684
+ };
2685
+ }, []);
2686
+ useEffect(() => {
2687
+ if (!editorRef.current) return;
2688
+ editorRef.current.updateColumns(adaptedData.columns);
2689
+ }, [adaptedData.columns, editorRef]);
2690
+ useEffect(() => {
2691
+ if (!editorRef.current) return;
2692
+ const highlight = adaptedData.highlight;
2693
+ const highlightCellsGroup = Object.entries(highlight).map(([styleKey, cells]) => {
2694
+ return {
2695
+ styleKey,
2696
+ cells
2697
+ };
2698
+ });
2699
+ highlightCellsGroup.forEach((group) => {
2700
+ if (!editorRef.current) return;
2701
+ const cells = group.cells.map((cell) => {
2702
+ if (cell.type === "cell") return { row: cell.row, col: cell.col };
2703
+ else if (cell.type === "row") {
2704
+ return {
2705
+ range: {
2706
+ start: { col: 0 + 1, row: cell.row + 1 },
2707
+ end: { col: editorRef.current.colCount, row: cell.row }
2708
+ }
2709
+ };
2710
+ } else if (cell.type === "column") {
2711
+ return {
2712
+ range: {
2713
+ start: { col: cell.col, row: 0 },
2714
+ end: { col: cell.col, row: editorRef.current.rowCount }
2715
+ }
2716
+ };
2717
+ } else throw new Error(`Unknown cell type ${cell.type}`);
2718
+ });
2719
+ editorRef.current.highlightCells(group.styleKey, cells);
2720
+ });
2721
+ editorRef.current.innerColumnControl = innerColumnControlState;
2722
+ editorRef.current.updateColumns(adaptedData.columns);
2723
+ }, [editorRef, adaptedData.highlight, innerColumnControlState, adaptedData.columns]);
2724
+ const [recordCount, setRecordCount] = useState(adaptedData.records.length);
2725
+ useEffect(() => {
2726
+ if (!editorRef.current) {
2727
+ setRecordCount(0);
2728
+ return;
2729
+ }
2730
+ const sizeUpdate = () => {
2731
+ if (!editorRef.current) setRecordCount(0);
2732
+ else setRecordCount(window.store.get(editorRef.current.recordsManager.size));
2733
+ };
2734
+ const cancel = window.store.sub(editorRef.current.recordsManager.size, sizeUpdate);
2735
+ return () => cancel();
2736
+ }, [editorRef, adaptedData.records, recordCount]);
2737
+ const calcHeight = useMemo(() => {
2738
+ if (!maxHeight) return "100%";
2739
+ const calHeight = recordCount * TABLE_DEFAULT_ROW_HEIGHT + TABLE_DEFAULT_ROW_HEIGHT + 2;
2740
+ const height = max([min([calHeight, maxHeight]), TABLE_DEFAULT_ROW_HEIGHT]);
2741
+ return height;
2742
+ }, [recordCount, maxHeight]);
2743
+ return /* @__PURE__ */ React.createElement(
2744
+ "div",
2745
+ {
2746
+ contentEditable: false,
2747
+ className: `table-wrapper`,
2748
+ style: { width: "100%", height: calcHeight, position: "relative" }
2749
+ },
2750
+ /* @__PURE__ */ React.createElement("div", { className: "table-container", ref: containerRef, style: { width: "100%", height: "100%" } }),
2751
+ /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TableSelectEditor, null), /* @__PURE__ */ React.createElement(LinkEditor, null), /* @__PURE__ */ React.createElement(UserShow, null), /* @__PURE__ */ React.createElement(DateSelect, null), /* @__PURE__ */ React.createElement(TextEditor, null), /* @__PURE__ */ React.createElement(NumEditor, null), /* @__PURE__ */ React.createElement(TableJsonEditor, null)),
2752
+ /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(CellContextMenu, null), /* @__PURE__ */ React.createElement(HeaderContextMenu, null), /* @__PURE__ */ React.createElement(MoreContextMenu, null))
2753
+ );
2754
+ };
2755
+ const TableLayout = () => {
2756
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(TableView, null), /* @__PURE__ */ React.createElement(Hotkeys, null));
2757
+ };
2758
+ const Table = (props) => {
2759
+ return /* @__PURE__ */ React.createElement(VTableProvider, { ...props }, /* @__PURE__ */ React.createElement(TableLayout, null));
2760
+ };
2761
+
2762
+ const MenuItemInputLabel = ({ icon, direction, type, onCellClick, disabled }) => {
2763
+ const menu = useMenu();
2764
+ const tableEditor = useActiveEditorTable();
2765
+ if (!menu) throw new Error("menu \u5FC5\u987B\u5B58\u5728");
2766
+ if (!tableEditor) throw new Error("tableEditor \u5FC5\u987B\u5B58\u5728");
2767
+ const [inputValue, setInputValue] = useState(1);
2768
+ const directionLabel = useMemo(() => {
2769
+ if (direction === "left") return "\u5DE6";
2770
+ if (direction === "right") return "\u53F3";
2771
+ if (direction === "up") return "\u4E0A";
2772
+ if (direction === "down") return "\u4E0B";
2773
+ }, [direction]);
2774
+ const onClick = (e) => {
2775
+ onCellClick?.({ cell: menu.cell, tableEditor, e }, inputValue);
2776
+ };
2777
+ return /* @__PURE__ */ React.createElement(Flex, { align: "center", onClick, gap: 10 }, icon, /* @__PURE__ */ React.createElement(Flex, { gap: 10, style: { width: "100%" }, align: "center" }, /* @__PURE__ */ React.createElement("span", null, "\u5411", directionLabel, "\u63D2\u5165"), /* @__PURE__ */ React.createElement(
2778
+ InputNumber,
2779
+ {
2780
+ defaultValue: 1,
2781
+ disabled,
2782
+ rootClassName: "menu-item-label-input",
2783
+ onClick: (e) => {
2784
+ e.stopPropagation();
2785
+ },
2786
+ style: { width: 40 },
2787
+ value: inputValue,
2788
+ onChange: (value) => setInputValue(value || 1),
2789
+ controls: false
2790
+ }
2791
+ ), /* @__PURE__ */ React.createElement("span", null, type === "row" ? "\u884C" : "\u5217")));
2792
+ };
2793
+
2794
+ function initEditorTool(type, value) {
2795
+ switch (type) {
2796
+ case 30:
2797
+ case 29:
2798
+ initSelectEditorTool(value);
2799
+ break;
2800
+ case 33:
2801
+ initLinkEditorTool(value);
2802
+ break;
2803
+ case 36:
2804
+ initDateEditorTool(value);
2805
+ break;
2806
+ case 31:
2807
+ initNumEditorTool(value);
2808
+ break;
2809
+ case 28:
2810
+ initTextEditorTool(value);
2811
+ break;
2812
+ case 34:
2813
+ initJsonEditorTool(value);
2814
+ break;
2815
+ case 38:
2816
+ break;
2817
+ default:
2818
+ throw new Error(`[initEditorTool] \u4E0D\u652F\u6301\u7684\u5DE5\u5177\u7C7B\u578B ${type}`);
2819
+ }
2820
+ }
2821
+ const clearEditorTool = () => {
2822
+ clearSelectEditorTool();
2823
+ clearLinkEditorTool();
2824
+ clearDateEditorTool();
2825
+ clearTextEditorTool();
2826
+ clearNumEditorTool();
2827
+ clearJsonEditorTool();
2828
+ };
2829
+
2830
+ class DateSelectEditorTool {
2831
+ onStart(props) {
2832
+ const { col, row, table, value, endEdit } = props;
2833
+ this.row = row;
2834
+ this.col = col;
2835
+ this.table = table;
2836
+ this.oldValue = value;
2837
+ const rect = table.getCellRelativeRect?.(col, row);
2838
+ if (!rect) throw new Error("[DateSelectEditorTool::onStart] rect is Nil");
2839
+ initEditorTool(36, {
2840
+ cell: {
2841
+ col,
2842
+ row,
2843
+ position: { x: rect.left, y: rect.top },
2844
+ width: rect.width,
2845
+ height: rect.height
2846
+ },
2847
+ endEdit,
2848
+ table,
2849
+ initialValue: value?.value
2850
+ });
2851
+ }
2852
+ //校验
2853
+ validateValue(newValue, oldValue) {
2854
+ this.newValue = newValue;
2855
+ this.oldValue = oldValue;
2856
+ return ValidateEnum.invalidateExit;
2857
+ }
2858
+ setValue() {
2859
+ }
2860
+ // 结束编辑时触发,返回编辑后的值
2861
+ getValue() {
2862
+ return { ...this.oldValue, value: getDateEditorValue() };
2863
+ }
2864
+ // 退出编辑状态
2865
+ onEnd() {
2866
+ const tableEditor = getActiveEditorTable();
2867
+ if (isNil(this.col) || isNil(this.row)) return;
2868
+ if (!tableEditor) {
2869
+ throw new Error("[DateSelectEditorTool::onEnd] tableEditor is Nil");
2870
+ }
2871
+ const command = new EditTableCellDataCommand(tableEditor, this.col, this.row, this.newValue, this.oldValue);
2872
+ tableEditor.commandManager.execute(command);
2873
+ clearEditorTool();
2874
+ }
2875
+ isEditorElement(target) {
2876
+ return !!target.closest(".ant-picker-dropdown");
2877
+ }
2878
+ }
2879
+
2880
+ class JsonEditorTool {
2881
+ // 双击单元格触发,进入编辑状态
2882
+ onStart(props) {
2883
+ const { col, row, table, value, endEdit } = props;
2884
+ this.row = row;
2885
+ this.col = col;
2886
+ this.table = table;
2887
+ this.oldValue = value;
2888
+ const rect = table.getCellRelativeRect(col, row);
2889
+ if (!rect) throw new Error("[LinkEditor::onStart] rect is Nil");
2890
+ initEditorTool(34, {
2891
+ cell: {
2892
+ col,
2893
+ row,
2894
+ position: { x: rect.left, y: rect.top },
2895
+ width: rect.width,
2896
+ height: rect.height
2897
+ },
2898
+ endEdit,
2899
+ table,
2900
+ initialValue: value?.value
2901
+ });
2902
+ }
2903
+ validateValue(newValue, oldValue) {
2904
+ this.newValue = newValue;
2905
+ this.oldValue = oldValue;
2906
+ return ValidateEnum.invalidateExit;
2907
+ }
2908
+ // 不可删除,否则会报错
2909
+ setValue() {
2910
+ }
2911
+ // 结束编辑时触发,返回编辑后的值
2912
+ getValue() {
2913
+ return { ...this.oldValue, value: getJsonEditorValue() };
2914
+ }
2915
+ // 退出编辑状态
2916
+ onEnd() {
2917
+ if (isNil(this.col) || isNil(this.row)) return;
2918
+ const tableEditor = getActiveEditorTable();
2919
+ if (!tableEditor) throw new Error("[JsonEditor::onEnd] tableEditor is Nil");
2920
+ if (isNil(this.col) || isNil(this.row)) throw new Error("[JsonEditor::onEnd] col or row is Nil");
2921
+ const command = new EditTableCellDataCommand(tableEditor, this.col, this.row, this.newValue, this.oldValue);
2922
+ tableEditor.commandManager.execute(command);
2923
+ clearEditorTool();
2924
+ }
2925
+ /**
2926
+ * 如果提供了此函数,VTable 将会在用户点击其他地方时调用此函数。
2927
+ * 如果此函数返回了一个假值,VTable 将会调用 `onEnd` 并退出编辑状态。
2928
+ * 如果未定义此函数或此函数返回了一个真值, VTable 将不会做任何事。
2929
+ * 这意味着,你需要手动调用 `onStart` 中提供的 `endEdit` 来结束编辑模式。
2930
+ */
2931
+ isEditorElement(target) {
2932
+ return !!target.closest(".json-editor-modal-container");
2933
+ }
2934
+ }
2935
+
2936
+ class LinkEditorTool {
2937
+ // 双击单元格触发,进入编辑状态
2938
+ onStart(props) {
2939
+ const { col, row, table, value, endEdit } = props;
2940
+ this.row = row;
2941
+ this.col = col;
2942
+ this.table = table;
2943
+ this.oldValue = value;
2944
+ const rect = table.getCellRelativeRect(col, row);
2945
+ if (!rect) throw new Error("[LinkEditor::onStart] rect is Nil");
2946
+ initEditorTool(33, {
2947
+ cell: {
2948
+ col,
2949
+ row,
2950
+ position: { x: rect.left, y: rect.top },
2951
+ width: rect.width,
2952
+ height: rect.height
2953
+ },
2954
+ endEdit,
2955
+ table,
2956
+ initialValue: value?.value
2957
+ });
2958
+ }
2959
+ validateValue(newValue, oldValue) {
2960
+ this.newValue = newValue;
2961
+ this.oldValue = oldValue;
2962
+ return ValidateEnum.invalidateExit;
2963
+ }
2964
+ // 不可删除,否则会报错
2965
+ setValue() {
2966
+ }
2967
+ // 结束编辑时触发,返回编辑后的值
2968
+ getValue() {
2969
+ return { ...this.oldValue, value: getLinkEditorValue() };
2970
+ }
2971
+ // 退出编辑状态
2972
+ onEnd() {
2973
+ if (isNil(this.col) || isNil(this.row)) return;
2974
+ const tableEditor = getActiveEditorTable();
2975
+ if (!tableEditor) throw new Error("[LinkEditor::onEnd] tableEditor is Nil");
2976
+ if (isNil(this.col) || isNil(this.row)) throw new Error("[LinkEditor::onEnd] col or row is Nil");
2977
+ const command = new EditTableCellDataCommand(tableEditor, this.col, this.row, this.newValue, this.oldValue);
2978
+ tableEditor.commandManager.execute(command);
2979
+ clearEditorTool();
2980
+ }
2981
+ /**
2982
+ * 如果提供了此函数,VTable 将会在用户点击其他地方时调用此函数。
2983
+ * 如果此函数返回了一个假值,VTable 将会调用 `onEnd` 并退出编辑状态。
2984
+ * 如果未定义此函数或此函数返回了一个真值, VTable 将不会做任何事。
2985
+ * 这意味着,你需要手动调用 `onStart` 中提供的 `endEdit` 来结束编辑模式。
2986
+ */
2987
+ isEditorElement(target) {
2988
+ return !!target.closest(".link-editor-popover-root") || !!target.closest(".link-editor-popover");
2989
+ }
2990
+ }
2991
+
2992
+ class NumEditorTool {
2993
+ // 双击单元格触发,进入编辑状态
2994
+ onStart(props) {
2995
+ const { col, row, table, value, endEdit } = props;
2996
+ this.row = row;
2997
+ this.col = col;
2998
+ this.oldValue = value;
2999
+ const rect = table.getCellRelativeRect(col, row);
3000
+ if (!rect) throw new Error("[NumEditor::onStart] rect is Nil");
3001
+ initEditorTool(31, {
3002
+ endEdit,
3003
+ initialValue: value?.value,
3004
+ table,
3005
+ cell: {
3006
+ col,
3007
+ row,
3008
+ position: { x: rect.left, y: rect.top },
3009
+ width: rect.width,
3010
+ height: rect.height
3011
+ }
3012
+ });
3013
+ }
3014
+ validateValue(newValue, oldValue) {
3015
+ this.newValue = newValue;
3016
+ this.oldValue = oldValue;
3017
+ return ValidateEnum.invalidateExit;
3018
+ }
3019
+ setValue() {
3020
+ }
3021
+ // 结束编辑时触发,返回编辑后的值
3022
+ getValue() {
3023
+ return { ...this.oldValue, value: getNumEditorValue() };
3024
+ }
3025
+ // 退出编辑状态
3026
+ onEnd() {
3027
+ if (isNil(this.col) || isNil(this.row)) return;
3028
+ const tableEditor = getActiveEditorTable();
3029
+ if (!tableEditor) throw new Error("[NumEditor::onEnd] tableEditor is Nil");
3030
+ if (isNil(this.col) || isNil(this.row)) throw new Error("[NumEditor::onEnd] col or row is Nil");
3031
+ const command = new EditTableCellDataCommand(tableEditor, this.col, this.row, this.newValue, this.oldValue);
3032
+ tableEditor.commandManager.execute(command);
3033
+ clearEditorTool();
3034
+ }
3035
+ isEditorElement(target) {
3036
+ return target.className.includes("ant-input-number");
3037
+ }
3038
+ }
3039
+
3040
+ class MultiSelectEditorTool {
3041
+ // 双击单元格触发,进入编辑状态
3042
+ onStart(props) {
3043
+ const { col, row, table, value, endEdit } = props;
3044
+ this.table = table;
3045
+ this.col = col;
3046
+ this.row = row;
3047
+ this.oldValue = value;
3048
+ const rect = table.getCellRelativeRect(col, row);
3049
+ if (!rect) throw new Error("[MultiSelectEditor::onStart] rect is Nil");
3050
+ const options = table.getBodyColumnDefine(col, 0)?.options || [];
3051
+ initEditorTool(30, {
3052
+ initialValue: value?.value ?? [],
3053
+ endEdit,
3054
+ options,
3055
+ table,
3056
+ cell: {
3057
+ col,
3058
+ row,
3059
+ position: { x: rect.left, y: rect.top },
3060
+ width: rect.width,
3061
+ height: rect.height
3062
+ },
3063
+ isMultiple: true
3064
+ });
3065
+ }
3066
+ validateValue(newValue, oldValue) {
3067
+ this.newValue = newValue;
3068
+ this.oldValue = oldValue;
3069
+ return ValidateEnum.invalidateExit;
3070
+ }
3071
+ setValue() {
3072
+ }
3073
+ // 结束编辑时触发,返回编辑后的值
3074
+ getValue() {
3075
+ return { ...this.oldValue, value: getSelectEditorValue() };
3076
+ }
3077
+ // 退出编辑状态
3078
+ onEnd() {
3079
+ clearEditorTool();
3080
+ }
3081
+ /**
3082
+ * 如果提供了此函数,VTable 将会在用户点击其他地方时调用此函数。
3083
+ * 如果此函数返回了一个假值,VTable 将会调用 `onEnd` 并退出编辑状态。
3084
+ * 如果未定义此函数或此函数返回了一个真值, VTable 将不会做任何事。
3085
+ * 这意味着,你需要手动调用 `onStart` 中提供的 `endEdit` 来结束编辑模式。
3086
+ */
3087
+ isEditorElement(target) {
3088
+ return !!target.closest(".multi-select-editor-popover-root") || !!target.closest(".multi-select-editor-popover") || !!target.closest(".selector-color-picker") || !!target.closest(".option-item-modal");
3089
+ }
3090
+ }
3091
+
3092
+ class SingleSelectEditorTool {
3093
+ // 双击单元格触发,进入编辑状态
3094
+ onStart(props) {
3095
+ const { col, row, table, value, endEdit } = props;
3096
+ this.table = table;
3097
+ this.col = col;
3098
+ this.row = row;
3099
+ this.oldValue = value;
3100
+ const rect = table.getCellRelativeRect(col, row);
3101
+ if (!rect) throw new Error("[SingleSelectEditor::onStart] rect is Nil");
3102
+ const options = table.getBodyColumnDefine(col, 0)?.options || [];
3103
+ initEditorTool(29, {
3104
+ initialValue: value?.value ?? [],
3105
+ endEdit,
3106
+ options,
3107
+ table,
3108
+ cell: {
3109
+ col,
3110
+ row,
3111
+ position: { x: rect.left, y: rect.top },
3112
+ width: rect.width,
3113
+ height: rect.height
3114
+ },
3115
+ isMultiple: false
3116
+ });
3117
+ }
3118
+ validateValue(newValue, oldValue) {
3119
+ this.newValue = newValue;
3120
+ this.oldValue = oldValue;
3121
+ return ValidateEnum.invalidateExit;
3122
+ }
3123
+ setValue() {
3124
+ }
3125
+ // 结束编辑时触发,返回编辑后的值
3126
+ getValue() {
3127
+ return { ...this.oldValue, value: getSelectEditorValue() };
3128
+ }
3129
+ // 退出编辑状态
3130
+ onEnd() {
3131
+ clearEditorTool();
3132
+ }
3133
+ /**
3134
+ * 如果提供了此函数,VTable 将会在用户点击其他地方时调用此函数。
3135
+ * 如果此函数返回了一个假值,VTable 将会调用 `onEnd` 并退出编辑状态。
3136
+ * 如果未定义此函数或此函数返回了一个真值, VTable 将不会做任何事。
3137
+ * 这意味着,你需要手动调用 `onStart` 中提供的 `endEdit` 来结束编辑模式。
3138
+ */
3139
+ isEditorElement(target) {
3140
+ return !!target.closest(".multi-select-editor-popover-root") || !!target.closest(".multi-select-editor-popover") || !!target.closest(".selector-color-picker") || !!target.closest(".option-item-modal");
3141
+ }
3142
+ }
3143
+
3144
+ class TextEditorTool {
3145
+ // 双击单元格触发,进入编辑状态
3146
+ onStart(props) {
3147
+ const { col, row, table, value, endEdit } = props;
3148
+ this.row = row;
3149
+ this.col = col;
3150
+ this.oldValue = value;
3151
+ const rect = table.getCellRelativeRect(col, row);
3152
+ if (!rect) throw new Error("[TextEditor::onStart] rect is Nil");
3153
+ initEditorTool(28, {
3154
+ endEdit,
3155
+ initialValue: value?.value ?? "",
3156
+ table,
3157
+ cell: {
3158
+ col,
3159
+ row,
3160
+ position: { x: rect.left, y: rect.top },
3161
+ width: rect.width,
3162
+ height: rect.height
3163
+ }
3164
+ });
3165
+ }
3166
+ validateValue(newValue, oldValue) {
3167
+ this.newValue = newValue;
3168
+ this.oldValue = oldValue;
3169
+ return ValidateEnum.invalidateExit;
3170
+ }
3171
+ setValue() {
3172
+ }
3173
+ // 结束编辑时触发,返回编辑后的值
3174
+ getValue() {
3175
+ return { ...this.oldValue, value: getTextEditorValue() };
3176
+ }
3177
+ // 退出编辑状态
3178
+ onEnd() {
3179
+ if (isNil(this.col) || isNil(this.row)) return;
3180
+ const tableEditor = getActiveEditorTable();
3181
+ if (!tableEditor) throw new Error("[TextEditor::onEnd] tableEditor is Nil");
3182
+ if (isNil(this.col) || isNil(this.row)) throw new Error("[TextEditor::onEnd] col or row is Nil");
3183
+ const command = new EditTableCellDataCommand(tableEditor, this.col, this.row, this.newValue, this.oldValue);
3184
+ tableEditor.commandManager.execute(command);
3185
+ clearEditorTool();
3186
+ }
3187
+ isEditorElement(target) {
3188
+ return target.className.includes("ant-input");
3189
+ }
3190
+ }
3191
+
3192
+ const inputEditor = new VTable_editors.InputEditor();
3193
+ const dateInputEditor = new VTable_editors.DateInputEditor();
3194
+ const textAreaEditor = new VTable_editors.TextAreaEditor({ readonly: false });
3195
+ const linkEditor = new LinkEditorTool();
3196
+ const dateRangeEditor = new DateSelectEditorTool();
3197
+ const textEditor = new TextEditorTool();
3198
+ const numberEditor = new NumEditorTool();
3199
+ const multiSelectEditor = new MultiSelectEditorTool();
3200
+ const singleSelectEditor = new SingleSelectEditorTool();
3201
+ const jsonEditor = new JsonEditorTool();
3202
+ const editorRegistry = () => {
3203
+ VTable.register.editor("inputEditor", inputEditor);
3204
+ VTable.register.editor("dateInputEditor", dateInputEditor);
3205
+ VTable.register.editor("textAreaEditor", textAreaEditor);
3206
+ VTable.register.editor("multiSelectEditor", multiSelectEditor);
3207
+ VTable.register.editor("linkEditor", linkEditor);
3208
+ VTable.register.editor("dateInputEditor", dateRangeEditor);
3209
+ VTable.register.editor("textEditor", textEditor);
3210
+ VTable.register.editor("numberEditor", numberEditor);
3211
+ VTable.register.editor("singleSelectEditor", singleSelectEditor);
3212
+ VTable.register.editor("jsonEditor", jsonEditor);
3213
+ };
3214
+
3215
+ const padding = 4;
3216
+ const createIconGraphics = (name, props) => {
3217
+ const { disabled, cursor, x = 0, y = 0, size = 20, color = COLOR_ICON, hoverBg, onClick } = props ?? {};
3218
+ const u = EnableAIBaseFontIcon.get(name, "unicode");
3219
+ const group = createGroup({
3220
+ display: "flex",
3221
+ x,
3222
+ y,
3223
+ width: size + padding,
3224
+ height: size + padding,
3225
+ cornerRadius: 4,
3226
+ cursor: disabled ? void 0 : cursor,
3227
+ alignContent: "center",
3228
+ alignItems: "center",
3229
+ justifyContent: "center"
3230
+ });
3231
+ const text = createText({
3232
+ text: u,
3233
+ fontSize: size,
3234
+ fontFamily: EnableAIBaseFontIcon.fontFamily,
3235
+ fill: color,
3236
+ cursor: disabled ? void 0 : cursor,
3237
+ textBaseline: "alphabetic"
3238
+ });
3239
+ if (hoverBg && !disabled) {
3240
+ group.addEventListener("mouseover", () => {
3241
+ group.setAttribute("background", "#f0f0f0");
3242
+ group.stage?.renderNextFrame();
3243
+ });
3244
+ group.addEventListener("mouseleave", () => {
3245
+ group.setAttribute("background", "");
3246
+ group.stage?.renderNextFrame();
3247
+ });
3248
+ if (onClick) group.addEventListener("click", onClick);
3249
+ }
3250
+ group.add(text);
3251
+ return group;
3252
+ };
3253
+
3254
+ const createPlusHeaderGraphics = (props) => {
3255
+ const { table, row, col, rect } = props;
3256
+ const { height, width } = rect ?? table.getCellRect(col, row);
3257
+ const group = createGroup({
3258
+ display: "flex",
3259
+ alignContent: "center",
3260
+ alignItems: "center",
3261
+ justifyContent: "center",
3262
+ width,
3263
+ height,
3264
+ cursor: "pointer"
3265
+ });
3266
+ const icon = createIconGraphics("plus", {
3267
+ cursor: "pointer"
3268
+ });
3269
+ group.add(icon);
3270
+ group.addEventListener("click", () => {
3271
+ const editor = getActiveEditorTable();
3272
+ if (!editor) throw new Error("[createPlusHeaderGraphics::onClick]editor is not found");
3273
+ editor.dispatch("column::add::from::inner::plus", {
3274
+ editor
3275
+ });
3276
+ });
3277
+ return group;
3278
+ };
3279
+
3280
+ dayjs.extend(utc);
3281
+ dayjs.extend(timezone);
3282
+ dayjs.extend(customParseFormat);
3283
+ const FIXED_TIMEZONE_LABEL = "(GMT+8)";
3284
+ const createDateGraphics = (props) => {
3285
+ const { table, row, col, rect } = props;
3286
+ const { height, width } = rect ?? table.getCellRect(col, row);
3287
+ const loading = !!props.value?.loading;
3288
+ const columnDefine = table.getBodyColumnDefine(col, row);
3289
+ const type = columnDefine.fmt;
3290
+ const cellStyle = table._getCellStyle(col, row);
3291
+ const functionalFontSize = getProp("fontSize", cellStyle, col, row, table);
3292
+ const functionalColor = getProp("color", cellStyle, col, row, table);
3293
+ const headerIconSize = getIconSizeFromFont(functionalFontSize);
3294
+ const padding = [5, 10];
3295
+ const group = createGroup({
3296
+ width,
3297
+ height,
3298
+ display: "flex",
3299
+ flexWrap: "nowrap",
3300
+ justifyContent: "flex-start",
3301
+ alignContent: "center",
3302
+ alignItems: "center"
3303
+ });
3304
+ const value = props.value?.value ?? "";
3305
+ const getPureDateFormat = (format) => {
3306
+ return format.replace(/\s*\(GMT\+8\)$/i, "").replace(/\s*\(G1T\+8\)$/i, "").replace(/\s*\(G10T\+8\)$/i, "").replace(/G1T\+8/gi, "").replace(/G10T\+8/gi, "").trim();
3307
+ };
3308
+ const formattedValue1 = (() => {
3309
+ const cleanedValue = value && value?.replace(/\s*\([^)]+\)$/gi, "").replace(/:/g, ":").replace(/\s+/g, " ").replace(/(|)/g, "").replace(/[\u200B-\u200D\uFEFF]/g, "").replace(/[^0-9\/\-\s:]/g, "");
3310
+ const pureDateTimeMatch = cleanedValue.match(/(\d{4}[-\/]\d{2}[-\/]\d{2}(?:\s+\d{2}:\d{2})?)/);
3311
+ const pureDateTime = pureDateTimeMatch?.[1]?.trim() || "";
3312
+ if (!pureDateTime) return "";
3313
+ const pureDateFormat = getPureDateFormat(type);
3314
+ if (!pureDateFormat) return "";
3315
+ const parseFormats = [pureDateFormat, "YYYY/MM/DD HH:mm", "YYYY-MM-DD HH:mm", "YYYY/MM/DD", "YYYY-MM-DD"];
3316
+ let finalParsedDate = dayjs(pureDateTime, parseFormats[0], true);
3317
+ for (const format of parseFormats.slice(1)) {
3318
+ if (!finalParsedDate.isValid()) {
3319
+ finalParsedDate = dayjs(pureDateTime, format, true);
3320
+ } else break;
3321
+ }
3322
+ if (!finalParsedDate.isValid()) return "";
3323
+ const pureFormattedDate = finalParsedDate.format(pureDateFormat);
3324
+ return type.includes("GMT+8") || type.includes("G1T+8") || type.includes("G10T+8") ? pureFormattedDate + " " + FIXED_TIMEZONE_LABEL : pureFormattedDate;
3325
+ })();
3326
+ const text = createText({
3327
+ fontSize: headerIconSize,
3328
+ fontFamily: "sans-serif",
3329
+ text: formattedValue1,
3330
+ fill: functionalColor,
3331
+ maxLineWidth: width - padding[1] * 2,
3332
+ boundsPadding: [5, 12, 5, 10]
3333
+ });
3334
+ group.add(text);
3335
+ return createGraphicsWithLoading(group, loading);
3336
+ };
3337
+
3338
+ const createLinkGraphics = (props) => {
3339
+ const { value, table, rect, col, row } = props;
3340
+ const { title, url } = value?.value ?? {};
3341
+ const loading = value?.loading ?? false;
3342
+ const padding = [5, 10];
3343
+ const { height, width } = rect ?? table.getCellRect(col, row);
3344
+ const group = createGroup({
3345
+ width,
3346
+ height,
3347
+ display: "flex",
3348
+ flexWrap: "nowrap",
3349
+ justifyContent: "space-around",
3350
+ alignContent: "center",
3351
+ alignItems: "center"
3352
+ // 垂直居中
3353
+ });
3354
+ const text = createText({
3355
+ fontSize: 12,
3356
+ fontFamily: "sans-serif",
3357
+ text: title,
3358
+ fill: COLOR_LINK,
3359
+ cursor: "pointer",
3360
+ maxLineWidth: width - padding[1] * 2,
3361
+ boundsPadding: [5, 12, 5, 10]
3362
+ });
3363
+ text.addEventListener("click", () => window.open(url));
3364
+ text.addEventListener("mouseover", () => {
3365
+ text.setAttribute("underline", 1);
3366
+ text.stage?.renderNextFrame();
3367
+ });
3368
+ text.addEventListener("mouseleave", () => {
3369
+ text.setAttribute("underline", 0);
3370
+ text.stage?.renderNextFrame();
3371
+ });
3372
+ group.add(text);
3373
+ return createGraphicsWithLoading(group, loading);
3374
+ };
3375
+
3376
+ const createMainColumnCellGraphics = (props) => {
3377
+ const { row, col, rect, table } = props;
3378
+ const { height, width } = rect ?? table.getCellRect(col, row);
3379
+ const loading = !!props.value?.loading;
3380
+ const cellStyle = table._getCellStyle(col, row);
3381
+ const functionalFontSize = getProp("fontSize", cellStyle, col, row, table);
3382
+ const group = createGroup({
3383
+ width,
3384
+ height,
3385
+ display: "flex",
3386
+ alignItems: "center",
3387
+ justifyContent: "space-between"
3388
+ });
3389
+ const text = createText({
3390
+ fontSize: functionalFontSize,
3391
+ text: props.value?.value ?? "",
3392
+ fill: getProp("color", cellStyle, col, row, table),
3393
+ cursor: "pointer",
3394
+ maxLineWidth: width - 30
3395
+ });
3396
+ group.add(text);
3397
+ const ig = createGroup({
3398
+ width: 30,
3399
+ height,
3400
+ display: "flex",
3401
+ alignItems: "center",
3402
+ justifyContent: "center"
3403
+ });
3404
+ const detailIcon = createIconGraphics("drawer", {
3405
+ hoverBg: true,
3406
+ cursor: "pointer",
3407
+ size: functionalFontSize,
3408
+ onClick: () => {
3409
+ const editor = getActiveEditorTable();
3410
+ if (!editor) throw new Error("createMainColumnCellGraphics::detailIcon onclick \u672A\u83B7\u53D6\u5230activeEditorTable");
3411
+ const { recordId } = editor.getInteractiveCell(col, row);
3412
+ editor.dispatch("record::open::detail::drawer", { editor, recordId });
3413
+ }
3414
+ });
3415
+ ig.add(detailIcon);
3416
+ ig.removeChild(detailIcon);
3417
+ group.addEventListener("mouseover", () => {
3418
+ ig.setAttribute("visible", true);
3419
+ ig.add(detailIcon);
3420
+ group.stage?.renderNextFrame();
3421
+ });
3422
+ group.addEventListener("mouseleave", () => {
3423
+ ig.setAttribute("visible", false);
3424
+ ig.removeChild(detailIcon);
3425
+ group.stage?.renderNextFrame();
3426
+ });
3427
+ group.add(ig);
3428
+ return createGraphicsWithLoading(group, loading);
3429
+ };
3430
+
3431
+ const createMoreGraphics = (props) => {
3432
+ const { onClick, onCancel, record, row, col, table, rect, value } = props;
3433
+ const loading = !!value?.loading;
3434
+ const cellStyle = table._getCellStyle(col, row);
3435
+ const functionalFontSize = getProp("fontSize", cellStyle, col, row, table);
3436
+ const functionalColor = getProp("color", cellStyle, col, row, table);
3437
+ const { height, width } = rect ?? table.getCellRect(col, row);
3438
+ const group = createGroup({
3439
+ width,
3440
+ height,
3441
+ display: "flex",
3442
+ cursor: "pointer",
3443
+ justifyContent: "center",
3444
+ alignItems: "center"
3445
+ });
3446
+ const icon = createIconGraphics("more", {
3447
+ size: getIconSizeFromFont(functionalFontSize),
3448
+ color: functionalColor,
3449
+ x: (width - 16) / 2,
3450
+ y: (height - 16) / 2,
3451
+ cursor: "pointer"
3452
+ });
3453
+ const handleEvent = (evt) => {
3454
+ evt.stopPropagation();
3455
+ const x = evt.clientX || evt.x || 0;
3456
+ const y = evt.clientY || evt.y || 0;
3457
+ onClick?.({ row, col, record, x, y });
3458
+ const rect2 = table.getCellRelativeRect(col, row);
3459
+ const tableEditor = getActiveEditorTable();
3460
+ if (!tableEditor) throw new Error("tableEditor \u4E0D\u5B58\u5728");
3461
+ const { recordId, columnId } = tableEditor.getInteractiveCell(row, col);
3462
+ setMenu({
3463
+ open: true,
3464
+ position: { x: rect2.left + width / 2 - 8, y: rect2.top + height / 2 - 8 },
3465
+ type: "more",
3466
+ cell: { col, row, recordId, columnId, rect: rect2 }
3467
+ });
3468
+ };
3469
+ const handleCancel = (evt) => {
3470
+ evt.stopPropagation();
3471
+ onCancel?.();
3472
+ setMenu(null);
3473
+ };
3474
+ group.addEventListener("pointerenter", handleEvent);
3475
+ group.addEventListener("pointermove", handleEvent);
3476
+ group.addEventListener("click", handleEvent);
3477
+ group.addEventListener("pointerleave", handleCancel);
3478
+ group.add(icon);
3479
+ return createGraphicsWithLoading(group, loading);
3480
+ };
3481
+
3482
+ const formatTagItem = (value, options, tableInstance) => {
3483
+ const {
3484
+ blankWidth = 16,
3485
+ fontWeight = 400,
3486
+ fontSize,
3487
+ fontColor = "black",
3488
+ fontFamily = "PingFang SC",
3489
+ basePadding = 16 * 2,
3490
+ tagMinWidth = 48,
3491
+ cellWidth
3492
+ } = options;
3493
+ const tagMaxWidth = Math.max(cellWidth - basePadding, tagMinWidth);
3494
+ const item = {
3495
+ label: value.label,
3496
+ value: value.value
3497
+ };
3498
+ const label = (item.label || "").replace(/[\u200B\u2060]/g, "");
3499
+ const { width } = tableInstance.measureText(label, {
3500
+ fontSize,
3501
+ fontWeight,
3502
+ fontFamily
3503
+ });
3504
+ const textLength = Math.ceil(width) + blankWidth;
3505
+ const tagWidth = Math.min(Math.max(textLength, tagMinWidth), tagMaxWidth);
3506
+ if (!item.tagProps) {
3507
+ item.tagProps = {
3508
+ fontColor,
3509
+ backgroundColor: value.color,
3510
+ width: 0
3511
+ };
3512
+ }
3513
+ item.tagProps.width = tagWidth;
3514
+ return {
3515
+ textLength,
3516
+ tagWidth,
3517
+ tagMaxWidth,
3518
+ tagMinWidth,
3519
+ item
3520
+ };
3521
+ };
3522
+
3523
+ const TAG_HEIGHT$2 = 18;
3524
+ const TAG_GAP = 8;
3525
+ const NUMBER_WIDTH = 25;
3526
+ const TAG_MIN_WIDTH$2 = TAG_GAP + NUMBER_WIDTH;
3527
+ const getTagsRowMap = (tags, options, tableInstance) => {
3528
+ const res = calcCellTags(
3529
+ tags,
3530
+ {
3531
+ cellWidth: options.cellWidth,
3532
+ fontSize: options.fontSize,
3533
+ blankWidth: options.blankWidth,
3534
+ tagMinWidth: TAG_MIN_WIDTH$2
3535
+ },
3536
+ tableInstance
3537
+ );
3538
+ return res.filterTagsRowMap;
3539
+ };
3540
+ const calcCellTags = (tags = [], options, tableInstance) => {
3541
+ if (!tags.length) return { filterTags: [], filterTagsRowMap: {} };
3542
+ const data = cloneDeep(tags);
3543
+ const filterTags = [];
3544
+ const filterTagsRowMap = {};
3545
+ const numberTagWidth = TAG_GAP + NUMBER_WIDTH;
3546
+ const len = data.length;
3547
+ let totalWidth = 0;
3548
+ const currentRow = 1;
3549
+ const addTags = (item) => {
3550
+ filterTags.push(item);
3551
+ if (!filterTagsRowMap[currentRow]) {
3552
+ filterTagsRowMap[currentRow] = [];
3553
+ }
3554
+ filterTagsRowMap[currentRow].push(item);
3555
+ };
3556
+ if (len === 1) {
3557
+ const { item } = formatTagItem(data[0], options, tableInstance);
3558
+ addTags(item);
3559
+ } else if (len > 1) {
3560
+ for (let i = 0; i < len; i += 1) {
3561
+ const { item, tagMaxWidth, tagMinWidth, tagWidth } = formatTagItem(data[i], options, tableInstance);
3562
+ const handlePushItem = (item2) => {
3563
+ const calcWidth = totalWidth + tagMinWidth + (i === len - 1 ? 0 : numberTagWidth);
3564
+ if (tagMaxWidth >= calcWidth) {
3565
+ const realTextWidth = Math.min(tagWidth, tagMaxWidth - totalWidth - (i === len - 1 ? 0 : numberTagWidth));
3566
+ item2.tagProps.width = realTextWidth;
3567
+ totalWidth = totalWidth + realTextWidth + TAG_GAP;
3568
+ addTags(item2);
3569
+ return false;
3570
+ } else if (tagMaxWidth < calcWidth) {
3571
+ if (tagMaxWidth < totalWidth + tagMinWidth + numberTagWidth) {
3572
+ addTags({
3573
+ label: "+" + (len - filterTags.length),
3574
+ value: -1,
3575
+ tagProps: {
3576
+ fontColor: "#404A59",
3577
+ backgroundColor: "#eff0f1",
3578
+ width: TAG_MIN_WIDTH$2
3579
+ }
3580
+ });
3581
+ return true;
3582
+ }
3583
+ if (i === len - 1) {
3584
+ item2.tagProps.width = Math.min(tagWidth, tagMaxWidth - totalWidth);
3585
+ addTags(item2);
3586
+ } else {
3587
+ item2.tagProps.width = Math.min(tagWidth, tagMaxWidth - totalWidth - numberTagWidth);
3588
+ addTags(item2);
3589
+ addTags({
3590
+ label: "+" + (len - filterTags.length),
3591
+ value: -1,
3592
+ tagProps: {
3593
+ fontColor: "#404A59",
3594
+ backgroundColor: "#eff0f1",
3595
+ width: NUMBER_WIDTH
3596
+ }
3597
+ });
3598
+ }
3599
+ return true;
3600
+ }
3601
+ };
3602
+ if (handlePushItem(item)) break;
3603
+ }
3604
+ }
3605
+ return { filterTags, filterTagsRowMap };
3606
+ };
3607
+ const createMultiSelectGraphic = (props) => {
3608
+ const { width, height, table, col, row } = props;
3609
+ const value = props.value?.value ?? [];
3610
+ const loading = !!props.value?.loading;
3611
+ const styles = table._getCellStyle(col, row);
3612
+ const options = table.getBodyColumnDefine(col, 0).options || [];
3613
+ const newValue = value.map((item) => {
3614
+ return options.find((option) => option.value === item.value);
3615
+ }).filter(Boolean);
3616
+ const fontSize = getProp("fontSize", styles, col, row, table);
3617
+ const tagsMap = getTagsRowMap(newValue, { cellWidth: width, blankWidth: 16, fontSize }, table) || {};
3618
+ const container = /* @__PURE__ */ React.createElement(
3619
+ VGroup,
3620
+ {
3621
+ attribute: {
3622
+ width: width - 32,
3623
+ height,
3624
+ display: "flex",
3625
+ alignItems: "center",
3626
+ alignContent: "center",
3627
+ dx: 16
3628
+ }
3629
+ },
3630
+ Object.keys(tagsMap).map((key) => {
3631
+ const tag = tagsMap[Number(key)];
3632
+ return /* @__PURE__ */ React.createElement(
3633
+ VGroup,
3634
+ {
3635
+ key,
3636
+ attribute: {
3637
+ width: width - 32,
3638
+ height: TAG_HEIGHT$2,
3639
+ display: "flex",
3640
+ alignItems: "center",
3641
+ flexWrap: "nowrap",
3642
+ overflow: "hidden",
3643
+ boundsPadding: [4, 0, 4, 0]
3644
+ }
3645
+ },
3646
+ tag.map((item, subIndex) => {
3647
+ return /* @__PURE__ */ React.createElement(
3648
+ VGroup,
3649
+ {
3650
+ key: item.value,
3651
+ attribute: {
3652
+ width: item.tagProps.width,
3653
+ height: TAG_HEIGHT$2,
3654
+ display: "flex",
3655
+ alignItems: "center",
3656
+ justifyContent: "center",
3657
+ overflow: "hidden",
3658
+ cornerRadius: TAG_HEIGHT$2 / 2,
3659
+ fill: item.tagProps.backgroundColor,
3660
+ boundsPadding: subIndex > 0 ? [0, 0, 0, 8] : 0
3661
+ }
3662
+ },
3663
+ /* @__PURE__ */ React.createElement(
3664
+ VText,
3665
+ {
3666
+ attribute: {
3667
+ text: item.label,
3668
+ fontSize,
3669
+ fill: getTextColorForBackground(item.tagProps.backgroundColor || "#000000"),
3670
+ textAlign: "left",
3671
+ textBaseline: "top",
3672
+ maxLineWidth: item.tagProps.width - 16
3673
+ }
3674
+ }
3675
+ )
3676
+ );
3677
+ })
3678
+ );
3679
+ })
3680
+ );
3681
+ return createGraphicsWithLoading(container, loading);
3682
+ };
3683
+
3684
+ const createNumberGraphics = (props) => {
3685
+ const { row, col, table, rect, textAlign = "right" } = props;
3686
+ const columnDefine = table.getBodyColumnDefine(col, row);
3687
+ const fmt = columnDefine.fmt ?? "integer";
3688
+ const value = props.value?.value;
3689
+ const loading = !!props.value?.loading;
3690
+ const cellStyle = table._getCellStyle(col, row);
3691
+ const fontSize = getProp("fontSize", cellStyle, col, row, table);
3692
+ const functionalColor = getProp("color", cellStyle, col, row, table);
3693
+ const { height, width } = rect ?? table.getCellRect(col, row);
3694
+ const textValue = formatValue(value, fmt);
3695
+ const padding = 10;
3696
+ const group = createGroup({
3697
+ width,
3698
+ height
3699
+ });
3700
+ const text = createText({
3701
+ text: textValue,
3702
+ fill: functionalColor,
3703
+ fontFamily: "sans-serif",
3704
+ fontSize,
3705
+ textBaseline: "middle",
3706
+ y: height / 2,
3707
+ maxLineWidth: width - padding * 2
3708
+ });
3709
+ if (textAlign === "left") {
3710
+ text.setAttribute("x", padding);
3711
+ text.setAttribute("textAlign", "left");
3712
+ } else if (textAlign === "center") {
3713
+ text.setAttribute("x", width / 2);
3714
+ text.setAttribute("textAlign", "center");
3715
+ } else {
3716
+ text.setAttribute("x", width - padding);
3717
+ text.setAttribute("textAlign", "right");
3718
+ }
3719
+ group.add(text);
3720
+ return createGraphicsWithLoading(group, loading);
3721
+ };
3722
+
3723
+ const getTextWidth = (text, fontSize) => {
3724
+ const textElement = createText({
3725
+ text,
3726
+ fontSize
3727
+ });
3728
+ const bounds = textElement.AABBBounds;
3729
+ return bounds.width();
3730
+ };
3731
+
3732
+ const AVATAR_SIZE = 20;
3733
+ const GAP = 4;
3734
+ const ITEM_GAP = 8;
3735
+ const ELLIPSIS = "...";
3736
+ const ELLIPSIS_WIDTH = 8;
3737
+ const PADDING_LEFT = 15;
3738
+ const getName = (id, value) => {
3739
+ if (id == -1) {
3740
+ return "\u7CFB\u7EDF";
3741
+ } else if (id == -2) {
3742
+ return "\u65E0";
3743
+ }
3744
+ return value;
3745
+ };
3746
+ const createPeopleGraphics = (props) => {
3747
+ const { row, col, rect, table } = props;
3748
+ const cellStyle = table?._getCellStyle(col, row);
3749
+ const functionalFontSize = getProp("fontSize", cellStyle, col, row, table);
3750
+ const functionalColor = getProp("color", cellStyle, col, row, table);
3751
+ const { height, width } = rect ?? table.getCellRect(col, row);
3752
+ const group = createGroup({ width, height, x: 15, y: 0 });
3753
+ const value = props.value?.value ?? [];
3754
+ const loading = props.value?.loading ?? false;
3755
+ const items = [];
3756
+ let cursor = 0;
3757
+ for (let i = 0; i < value.length; i++) {
3758
+ const person = value[i];
3759
+ const nameWidth = getTextWidth(getName(person.id, person.name), functionalFontSize);
3760
+ const fullWidth = AVATAR_SIZE + GAP + nameWidth;
3761
+ const remaining = width - cursor - PADDING_LEFT;
3762
+ if (fullWidth <= remaining) {
3763
+ items.push({ person, mode: "full" });
3764
+ cursor += fullWidth + ITEM_GAP;
3765
+ continue;
3766
+ }
3767
+ const minWidth = AVATAR_SIZE + GAP + ELLIPSIS_WIDTH;
3768
+ if (remaining >= minWidth) {
3769
+ const availableNameWidth = remaining - AVATAR_SIZE - GAP;
3770
+ items.push({
3771
+ person,
3772
+ mode: "truncate",
3773
+ availableNameWidth
3774
+ });
3775
+ }
3776
+ break;
3777
+ }
3778
+ const hasMore = items.length < value.length;
3779
+ let x = 0;
3780
+ items.forEach((item) => {
3781
+ const personGroup = renderPersonItem(item, x, height, functionalFontSize, functionalColor, table, {
3782
+ row,
3783
+ col,
3784
+ width,
3785
+ height
3786
+ });
3787
+ group.add(personGroup);
3788
+ const nameW = item.mode === "full" ? getTextWidth(getName(item.person.id, item.person?.name ?? ""), functionalFontSize) : item.availableNameWidth ?? 0;
3789
+ x += AVATAR_SIZE + GAP + nameW + ITEM_GAP;
3790
+ });
3791
+ if (hasMore) {
3792
+ group.add(
3793
+ createText({
3794
+ x,
3795
+ y: height / 2,
3796
+ text: ELLIPSIS,
3797
+ textBaseline: "middle",
3798
+ fill: functionalColor || "#666",
3799
+ fontSize: functionalFontSize
3800
+ })
3801
+ );
3802
+ }
3803
+ return createGraphicsWithLoading(group, loading);
3804
+ };
3805
+ function renderPersonItem(item, x, containerHeight, functionalFontSize, functionalColor, table, cell) {
3806
+ const { person, mode, availableNameWidth } = item;
3807
+ const y = (containerHeight - AVATAR_SIZE) / 2;
3808
+ const g = createGroup({ x, y, height: AVATAR_SIZE });
3809
+ if (person.id == -1 || person.id == -2) {
3810
+ const avatarBg = createRect({
3811
+ width: AVATAR_SIZE,
3812
+ height: AVATAR_SIZE,
3813
+ cornerRadius: AVATAR_SIZE / 2,
3814
+ fill: person.id == -1 ? "#52c41a" : "#999"
3815
+ });
3816
+ g.add(avatarBg);
3817
+ const avatarText = createText({
3818
+ x: AVATAR_SIZE / 2,
3819
+ y: AVATAR_SIZE / 2,
3820
+ text: person.id == -1 ? "\u7CFB\u7EDF" : "\u65E0",
3821
+ textBaseline: "middle",
3822
+ textAlign: "center",
3823
+ fill: "#fff",
3824
+ fontSize: 8
3825
+ });
3826
+ g.add(avatarText);
3827
+ } else if (person.avatar) {
3828
+ const imageGraphics = createImage({
3829
+ width: AVATAR_SIZE,
3830
+ height: AVATAR_SIZE,
3831
+ image: person.avatar,
3832
+ cornerRadius: AVATAR_SIZE / 2
3833
+ });
3834
+ g.add(imageGraphics);
3835
+ } else {
3836
+ const avatarBg = createRect({
3837
+ width: AVATAR_SIZE,
3838
+ height: AVATAR_SIZE,
3839
+ cornerRadius: AVATAR_SIZE / 2,
3840
+ fill: getColorByStr(person.name?.slice(-2))
3841
+ });
3842
+ g.add(avatarBg);
3843
+ const avatarText = createText({
3844
+ x: AVATAR_SIZE / 2,
3845
+ y: AVATAR_SIZE / 2,
3846
+ text: (person.name?.length ?? 0) >= 2 ? person.name?.slice(-2) : person.name?.[0] ?? "?",
3847
+ textBaseline: "middle",
3848
+ textAlign: "center",
3849
+ fill: "#fff",
3850
+ fontSize: 8
3851
+ });
3852
+ g.add(avatarText);
3853
+ }
3854
+ if (table && cell && person.id !== -1 && person.id !== -2) {
3855
+ g.on("mouseenter", () => {
3856
+ const cellRect = table.getCellRelativeRect(cell.col, cell.row);
3857
+ const avatarCenterX = cellRect.left + PADDING_LEFT + x + AVATAR_SIZE / 2;
3858
+ const avatarCenterY = cellRect.top + y + AVATAR_SIZE / 2;
3859
+ initUserShowTool({
3860
+ cell: {
3861
+ row: cell.row,
3862
+ col: cell.col,
3863
+ position: { x: avatarCenterX, y: avatarCenterY },
3864
+ width: cell.width,
3865
+ height: cell.height
3866
+ },
3867
+ table,
3868
+ endEdit: () => {
3869
+ },
3870
+ id: person.id
3871
+ });
3872
+ });
3873
+ g.on("mouseleave", () => {
3874
+ clearUserShowTool();
3875
+ });
3876
+ }
3877
+ let text = getName(person.id, person.name ?? "");
3878
+ if (mode === "truncate" && availableNameWidth !== void 0 && person.id !== -1 && person.id !== -2) {
3879
+ let left = 0;
3880
+ let right = person.name?.length || 0;
3881
+ let bestLength = 0;
3882
+ while (left <= right) {
3883
+ const mid = Math.floor((left + right) / 2);
3884
+ const testText = person.name?.slice(0, mid) + ELLIPSIS;
3885
+ const testWidth = getTextWidth(testText, functionalFontSize);
3886
+ if (testWidth <= availableNameWidth) {
3887
+ bestLength = mid;
3888
+ left = mid + 1;
3889
+ } else {
3890
+ right = mid - 1;
3891
+ }
3892
+ }
3893
+ text = bestLength > 0 ? person.name?.slice(0, bestLength) + ELLIPSIS : ELLIPSIS;
3894
+ }
3895
+ g.add(
3896
+ createText({
3897
+ x: AVATAR_SIZE + GAP,
3898
+ y: AVATAR_SIZE / 2,
3899
+ text,
3900
+ textBaseline: "middle",
3901
+ fill: functionalColor,
3902
+ fontSize: functionalFontSize
3903
+ })
3904
+ );
3905
+ return g;
3906
+ }
3907
+
3908
+ const TAG_HEIGHT$1 = 18;
3909
+ const CELL_PADDING$1 = 16;
3910
+ const TAG_PADDING$1 = 8;
3911
+ const TAG_MIN_WIDTH$1 = 35;
3912
+ const createSingleSelectGraphic = (props) => {
3913
+ const { table, col, row, rect } = props;
3914
+ const { height, width } = rect ?? table.getCellRect(col, row);
3915
+ const value = props.value?.value ?? [];
3916
+ const loading = !!props.value?.loading;
3917
+ const options = table.getBodyColumnDefine(col, 0).options || [];
3918
+ const valueItem = options.find((option) => option.value === value[0]?.value);
3919
+ if (!valueItem) {
3920
+ return null;
3921
+ }
3922
+ const styles = table._getCellStyle(col, row);
3923
+ const fontSize = getProp("fontSize", styles, col, row, table);
3924
+ const { item } = formatTagItem(
3925
+ valueItem,
3926
+ {
3927
+ cellWidth: width,
3928
+ blankWidth: 16,
3929
+ basePadding: CELL_PADDING$1 * 2,
3930
+ tagMinWidth: TAG_MIN_WIDTH$1,
3931
+ fontSize
3932
+ },
3933
+ table
3934
+ );
3935
+ const container = /* @__PURE__ */ React.createElement(
3936
+ VGroup,
3937
+ {
3938
+ attribute: {
3939
+ width: width - 32,
3940
+ height,
3941
+ display: "flex",
3942
+ alignItems: "center",
3943
+ alignContent: "center",
3944
+ dx: 16
3945
+ }
3946
+ },
3947
+ /* @__PURE__ */ React.createElement(
3948
+ VGroup,
3949
+ {
3950
+ key: item.value,
3951
+ attribute: {
3952
+ width: item.tagProps.width,
3953
+ height: TAG_HEIGHT$1,
3954
+ display: "flex",
3955
+ alignItems: "center",
3956
+ justifyContent: "center",
3957
+ overflow: "hidden",
3958
+ cornerRadius: TAG_HEIGHT$1 / 2 - 1,
3959
+ fill: item.tagProps.backgroundColor
3960
+ }
3961
+ },
3962
+ /* @__PURE__ */ React.createElement(
3963
+ VText,
3964
+ {
3965
+ attribute: {
3966
+ text: item.label,
3967
+ fontSize,
3968
+ fill: getTextColorForBackground(item.tagProps.backgroundColor || "#000000"),
3969
+ textAlign: "left",
3970
+ textBaseline: "top",
3971
+ maxLineWidth: item.tagProps.width - TAG_PADDING$1 * 2
3972
+ }
3973
+ }
3974
+ )
3975
+ )
3976
+ );
3977
+ return createGraphicsWithLoading(container, loading);
3978
+ };
3979
+
3980
+ const TAG_HEIGHT = 18;
3981
+ const CELL_PADDING = 16;
3982
+ const TAG_PADDING = 8;
3983
+ const TAG_MIN_WIDTH = 40;
3984
+ const TAG_BACKGROUND_COLOR = "#FAFAFA";
3985
+ const createTagGraphic = (props) => {
3986
+ const { table, row, col, rect } = props;
3987
+ const { height, width } = rect ?? table.getCellRect(col, row);
3988
+ const value = props.value?.value ?? "";
3989
+ const loading = !!props.value?.loading;
3990
+ const styles = table._getCellStyle(col, row);
3991
+ const fontSize = getProp("fontSize", styles, col, row, table);
3992
+ const { item } = formatTagItem(
3993
+ { label: value, value, color: TAG_BACKGROUND_COLOR },
3994
+ {
3995
+ cellWidth: width,
3996
+ blankWidth: 16,
3997
+ basePadding: CELL_PADDING * 2,
3998
+ tagMinWidth: TAG_MIN_WIDTH,
3999
+ fontSize
4000
+ },
4001
+ table
4002
+ );
4003
+ const container = /* @__PURE__ */ React.createElement(
4004
+ VGroup,
4005
+ {
4006
+ attribute: {
4007
+ width: width - 32,
4008
+ height,
4009
+ display: "flex",
4010
+ alignItems: "center",
4011
+ alignContent: "center",
4012
+ dx: 16
4013
+ }
4014
+ },
4015
+ !isNil(props.value?.value) && /* @__PURE__ */ React.createElement(
4016
+ VGroup,
4017
+ {
4018
+ key: item.value,
4019
+ attribute: {
4020
+ width: item.tagProps.width,
4021
+ height: TAG_HEIGHT,
4022
+ display: "flex",
4023
+ alignItems: "center",
4024
+ justifyContent: "center",
4025
+ overflow: "hidden",
4026
+ cornerRadius: 4,
4027
+ stroke: "#D9D9D9",
4028
+ fill: item.tagProps.backgroundColor
4029
+ }
4030
+ },
4031
+ /* @__PURE__ */ React.createElement(
4032
+ VText,
4033
+ {
4034
+ attribute: {
4035
+ text: item.label,
4036
+ fontSize,
4037
+ zIndex: 1e3,
4038
+ textAlign: "left",
4039
+ textBaseline: "top",
4040
+ fill: item.tagProps.fontColor,
4041
+ maxLineWidth: item.tagProps.width - TAG_PADDING * 2
4042
+ }
4043
+ }
4044
+ )
4045
+ )
4046
+ );
4047
+ return createGraphicsWithLoading(container, loading);
4048
+ };
4049
+
4050
+ const createTimeLenGraphics = (props) => {
4051
+ const { table, row, col, rect } = props;
4052
+ const { height, width } = rect ?? table.getCellRect(col, row);
4053
+ const padding = [5, 10];
4054
+ const loading = props.value?.loading;
4055
+ const value = props.value?.value;
4056
+ const cellStyle = table._getCellStyle(col, row);
4057
+ const functionalFontSize = getProp("fontSize", cellStyle, col, row, table);
4058
+ const functionalColor = getProp("color", cellStyle, col, row, table);
4059
+ const time = !isNil(value) ? formatDuration(value) : "";
4060
+ const group = createGroup({
4061
+ width,
4062
+ height,
4063
+ display: "flex",
4064
+ flexWrap: "nowrap",
4065
+ justifyContent: "space-around",
4066
+ alignContent: "center",
4067
+ alignItems: "center"
4068
+ // 垂直居中
4069
+ });
4070
+ const text = createText({
4071
+ fontSize: functionalFontSize,
4072
+ fontFamily: "sans-serif",
4073
+ text: time,
4074
+ maxLineWidth: width - padding[1] * 2,
4075
+ boundsPadding: [5, 12, 5, 10],
4076
+ fill: functionalColor
4077
+ });
4078
+ group.add(text);
4079
+ return createGraphicsWithLoading(group, !!loading);
4080
+ };
4081
+
4082
+ const suffixToIconName = {
4083
+ // 图片类型
4084
+ png: "image-file",
4085
+ jpg: "image-file",
4086
+ jpeg: "image-file",
4087
+ bmp: "image-file",
4088
+ tif: "image-file",
4089
+ tiff: "image-file",
4090
+ heic: "image-file",
4091
+ pdf: "pdf",
4092
+ doc: "doc",
4093
+ docs: "docs",
4094
+ // 若枚举中没有 "docs",改为 "doc"
4095
+ ppt: "ppt",
4096
+ mp4: "video-file",
4097
+ mp3: "mp3",
4098
+ csv: "csv"
4099
+ };
4100
+ const createUploadDocumentGraphics = (props) => {
4101
+ const { table, row, col, rect } = props;
4102
+ const { height, width } = rect ?? table.getCellRect(col, row);
4103
+ const cellStyle = table._getCellStyle(col, row);
4104
+ const functionalFontSize = getProp("fontSize", cellStyle, col, row, table);
4105
+ const functionalColor = getProp("color", cellStyle, col, row, table);
4106
+ const headerIconSize = getIconSizeFromFont(functionalFontSize);
4107
+ const padding = [5, 10, 20];
4108
+ const value = props.value?.value ?? [];
4109
+ const loading = !!props.value?.loading;
4110
+ const getFileIconName = () => {
4111
+ if (value.length < 1) {
4112
+ return "text";
4113
+ }
4114
+ const fileNameOnly = value[0]?.name.split("/").pop() || "";
4115
+ const suffix = fileNameOnly.split(".").pop()?.toLowerCase() || "";
4116
+ return suffixToIconName[suffix] || "text";
4117
+ };
4118
+ const group = createGroup({
4119
+ width,
4120
+ height,
4121
+ display: "flex",
4122
+ flexWrap: "nowrap",
4123
+ justifyContent: "center",
4124
+ alignContent: "center",
4125
+ alignItems: "center"
4126
+ // 垂直居中
4127
+ // x: 15,
4128
+ });
4129
+ const uploadIcon = createIconGraphics("upload", { x: 0, y: 0, size: 14, color: functionalColor });
4130
+ const text = createText({
4131
+ fontSize: headerIconSize,
4132
+ fontFamily: "sans-serif",
4133
+ text: "\u70B9\u51FB\u4E0A\u4F20",
4134
+ fill: functionalColor,
4135
+ cursor: "pointer",
4136
+ // TODO padding maxLineWidth 计算优化
4137
+ maxLineWidth: width - padding[2] * 2 - uploadIcon.AABBBounds.width() - 12 - 5,
4138
+ boundsPadding: [4, 12, 5, 5],
4139
+ overflow: "hidden",
4140
+ textBaseline: "middle"
4141
+ });
4142
+ const borderGroup = createGroup({
4143
+ width: width - 30,
4144
+ height: height - 8,
4145
+ display: "flex",
4146
+ alignItems: "center",
4147
+ justifyContent: "center",
4148
+ stroke: "#D9D9D9",
4149
+ cursor: "pointer"
4150
+ });
4151
+ borderGroup.add(uploadIcon);
4152
+ borderGroup.add(text);
4153
+ borderGroup.addEventListener("click", () => {
4154
+ const columnDefine = table.getBodyColumnDefine(col, row);
4155
+ const lock = columnDefine.lock;
4156
+ const editor = getActiveEditorTable();
4157
+ if (lock || editor?.lock) return;
4158
+ const fileInput = document.createElement("input");
4159
+ fileInput.type = "file";
4160
+ fileInput.style.display = "none";
4161
+ fileInput.addEventListener("change", (e) => {
4162
+ const target = e.target;
4163
+ const files = target.files;
4164
+ if (files && files.length > 0) {
4165
+ const selectedFile = files[0];
4166
+ console.log("\u7528\u6237\u9009\u62E9\u7684\u6587\u4EF6\uFF1A", selectedFile);
4167
+ uploadFileToServer(selectedFile, col, row, props.value);
4168
+ }
4169
+ fileInput.value = "";
4170
+ });
4171
+ document.body.appendChild(fileInput);
4172
+ fileInput.click();
4173
+ setTimeout(() => {
4174
+ document.body.removeChild(fileInput);
4175
+ }, 100);
4176
+ });
4177
+ group.add(borderGroup);
4178
+ const groupname = createGroup({
4179
+ width,
4180
+ height,
4181
+ display: "flex",
4182
+ flexWrap: "nowrap",
4183
+ justifyContent: "flex-start",
4184
+ // alignContent: 'center',
4185
+ alignItems: "center",
4186
+ // 垂直居中
4187
+ x: 15
4188
+ });
4189
+ const uploadIconName = createIconGraphics(getFileIconName(), { x: 0, y: 0, size: 14, color: functionalColor });
4190
+ const textname = createText({
4191
+ fontSize: headerIconSize,
4192
+ fontFamily: "sans-serif",
4193
+ text: value.length > 0 ? value[0].name : "",
4194
+ fill: functionalColor,
4195
+ cursor: "pointer",
4196
+ maxLineWidth: width - padding[2] * 2,
4197
+ // marginLeft: 5,
4198
+ boundsPadding: [4, 12, 5, 5]
4199
+ });
4200
+ groupname.add(uploadIconName);
4201
+ groupname.add(textname);
4202
+ groupname.addEventListener("click", () => {
4203
+ const editor = getActiveEditorTable();
4204
+ if (!editor) throw new Error("\u672A\u83B7\u53D6\u5230\u8868\u683C\u7F16\u8F91\u5668\u5B9E\u4F8B");
4205
+ editor.dispatch("cell::attachment::preview", {
4206
+ editor,
4207
+ cellValue: props.value
4208
+ });
4209
+ });
4210
+ if (value.length > 0) {
4211
+ return createGraphicsWithLoading(groupname, loading);
4212
+ } else {
4213
+ return createGraphicsWithLoading(group, loading);
4214
+ }
4215
+ };
4216
+ const uploadFileToServer = async (file, col, row, originValue) => {
4217
+ const name = file.name;
4218
+ const suffix = name.split(".").pop();
4219
+ const column = getActiveEditorTable()?.getColumnDefine(col);
4220
+ let getField;
4221
+ if (column.field) {
4222
+ getField = column.field;
4223
+ }
4224
+ try {
4225
+ const editor = getActiveEditorTable();
4226
+ if (!editor) {
4227
+ throw new Error("\u672A\u83B7\u53D6\u5230\u8868\u683C\u7F16\u8F91\u5668\u5B9E\u4F8B");
4228
+ }
4229
+ const path = `dataset/base/table/upload/${getField}/${getUUID()}.${suffix}`;
4230
+ editor.updateCellData(col, row, {
4231
+ loading: true
4232
+ });
4233
+ editor.uploadFile(
4234
+ {
4235
+ file,
4236
+ // 注意:需确保 formData 是后端/编辑器要求的文件类型(如 Blob、File、FormData 等)
4237
+ type: file.type
4238
+ // 类型标识,根据业务传如 'image'/'video'/'file' 等
4239
+ },
4240
+ path
4241
+ ).then(() => {
4242
+ console.log("\u4E0A\u4F20\u6210\u529F\uFF01\uFF01");
4243
+ const tableEditor = getActiveEditorTable();
4244
+ if (!tableEditor) throw new Error("[createUploadFileGraphics::onEnd] tableEditor is Nil");
4245
+ const run = () => {
4246
+ const oldData = { ...originValue, loading: false };
4247
+ const { columnId, recordId } = tableEditor.getInteractiveCell(col, row);
4248
+ const data = {
4249
+ ...oldData,
4250
+ loading: false,
4251
+ value: [{ name, path }]
4252
+ };
4253
+ tableEditor.updateCellData(col, row, data);
4254
+ tableEditor.dispatch("cells::update", {
4255
+ editor: tableEditor,
4256
+ data: [
4257
+ {
4258
+ current: data,
4259
+ previous: oldData,
4260
+ recordId,
4261
+ columnId
4262
+ }
4263
+ ]
4264
+ });
4265
+ return {
4266
+ restore: () => {
4267
+ tableEditor.updateCellData(col, row, oldData);
4268
+ tableEditor.dispatch("cells::update", {
4269
+ editor: tableEditor,
4270
+ data: [
4271
+ {
4272
+ current: oldData,
4273
+ previous: data,
4274
+ recordId,
4275
+ columnId
4276
+ }
4277
+ ]
4278
+ });
4279
+ }
4280
+ };
4281
+ };
4282
+ const command = new EditTableCellDataFunctionalCommand(tableEditor, run);
4283
+ tableEditor.commandManager.execute(command);
4284
+ }).catch((e) => {
4285
+ console.log(e);
4286
+ });
4287
+ } catch (error) {
4288
+ console.error("\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25\uFF1A", error);
4289
+ }
4290
+ };
4291
+
4292
+ export { BaseTableInteractive, CellContextMenu, DateSelect, DateSelectEditorTool, EditTableCellDataCommand, EditTableCellDataFunctionalCommand, HeaderContextMenu, INNER_TABLE_COLUMN_KEY_CHECKBOX, INNER_TABLE_COLUMN_KEY_LIST, INNER_TABLE_COLUMN_KEY_PLUS, INNER_TABLE_RECORD_ID_KEY, JsonEditorTool, LinkEditor, LinkEditorTool, MenuItemInputLabel, MoreContextMenu, MultiSelectEditorTool, NumEditor, NumEditorTool, RecordsManager, SelectEditor, SingleSelectEditorTool, Table, TableChangeEvent, TableEditor, TableEditorEvent, TableJsonEditor, TableLayout, TableSelectEditor, TableView, TextEditor, TextEditorTool, UserShow, VTableEditorType, activeTableIdAtom, adaptColumnStructure, adaptRecordStructure, adaptRecordsStructure, adaptedCellPosition, attachmentColumnCreator, checkboxColumnCreator, clearDateEditorTool, clearEditorTool, clearJsonEditorTool, clearLinkEditorTool, clearNumEditorTool, clearSelectEditorTool, clearTextEditorTool, clearUserShowTool, createBaseTableEditor, createColumns, createDateGraphics, createDefaultColumnConfig, createGraphicsWithLoading, createHeaderGraphics, createIconGraphics, createJsonGraphics, createLinkGraphics, createMainColumnCellGraphics, createMoreGraphics, createMultiSelectGraphic, createNumberGraphics, createPeopleGraphics, createPlusHeaderGraphics, createSingleSelectGraphic, createTagGraphic, createTextGraphics, createTimeLenGraphics, createUploadDocumentGraphics, dateColumnCreator, dateProcess, dropdownColumnCreator, editorRegistry, editorTableStoreAtom, getActiveEditorTable, getActiveTableId, getColFromColIndex, getDateEditorData, getDateEditorValue, getEditorByToolType, getEditorTableStore, getFunctionalProp, getIconSizeFromFont, getJsonEditorData, getJsonEditorValue, getLinkEditorData, getLinkEditorValue, getMenu, getNumEditorData, getNumEditorValue, getProp, getRowFromRowIndex, getSelectEditorData, getSelectEditorValue, getTextEditorData, getTextEditorValue, getTextWidth, getUserShowData, getUserShowId, highlightMerge, initDateEditorTool, initEditorTool, initJsonEditorTool, initLinkEditorTool, initNumEditorTool, initSelectEditorTool, initTextEditorTool, initUserShowTool, isColumnEditable, jsonColumnCreator, linkColumnCreator, moreColumnCreator, multiSelectColumnCreator, numberColumnCreator, plusColumnCreator, revertRecordStructure, setActiveEditorTable, setActiveTableId, setDateEditorData, setDateEditorValue, setEditorTableStore, setJsonEditorData, setJsonEditorValue, setLinkEditorData, setLinkEditorValue, setMenu, setNumEditorData, setNumEditorValue, setSelectEditorData, setSelectEditorValue, setTextEditorData, setTextEditorValue, setUserShowData, setUserShowId, tagColumnCreator, theme, timeLenColumnCreator, updateSelectEditorCell, useActiveEditorTable, useActiveTableId, useComponentVTable, useDateEditorData, useDateEditorValue, useEditorTableStore, useJsonEditorData, useJsonEditorValue, useLinkEditorData, useLinkEditorValue, useMenu, useNumEditorData, useNumEditorValue, useSelectEditorData, useSelectEditorValue, useTextEditorData, useTextEditorValue, useUserShowData, useUserShowId, userColumnCreator };