@canvas-components/react-list-table 0.3.45 → 0.3.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,13 +1,17 @@
1
- import { createContext, memo, forwardRef, useMemo, useRef, useState, useEffect, useImperativeHandle, useLayoutEffect, useContext, useId, Fragment as Fragment$1 } from 'react';
2
- import { createPortal } from 'react-dom';
3
- import { getSharedScrollbarSettings, subscribeSharedScrollbarSettings, setSharedScrollbarSettings, ReactImagePreview, ReactCheckbox, ReactNumberInput, ReactSelect, ReactPagination, ReactFilterPanel, ReactPopup, ReactScrollbarSettingsDialog, ReactScrollbarPositionDialog, ReactModal, ReactForm, ReactInput, ReactRadio, ReactColorPicker, ReactButton, ReactVirtualList, ReactPopconfirm, ReactContentScaleProvider, ReactInputNumber, resolveReactScrollbarMode, ReactAutoComplete, ReactTimeRangePicker, ReactDateTimeRangePicker, ReactDateTimePicker, ReactTimePicker, ReactDateRangePicker, ReactDatePicker, ReactMonthPicker, ReactQuarterPicker, ReactYearPicker, ReactInputRange, ReactCascader, ReactTreeSelect } from '@canvas-components/react-components';
4
- import { printBrowserTable, getBrowserPrintLayoutWarning, getBrowserPrintRowLayoutWarning, BROWSER_PRINT_PAPER_SIZES, calculateBrowserPrintRowsPerPage, calculateBrowserPrintColumnWidths, createBrowserPrintPreviewHtml } from '@canvas-components/print';
5
- import { EXPAND_COLUMN, SELECTION_COLUMN, ROW_SELECTION_COLUMN, resetListTableStoredColumnConfig, ListTable } from '@canvas-components/list-table';
1
+ import { createContext, memo, forwardRef, useMemo, useRef, useState, useEffect, useImperativeHandle, useLayoutEffect, useContext, useId, Fragment as Fragment$1, useCallback } from 'react';
2
+ import { createPortal, flushSync } from 'react-dom';
3
+ import { getSharedScrollbarSettings, subscribeSharedScrollbarSettings, ReactTooltip, setSharedScrollbarSettings, ReactImagePreview, ReactCheckbox, ReactSortableList, ReactInputNumber, ReactSelect, ReactPagination, ReactFilterPanel, ReactPopup, ReactScrollbarSettingsDialog, ReactScrollbarPositionDialog, ReactModal, ReactButton, ReactInput, ReactPopconfirm, ReactContentScaleProvider, ReactColorPicker, resolveReactScrollbarMode, ReactProgress, ReactAutoComplete, ReactTimeRangePicker, ReactDateTimeRangePicker, ReactDateTimePicker, ReactTimePicker, ReactDateRangePicker, ReactDatePicker, ReactMonthPicker, ReactQuarterPicker, ReactYearPicker, ReactInputRange, ReactCascader, ReactTreeSelect } from '@canvas-components/react-components';
4
+ import { readPrintDesignTemplates, isPrintDesignDocumentCompatible, isPrintDesignTemplateInvalid, ReactPrintDesign } from '@canvas-components/react-print-design';
5
+ import { EXPAND_COLUMN, SELECTION_COLUMN, ROW_SELECTION_COLUMN, resetListTableStoredColumnConfig, ListTable, getListTableStoredColumnConfig, saveListTableStoredColumnConfig } from '@canvas-components/list-table';
6
6
  export { EXPAND_COLUMN, ROW_SELECTION_COLUMN, SELECTION_COLUMN, getListTableStoredPageSize } from '@canvas-components/list-table';
7
7
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
8
8
  import { CanvasChart } from '@canvas-components/chart';
9
9
  import { formatDateTimeValue, isDateTimeFormatPattern } from '@canvas-components/utils/date';
10
10
  import { formatNumberValue, formatPercentValue, formatScientificValue, formatCustomNumberPattern } from '@canvas-components/utils/number';
11
+ import { mapInBatches } from '@canvas-components/utils';
12
+ import { transformPrintDesignToHtmlBatches, createPrintDesignPage, createPrintDesignDocument } from '@canvas-components/print-design';
13
+ import { BROWSER_PRINT_PAPER_SIZES } from '@canvas-components/print';
14
+ import { createRoot } from 'react-dom/client';
11
15
 
12
16
  // #style-inject:#style-inject
13
17
  function styleInject(css, { insertAt } = {}) {
@@ -32,7 +36,7 @@ function styleInject(css, { insertAt } = {}) {
32
36
  }
33
37
 
34
38
  // src/styles/table-overrides.css
35
- styleInject(".react-list-table-filter-option {\n transition: background-color 0.2s ease;\n}\n.react-list-table-filter-option:hover {\n background: #f3f4f6;\n}\n.react-list-table-menu-action,\n.react-list-table-menu-row {\n font-size: 14px;\n transition: background-color 0.2s ease, color 0.2s ease;\n}\n.react-list-table-menu-action:hover,\n.react-list-table-menu-row:hover {\n background: #f3f4f6 !important;\n}\n.react-list-table-custom-selection-copy {\n transition: background-color 0.2s ease;\n}\n.react-list-table-custom-selection-copy:hover {\n background: #4096ff !important;\n}\n.react-list-table-custom-selection-input > .canvas-react-input-number-handler-down {\n display: none;\n}\n.react-list-table-menu-chevron {\n width: 7px;\n height: 7px;\n flex: 0 0 auto;\n margin-left: 8px;\n border-top: 1.5px solid #9ca3af;\n border-right: 1.5px solid #9ca3af;\n transform: rotate(45deg);\n transform-origin: center;\n transition: transform 0.18s ease, border-color 0.18s ease;\n}\n.react-list-table-menu-action:hover .react-list-table-menu-chevron,\n.react-list-table-menu-row:hover .react-list-table-menu-chevron {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n.react-list-table-menu-chevron-active {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n");
39
+ styleInject(".react-list-table-host {\n touch-action: none;\n min-width: 0;\n min-height: 0;\n}\n.react-list-table-filter-option {\n transition: background-color 0.2s ease;\n}\n.react-list-table-filter-option:hover {\n background: #f3f4f6;\n}\n.react-list-table-menu-action,\n.react-list-table-menu-row {\n font-size: 14px;\n transition: background-color 0.2s ease, color 0.2s ease;\n}\n.react-list-table-menu-action:hover,\n.react-list-table-menu-row:hover {\n background: #f3f4f6 !important;\n}\n.react-list-table-custom-selection-copy {\n transition: background-color 0.2s ease;\n}\n.react-list-table-custom-selection-copy:hover {\n background: #4096ff !important;\n}\n.react-list-table-custom-selection-input > .canvas-react-input-number-handler-down {\n display: none;\n}\n.react-list-table-menu-chevron {\n width: 7px;\n height: 7px;\n flex: 0 0 auto;\n margin-left: 8px;\n border-top: 1.5px solid #9ca3af;\n border-right: 1.5px solid #9ca3af;\n transform: rotate(45deg);\n transform-origin: center;\n transition: transform 0.18s ease, border-color 0.18s ease;\n}\n.react-list-table-menu-action:hover .react-list-table-menu-chevron,\n.react-list-table-menu-row:hover .react-list-table-menu-chevron {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n.react-list-table-menu-chevron-active {\n border-color: #6b7280;\n transform: rotate(135deg);\n}\n");
36
40
  var conditionalOperators = {
37
41
  eq: "\u7B49\u4E8E",
38
42
  neq: "\u4E0D\u7B49\u4E8E",
@@ -948,11 +952,20 @@ function ReactTableContextMenuView(props) {
948
952
  }
949
953
  ),
950
954
  /* @__PURE__ */ jsx(ResetMenu, { request }),
951
- buildCacheMenuItems(request).length > 0 ? /* @__PURE__ */ jsx(
955
+ request.columnSettingsCacheAvailable === true && buildCacheMenuItems(request).length > 0 ? /* @__PURE__ */ jsx(
952
956
  MenuSubmenu,
953
957
  {
954
958
  label: "\u7F13\u5B58\u5230\u672C\u5730",
955
- items: buildCacheMenuItems(request)
959
+ items: [
960
+ ...buildCacheMenuItems(request),
961
+ {
962
+ key: "view-cache-config",
963
+ label: "\u67E5\u770B\u7F13\u5B58\u914D\u7F6E",
964
+ separatorBefore: true,
965
+ disabled: request.columnSettingsCacheAvailable !== true,
966
+ onClick: props.onViewCacheConfig
967
+ }
968
+ ]
956
969
  }
957
970
  ) : null
958
971
  ]
@@ -1049,22 +1062,41 @@ function ReactTableContextMenuView(props) {
1049
1062
  }
1050
1063
  ),
1051
1064
  /* @__PURE__ */ jsx(MenuAction, { label: "\u67E5\u627E", onClick: request.onOpenSearch }),
1052
- /* @__PURE__ */ jsx(
1065
+ request.onPrint ? /* @__PURE__ */ jsx(
1066
+ MenuAction,
1067
+ {
1068
+ label: "\u6253\u5370",
1069
+ onClick: () => {
1070
+ setMenuOpen(false);
1071
+ request.onPrint?.();
1072
+ }
1073
+ }
1074
+ ) : /* @__PURE__ */ jsx(
1053
1075
  MenuSubmenu,
1054
1076
  {
1055
1077
  label: "\u6253\u5370",
1056
1078
  items: [
1057
- {
1058
- key: "print-direct",
1059
- label: "\u76F4\u63A5\u6253\u5370",
1060
- onClick: request.onGetPrintData ? () => {
1079
+ ...props.printTemplates && props.printTemplates.length > 1 ? props.printTemplates.map((template) => ({
1080
+ key: `print-template-${template.key}`,
1081
+ label: `\u76F4\u63A5\u6253\u5370 ${template.label}`,
1082
+ disabled: template.disabled,
1083
+ onClick: template.disabled ? void 0 : () => {
1061
1084
  setMenuOpen(false);
1062
- props.onDirectPrint?.();
1063
- } : void 0
1064
- },
1085
+ props.onPrintTemplate?.(template.key);
1086
+ }
1087
+ })) : [
1088
+ {
1089
+ key: "print-direct",
1090
+ label: "\u76F4\u63A5\u6253\u5370",
1091
+ onClick: request.onGetPrintData ? () => {
1092
+ setMenuOpen(false);
1093
+ props.onDirectPrint?.();
1094
+ } : void 0
1095
+ }
1096
+ ],
1065
1097
  {
1066
1098
  key: "print-configure",
1067
- label: "\u6253\u5370\u914D\u7F6E",
1099
+ label: "\u6253\u5370\u8BBE\u8BA1",
1068
1100
  onClick: request.onGetPrintData ? () => {
1069
1101
  setMenuOpen(false);
1070
1102
  props.onPrint?.();
@@ -1073,6 +1105,14 @@ function ReactTableContextMenuView(props) {
1073
1105
  ]
1074
1106
  }
1075
1107
  ),
1108
+ request.customItems?.map((item) => /* @__PURE__ */ jsx(
1109
+ CustomContextMenuItem,
1110
+ {
1111
+ item,
1112
+ onClose: () => setMenuOpen(false)
1113
+ },
1114
+ item.key
1115
+ )),
1076
1116
  /* @__PURE__ */ jsx(
1077
1117
  MenuAction,
1078
1118
  {
@@ -1347,13 +1387,21 @@ function ReactTableContextMenuView(props) {
1347
1387
  }
1348
1388
  ),
1349
1389
  /* @__PURE__ */ jsx(ZoomMenu, { value: request.zoom ?? 1, onChange: request.onChangeZoom }),
1350
- /* @__PURE__ */ jsx(
1390
+ request.columnSettingsCacheAvailable === true ? /* @__PURE__ */ jsx(
1351
1391
  MenuSubmenu,
1352
1392
  {
1353
1393
  label: "\u7F13\u5B58\u5230\u672C\u5730",
1354
- items: buildCacheMenuItems(request)
1394
+ items: [
1395
+ ...buildCacheMenuItems(request),
1396
+ {
1397
+ key: "view-cache-config",
1398
+ label: "\u67E5\u770B\u7F13\u5B58\u914D\u7F6E",
1399
+ separatorBefore: true,
1400
+ onClick: props.onViewCacheConfig
1401
+ }
1402
+ ]
1355
1403
  }
1356
- ),
1404
+ ) : null,
1357
1405
  request.columns && request.onToggleColumn ? /* @__PURE__ */ jsxs(Fragment, { children: [
1358
1406
  /* @__PURE__ */ jsx(MenuDivider, {}),
1359
1407
  /* @__PURE__ */ jsx(
@@ -1498,6 +1546,31 @@ function MenuAction(props) {
1498
1546
  }
1499
1547
  );
1500
1548
  }
1549
+ function CustomContextMenuItem(props) {
1550
+ const { item, onClose } = props;
1551
+ const children = item.children?.filter(Boolean) ?? [];
1552
+ if (children.length > 0) {
1553
+ return /* @__PURE__ */ jsx(MenuSubmenu, { label: item.label, items: [], minWidth: 240, children: children.map((child) => /* @__PURE__ */ jsx(
1554
+ CustomContextMenuItem,
1555
+ {
1556
+ item: child,
1557
+ onClose
1558
+ },
1559
+ child.key
1560
+ )) });
1561
+ }
1562
+ return /* @__PURE__ */ jsx(
1563
+ MenuAction,
1564
+ {
1565
+ label: item.label,
1566
+ disabled: item.disabled,
1567
+ onClick: item.disabled ? void 0 : () => {
1568
+ onClose();
1569
+ item.onClick?.();
1570
+ }
1571
+ }
1572
+ );
1573
+ }
1501
1574
  function MenuSubmenu(props) {
1502
1575
  const { label, items, header, footer, children, minWidth = 220 } = props;
1503
1576
  const menuItems = items.filter((item) => item.onClick || item.disabled);
@@ -3457,6 +3530,107 @@ var moreFormatChoiceSelectedStyle = {
3457
3530
  background: "#f3f4f6",
3458
3531
  color: "#1677ff"
3459
3532
  };
3533
+ function ReactListTableCacheConfigDialog(props) {
3534
+ const [rows, setRows] = useState([]);
3535
+ const [config, setConfig] = useState(null);
3536
+ const [loading, setLoading] = useState(true);
3537
+ useEffect(() => {
3538
+ let active = true;
3539
+ setLoading(true);
3540
+ void getListTableStoredColumnConfig(props.storageKey).then((config2) => {
3541
+ if (!active) return;
3542
+ setConfig(config2 ?? null);
3543
+ setRows(
3544
+ config2 ? createCacheConfigRows(config2) : []
3545
+ );
3546
+ setLoading(false);
3547
+ });
3548
+ return () => {
3549
+ active = false;
3550
+ };
3551
+ }, [props.storageKey]);
3552
+ const removeRow = (key) => {
3553
+ if (!config) return;
3554
+ const next = removeCacheConfigKey(config, key);
3555
+ setConfig(next);
3556
+ setRows((current) => current.filter((row) => row.key !== key));
3557
+ void saveListTableStoredColumnConfig(props.storageKey, next);
3558
+ };
3559
+ return /* @__PURE__ */ jsx(
3560
+ ReactModal,
3561
+ {
3562
+ open: true,
3563
+ title: "\u7F13\u5B58\u914D\u7F6E",
3564
+ width: 1180,
3565
+ fullscreenable: true,
3566
+ onCancel: props.onClose,
3567
+ footer: false,
3568
+ children: loading ? /* @__PURE__ */ jsx("div", { style: { padding: 24, textAlign: "center", color: "#64748b" }, children: "\u6B63\u5728\u8BFB\u53D6\u7F13\u5B58\u914D\u7F6E\u2026" }) : rows.length === 0 ? /* @__PURE__ */ jsx("div", { style: { padding: 24, textAlign: "center", color: "#64748b" }, children: "\u5F53\u524D\u6CA1\u6709\u7F13\u5B58\u914D\u7F6E" }) : props.renderTable({
3569
+ tableId: "list-table-cache-config",
3570
+ columns: [
3571
+ {
3572
+ key: "key",
3573
+ dataIndex: "key",
3574
+ title: "\u914D\u7F6E\u9879",
3575
+ width: 190,
3576
+ cellStyle: { verticalAlign: "top" }
3577
+ },
3578
+ {
3579
+ key: "value",
3580
+ dataIndex: "value",
3581
+ title: "\u7F13\u5B58\u503C",
3582
+ width: 850,
3583
+ cellType: "json",
3584
+ cellStyle: {
3585
+ verticalAlign: "top",
3586
+ fontFamily: "monospace",
3587
+ fontSize: 12
3588
+ }
3589
+ },
3590
+ {
3591
+ key: "operation",
3592
+ dataIndex: "key",
3593
+ title: "\u64CD\u4F5C",
3594
+ width: 90,
3595
+ render: (_value, row) => ({
3596
+ type: "button",
3597
+ text: "\u79FB\u9664",
3598
+ buttonType: "link",
3599
+ size: "small",
3600
+ danger: true,
3601
+ popconfirm: {
3602
+ title: "\u786E\u8BA4\u79FB\u9664\u7F13\u5B58\u914D\u7F6E\u5417\uFF1F",
3603
+ description: `\u5C06\u79FB\u9664\u201C${row.key}\u201D\u914D\u7F6E\u9879\u3002`,
3604
+ okText: "\u79FB\u9664",
3605
+ cancelText: "\u53D6\u6D88"
3606
+ },
3607
+ cursor: "pointer",
3608
+ onClick: () => removeRow(row.key)
3609
+ })
3610
+ }
3611
+ ],
3612
+ dataSource: rows,
3613
+ rowKey: "key",
3614
+ rowHeight: "auto",
3615
+ pagination: false,
3616
+ toolbar: false,
3617
+ contextMenu: false,
3618
+ style: { width: "100%", height: "min(68vh, 620px)" }
3619
+ })
3620
+ }
3621
+ );
3622
+ }
3623
+ function createCacheConfigRows(config) {
3624
+ return Object.entries(config).map(([key, value]) => ({
3625
+ key,
3626
+ value
3627
+ }));
3628
+ }
3629
+ function removeCacheConfigKey(config, key) {
3630
+ const next = { ...config };
3631
+ delete next[key];
3632
+ return next;
3633
+ }
3460
3634
  var SEARCH_MODAL_BODY_STYLE = {
3461
3635
  padding: 12
3462
3636
  };
@@ -3834,6 +4008,8 @@ function ReactListTableExportNameDialog({
3834
4008
  if (request.columns && !selectedKeys.length) return setError("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u5217");
3835
4009
  if (request.columns && cacheEnabled) {
3836
4010
  writeExportCache(storageKey, { version: 1, rows, selectedKeys: selectedKeys.map(String), includeSummary });
4011
+ } else if (request.columns) {
4012
+ writeExportCache(storageKey, null);
3837
4013
  }
3838
4014
  request.onConfirm(
3839
4015
  `${fileName.trim()}${extension}`,
@@ -3989,32 +4165,6 @@ function createPrintColumns(columns, summaryColumns = []) {
3989
4165
  };
3990
4166
  });
3991
4167
  }
3992
- function readPrintSettings(storageKey) {
3993
- if (!storageKey) return null;
3994
- try {
3995
- const value = JSON.parse(
3996
- localStorage.getItem(`canvas-table-print:${storageKey}`) ?? "null"
3997
- );
3998
- if (!value || typeof value !== "object") return null;
3999
- const settings = value;
4000
- if (settings.version !== 2 || settings.presetName !== void 0 && typeof settings.presetName !== "string" || !Array.isArray(settings.columns) || !settings.columns.every(
4001
- (column) => column && typeof column.key === "string" && Number.isFinite(column.width) && column.width >= 10
4002
- ) || !Array.isArray(settings.selectedKeys) || !settings.selectedKeys.every((key) => typeof key === "string") || !Number.isInteger(settings.rowsPerPage) || settings.rowsPerPage < 1)
4003
- return null;
4004
- return settings;
4005
- } catch {
4006
- return null;
4007
- }
4008
- }
4009
- function writePrintSettings(storageKey, settings) {
4010
- if (!storageKey) return;
4011
- try {
4012
- const key = `canvas-table-print:${storageKey}`;
4013
- if (settings) localStorage.setItem(key, JSON.stringify(settings));
4014
- else localStorage.removeItem(key);
4015
- } catch {
4016
- }
4017
- }
4018
4168
  function restorePrintColumns(columns, settings) {
4019
4169
  const current = new Map(columns.map((column) => [column.key, column]));
4020
4170
  const restored = [];
@@ -4027,1525 +4177,737 @@ function restorePrintColumns(columns, settings) {
4027
4177
  return [...restored, ...current.values()];
4028
4178
  }
4029
4179
  function resolvePrintSelectedKeys(sourceColumns, presetConfig, cached) {
4030
- const cachedColumnKeys = new Set(cached?.columns.map((column) => column.key));
4180
+ new Set(cached?.columns.map((column) => column.key));
4031
4181
  const presetKeys = presetConfig?.selectedKeys ? new Set(presetConfig.selectedKeys) : null;
4032
4182
  return sourceColumns.filter((column) => {
4033
- if (cached) {
4034
- return cachedColumnKeys.has(column.key) ? cached.selectedKeys.includes(column.key) : true;
4035
- }
4036
4183
  if (presetKeys) return presetKeys.has(column.key);
4037
4184
  return true;
4038
4185
  }).map((column) => column.key);
4039
4186
  }
4040
- function selectPrintColumns(columns, selectedKeys) {
4041
- const selected = new Set(selectedKeys.map(String));
4042
- return columns.filter((column) => selected.has(column.key));
4043
- }
4044
- function createCachedPrintTableOptions(source, storageKey, printPresets = []) {
4045
- const cached = readPrintSettings(storageKey);
4046
- const selectedPreset = cached?.presetName ? printPresets.find((preset) => preset.name === cached.presetName) : cached ? void 0 : printPresets[0];
4047
- const config = selectedPreset?.config;
4048
- const settings = { ...config, ...cached };
4187
+ function createTableDocument(source, preset) {
4188
+ const config = preset?.config;
4189
+ const page = createPrintDesignPage("\u8868\u683C\u6253\u5370");
4190
+ const [paperWidth, paperHeight] = BROWSER_PRINT_PAPER_SIZES[config?.paperSize ?? "a4"];
4191
+ page.width = config?.orientation === "landscape" ? paperHeight : paperWidth;
4192
+ page.height = config?.orientation === "landscape" ? paperWidth : paperHeight;
4193
+ page.orientation = config?.orientation ?? "portrait";
4194
+ page.margins = config?.pageMargins ?? page.margins;
4195
+ const selectedKeys = resolvePrintSelectedKeys(source.columns, config);
4049
4196
  const columns = restorePrintColumns(
4050
- restorePrintColumns(
4051
- createPrintColumns(source.columns, source.summaryColumns),
4052
- config?.columns ? { columns: config.columns } : null
4053
- ),
4054
- cached
4197
+ createPrintColumns(source.columns, source.summaryColumns),
4198
+ config?.columns ? { columns: config.columns } : null
4199
+ ).filter((column) => selectedKeys.includes(column.key));
4200
+ const left = page.margins?.left ?? 10;
4201
+ const right = page.margins?.right ?? 10;
4202
+ const bottom = page.margins?.bottom ?? 10;
4203
+ const contentWidth = page.width - left - right;
4204
+ const widthSum = columns.reduce((sum, column) => sum + column.width, 0);
4205
+ const rowHeight = config?.rowHeight ?? 8;
4206
+ const footerY = page.height - bottom - 6;
4207
+ const tableY = 28;
4208
+ const tableHeight = footerY - tableY - 8;
4209
+ const rowsPerPage = Math.max(1, Math.floor((tableHeight - 9) / rowHeight));
4210
+ const pageCount = Math.max(1, Math.ceil(source.rows.length / rowsPerPage));
4211
+ const printedAt = (/* @__PURE__ */ new Date()).toLocaleString("zh-CN");
4212
+ const pages = Array.from(
4213
+ { length: pageCount },
4214
+ (_, index) => ({
4215
+ ...page,
4216
+ id: index === 0 ? page.id : createPrintDesignPage().id,
4217
+ name: `\u8868\u683C\u6253\u5370 ${index + 1}`,
4218
+ elements: [
4219
+ {
4220
+ id: `list-table-title-${index}`,
4221
+ type: "text",
4222
+ name: "\u6807\u9898",
4223
+ x: left,
4224
+ y: 12,
4225
+ width: contentWidth,
4226
+ height: 10,
4227
+ text: config?.title ?? "\u8868\u683C\u6253\u5370",
4228
+ fontSize: config?.titleFontSize ?? 18,
4229
+ fontWeight: "700",
4230
+ textAlign: "center"
4231
+ },
4232
+ {
4233
+ id: `list-table-data-${index}`,
4234
+ type: "table",
4235
+ name: "\u8868\u683C\u6570\u636E",
4236
+ x: left,
4237
+ y: tableY,
4238
+ width: contentWidth,
4239
+ height: tableHeight,
4240
+ dataMode: "binding",
4241
+ dataPath: `listTable.pages.${index}.rows`,
4242
+ dataSource: [],
4243
+ columns: columns.map((column) => ({
4244
+ key: column.key,
4245
+ title: column.title,
4246
+ dataIndex: column.key,
4247
+ width: widthSum > 0 ? Math.max(24, column.width / widthSum * 720) : 120,
4248
+ align: column.align
4249
+ })),
4250
+ rowHeight,
4251
+ headerRowHeight: 9,
4252
+ showHeader: true
4253
+ },
4254
+ {
4255
+ id: `list-table-print-time-${index}`,
4256
+ type: "text",
4257
+ name: "\u6253\u5370\u65F6\u95F4",
4258
+ x: left,
4259
+ y: footerY,
4260
+ width: contentWidth / 2,
4261
+ height: 6,
4262
+ text: `\u6253\u5370\u65F6\u95F4\uFF1A${printedAt}`,
4263
+ fontSize: 9,
4264
+ color: "#000000"
4265
+ },
4266
+ {
4267
+ id: `list-table-page-number-${index}`,
4268
+ type: "text",
4269
+ name: "\u5206\u9875\u4FE1\u606F",
4270
+ x: left + contentWidth / 2,
4271
+ y: footerY,
4272
+ width: contentWidth / 2,
4273
+ height: 6,
4274
+ text: `\u7B2C ${index + 1} \u9875 / \u5171 ${pageCount} \u9875`,
4275
+ textAlign: "right",
4276
+ fontSize: 9,
4277
+ color: "#000000"
4278
+ }
4279
+ ]
4280
+ })
4055
4281
  );
4056
- const selectedKeys = resolvePrintSelectedKeys(source.columns, config, cached);
4057
- return configurePrintTable(
4058
- source,
4059
- columns,
4060
- selectedKeys,
4061
- settings.rowsPerPage ?? 30,
4062
- {
4063
- includeSummary: settings.includeSummary ?? Boolean(
4064
- source.summaryRow || source.summaryColumns?.some(
4065
- (column) => column.summary.type !== "none"
4066
- )
4067
- ),
4068
- rowHeight: settings.rowHeight ?? 8,
4069
- title: settings.title ?? "\u8868\u683C\u6253\u5370",
4070
- subtitle: settings.subtitle ?? "",
4071
- titleFontSize: settings.titleFontSize ?? 18,
4072
- subtitleFontSize: settings.subtitleFontSize ?? 12,
4073
- summaryPosition: settings.summaryPosition ?? "last-page",
4074
- showPageNumbers: settings.showPageNumbers ?? false,
4075
- pageNumberPosition: settings.pageNumberPosition ?? "right",
4076
- showPrintTime: settings.showPrintTime ?? false,
4077
- printTimePosition: settings.printTimePosition ?? "left",
4078
- pageMargins: settings.pageMargins ?? {
4079
- top: 12,
4080
- right: 12,
4081
- bottom: 12,
4082
- left: 12
4083
- },
4084
- paperSize: settings.paperSize ?? "a4",
4085
- orientation: settings.orientation ?? "portrait",
4086
- useCellBorders: settings.useCellBorders ?? true,
4087
- borderColor: settings.borderColor ?? "#000000",
4088
- textColor: settings.textColor ?? "#000000",
4089
- headerBackgroundColor: settings.headerBackgroundColor ?? "#f2f5f8",
4090
- summaryBackgroundColor: settings.summaryBackgroundColor ?? "#f2f5f8"
4091
- }
4282
+ return createPrintDesignDocument({
4283
+ name: preset?.name ?? "\u8868\u683C\u6253\u5370\u6A21\u677F",
4284
+ pages
4285
+ });
4286
+ }
4287
+ function mapPrintSourceRows(source) {
4288
+ return source.rows.map(
4289
+ (row) => Object.fromEntries(
4290
+ source.columns.map((column, index) => [column.key, row[index] ?? ""])
4291
+ )
4092
4292
  );
4093
4293
  }
4094
- async function configurePrintTable(source, columns, selectedKeys, rowsPerPage, settings = {}) {
4095
- const selected = new Set(selectedKeys);
4096
- const activeColumns = columns.filter((column) => selected.has(column.key));
4097
- const sourceIndexes = new Map(
4098
- source.columns.map((column, index) => [column.key, index])
4294
+ function mapPrintSourceRowsAsync(source, options) {
4295
+ return mapInBatches(
4296
+ source.rows.length,
4297
+ (index) => Object.fromEntries(
4298
+ source.columns.map((column, columnIndex) => [
4299
+ column.key,
4300
+ source.rows[index][columnIndex] ?? ""
4301
+ ])
4302
+ ),
4303
+ options
4099
4304
  );
4100
- const summaries = Object.fromEntries(
4101
- activeColumns.map((column) => [
4102
- column.key,
4103
- {
4104
- type: column.summaryType ?? "none",
4105
- showTypePrefix: column.showTypePrefix === "yes",
4106
- precision: column.precision,
4107
- roundingMode: column.roundingMode ?? "round",
4108
- preserveTrailingZeros: column.preserveTrailingZeros === "yes"
4109
- }
4110
- ])
4305
+ }
4306
+ function getRowsPerPage(document2, rowCount) {
4307
+ const firstTable = document2.pages[0]?.elements.find(
4308
+ (item) => item.type === "table"
4111
4309
  );
4112
- const summaryRow = settings.includeSummary ? source.getSummaryRow ? await source.getSummaryRow({
4113
- columnKeys: activeColumns.map((column) => column.key),
4114
- sortState: [],
4115
- summaries,
4116
- includeSummary: true
4117
- }) : source.summaryRow ? activeColumns.map(
4118
- (column) => source.summaryRow?.[sourceIndexes.get(column.key) ?? -1] ?? ""
4119
- ) : void 0 : void 0;
4310
+ return firstTable?.type === "table" ? Math.max(
4311
+ 1,
4312
+ Math.floor(
4313
+ (firstTable.height - (firstTable.showHeader === false ? 0 : firstTable.headerRowHeight ?? 9)) / (firstTable.rowHeight ?? 8)
4314
+ )
4315
+ ) : Math.max(1, Math.ceil(rowCount / Math.max(1, document2.pages.length)));
4316
+ }
4317
+ function getPrintDesignSampleSize() {
4318
+ return getRowsPerPage(createTableDocument({ columns: [], rows: [] }), 0);
4319
+ }
4320
+ function createTableData(source, document2, rows = mapPrintSourceRows(source)) {
4321
+ const rowsPerPage = getRowsPerPage(document2, rows.length);
4120
4322
  return {
4121
- title: settings.title ?? "\u8868\u683C\u6253\u5370",
4122
- ...settings.subtitle ? { subtitle: settings.subtitle } : {},
4123
- titleFontSize: settings.titleFontSize ?? 18,
4124
- subtitleFontSize: settings.subtitleFontSize ?? 12,
4125
- columns: activeColumns.map(({ key, title, width, align, overflow }) => ({
4126
- key,
4127
- title,
4128
- width,
4129
- ...align ? { align } : {},
4130
- ...overflow ? { overflow } : {}
4131
- })),
4132
- rows: source.rows.map(
4133
- (row) => activeColumns.map(
4134
- (column) => row[sourceIndexes.get(column.key) ?? -1] ?? ""
4135
- )
4136
- ),
4137
- rowsPerPage,
4138
- rowHeight: settings.rowHeight ?? 8,
4139
- ...settings.includeSummary && summaryRow ? {
4140
- summaryRow: activeColumns.map(
4141
- (_column, index) => summaryRow[index] ?? ""
4142
- ),
4143
- summaryPosition: settings.summaryPosition ?? "last-page"
4144
- } : {},
4145
- showPageNumbers: settings.showPageNumbers ?? false,
4146
- pageNumberPosition: settings.pageNumberPosition ?? "right",
4147
- showPrintTime: settings.showPrintTime ?? false,
4148
- printTimePosition: settings.printTimePosition ?? "left",
4149
- pageMargins: settings.pageMargins ?? {
4150
- top: 12,
4151
- right: 12,
4152
- bottom: 12,
4153
- left: 12
4154
- },
4155
- paperSize: settings.paperSize ?? "a4",
4156
- orientation: settings.orientation ?? "portrait",
4157
- useCellBorders: settings.useCellBorders ?? true,
4158
- borderColor: settings.borderColor ?? "#000000",
4159
- textColor: settings.textColor ?? "#000000",
4160
- headerBackgroundColor: settings.headerBackgroundColor ?? "#f2f5f8",
4161
- summaryBackgroundColor: settings.summaryBackgroundColor ?? "#f2f5f8"
4323
+ listTable: {
4324
+ rows,
4325
+ columns: source.columns.map((column) => ({ key: column.key })),
4326
+ pages: document2.pages.map((_, index) => ({
4327
+ rows: rows.slice(index * rowsPerPage, (index + 1) * rowsPerPage)
4328
+ }))
4329
+ }
4162
4330
  };
4163
4331
  }
4164
- var summaryOptions2 = [
4165
- { value: "original", label: "\u539F\u6C47\u603B" },
4166
- { value: "none", label: "\u65E0\u6C47\u603B" },
4167
- { value: "title", label: "\u6807\u9898" },
4168
- { value: "sum", label: "\u6C42\u548C" },
4169
- { value: "avg", label: "\u5E73\u5747\u503C" },
4170
- { value: "count", label: "\u8BA1\u6570" },
4171
- { value: "unionCount", label: "\u53BB\u91CD\u8BA1\u6570" },
4172
- { value: "rowCount", label: "\u884C\u6570" },
4173
- { value: "mergedRowCount", label: "\u5408\u5E76\u884C\u6570" },
4174
- { value: "min", label: "\u6700\u5C0F\u503C" },
4175
- { value: "max", label: "\u6700\u5927\u503C" }
4176
- ];
4177
- var roundingOptions2 = [
4178
- { value: "round", label: "\u56DB\u820D\u4E94\u5165" },
4179
- { value: "floor", label: "\u5411\u4E0B\u53D6\u6574" },
4180
- { value: "ceil", label: "\u5411\u4E0A\u53D6\u6574" },
4181
- { value: "truncate", label: "\u622A\u65AD" }
4182
- ];
4183
- var booleanOptions2 = [
4184
- { value: "yes", label: "\u662F" },
4185
- { value: "no", label: "\u5426" }
4186
- ];
4187
- function ReactListTablePrintDialog(props) {
4188
- const tableId = useId();
4189
- const [cached] = useState(() => readPrintSettings(props.storageKey));
4190
- const initialPreset = cached?.presetName ? props.printPresets?.find((preset) => preset.name === cached.presetName) : cached ? void 0 : props.printPresets?.[0];
4191
- const initialConfig = initialPreset?.config;
4192
- const [columns, setColumns] = useState(
4193
- () => restorePrintColumns(
4194
- restorePrintColumns(
4195
- createPrintColumns(props.source.columns, props.source.summaryColumns),
4196
- initialConfig?.columns ? { columns: initialConfig.columns } : null
4197
- ),
4198
- cached
4199
- )
4200
- );
4201
- const [selectedKeys, setSelectedKeys] = useState(
4202
- () => resolvePrintSelectedKeys(props.source.columns, initialConfig, cached)
4203
- );
4204
- const [rowsPerPage, setRowsPerPage] = useState(
4205
- cached?.rowsPerPage ?? initialConfig?.rowsPerPage ?? 30
4206
- );
4207
- const [rowHeight, setRowHeight] = useState(
4208
- cached?.rowHeight ?? initialConfig?.rowHeight ?? 8
4209
- );
4210
- const [autoCalculateRows, setAutoCalculateRows] = useState(
4211
- cached?.autoCalculateRowsPerPage ?? initialConfig?.autoCalculateRowsPerPage ?? false
4212
- );
4213
- const [title, setTitle] = useState(
4214
- cached?.title ?? initialConfig?.title ?? "\u8868\u683C\u6253\u5370"
4215
- );
4216
- const [subtitle, setSubtitle] = useState(
4217
- cached?.subtitle ?? initialConfig?.subtitle ?? ""
4218
- );
4219
- const [titleFontSize, setTitleFontSize] = useState(
4220
- cached?.titleFontSize ?? initialConfig?.titleFontSize ?? 18
4221
- );
4222
- const [subtitleFontSize, setSubtitleFontSize] = useState(
4223
- cached?.subtitleFontSize ?? initialConfig?.subtitleFontSize ?? 12
4224
- );
4225
- const [includeSummary, setIncludeSummary] = useState(
4226
- () => cached?.includeSummary ?? initialConfig?.includeSummary ?? Boolean(
4227
- props.source.summaryRow || props.source.summaryColumns?.some(
4228
- (column) => column.summary.type !== "none"
4229
- )
4230
- )
4231
- );
4232
- const [summaryPosition, setSummaryPosition] = useState(cached?.summaryPosition ?? initialConfig?.summaryPosition ?? "last-page");
4233
- const [showPageNumbers, setShowPageNumbers] = useState(
4234
- cached?.showPageNumbers ?? initialConfig?.showPageNumbers ?? false
4235
- );
4236
- const [pageNumberPosition, setPageNumberPosition] = useState(cached?.pageNumberPosition ?? initialConfig?.pageNumberPosition ?? "right");
4237
- const [showPrintTime, setShowPrintTime] = useState(
4238
- cached?.showPrintTime ?? initialConfig?.showPrintTime ?? false
4332
+ async function createTableDataAsync(source, document2, rows, options) {
4333
+ const rowsPerPage = getRowsPerPage(document2, rows.length);
4334
+ const pages = await mapInBatches(
4335
+ document2.pages.length,
4336
+ (index) => ({
4337
+ rows: rows.slice(index * rowsPerPage, (index + 1) * rowsPerPage)
4338
+ }),
4339
+ options
4239
4340
  );
4240
- const [printTimePosition, setPrintTimePosition] = useState(cached?.printTimePosition ?? initialConfig?.printTimePosition ?? "left");
4241
- const [pageMargins, setPageMargins] = useState(
4242
- cached?.pageMargins ?? initialConfig?.pageMargins ?? {
4243
- top: 12,
4244
- right: 12,
4245
- bottom: 12,
4246
- left: 12
4341
+ return {
4342
+ listTable: {
4343
+ rows,
4344
+ columns: source.columns.map((column) => ({ key: column.key })),
4345
+ pages
4247
4346
  }
4347
+ };
4348
+ }
4349
+ async function createTablePreviewSource(source, template, options, preparedRows) {
4350
+ const rows = preparedRows ?? await mapPrintSourceRowsAsync(source, options);
4351
+ const pageCount = Math.max(
4352
+ template.pages.length,
4353
+ Math.ceil(rows.length / getRowsPerPage(template, rows.length))
4248
4354
  );
4249
- const [paperSize, setPaperSize] = useState(cached?.paperSize ?? initialConfig?.paperSize ?? "a4");
4250
- const [orientation, setOrientation] = useState(
4251
- cached?.orientation ?? initialConfig?.orientation ?? "portrait"
4252
- );
4253
- const [useCellBorders, setUseCellBorders] = useState(
4254
- cached?.useCellBorders ?? initialConfig?.useCellBorders ?? true
4255
- );
4256
- const [borderColor, setBorderColor] = useState(
4257
- cached?.borderColor ?? initialConfig?.borderColor ?? "#000000"
4258
- );
4259
- const [textColor, setTextColor] = useState(
4260
- cached?.textColor ?? initialConfig?.textColor ?? "#000000"
4261
- );
4262
- const [headerBackgroundColor, setHeaderBackgroundColor] = useState(
4263
- cached?.headerBackgroundColor ?? initialConfig?.headerBackgroundColor ?? "#f2f5f8"
4264
- );
4265
- const [summaryBackgroundColor, setSummaryBackgroundColor] = useState(
4266
- cached?.summaryBackgroundColor ?? initialConfig?.summaryBackgroundColor ?? "#f2f5f8"
4267
- );
4268
- const [selectedPresetName, setSelectedPresetName] = useState(
4269
- initialPreset?.name ?? ""
4270
- );
4271
- const [showSectionDividers, setShowSectionDividers] = useState(
4272
- cached?.showSectionDividers ?? initialConfig?.showSectionDividers ?? true
4273
- );
4274
- const [sectionDividerTopSpacing, setSectionDividerTopSpacing] = useState(
4275
- cached?.sectionDividerTopSpacing ?? initialConfig?.sectionDividerTopSpacing ?? 4
4276
- );
4277
- const [sectionDividerBottomSpacing, setSectionDividerBottomSpacing] = useState(
4278
- cached?.sectionDividerBottomSpacing ?? initialConfig?.sectionDividerBottomSpacing ?? 4
4355
+ const pages = await mapInBatches(
4356
+ pageCount,
4357
+ (index) => {
4358
+ const page = template.pages[index % template.pages.length];
4359
+ return {
4360
+ ...page,
4361
+ id: `${page.id}:print:${index}`,
4362
+ name: `\u8868\u683C\u6253\u5370 ${index + 1}`,
4363
+ elements: page.elements.map((element) => {
4364
+ const id = `${element.id}:print:${index}`;
4365
+ if (element.type === "table" && (element.dataPath === "listTable.rows" || /^listTable\.pages\.\d+\.rows$/.test(element.dataPath ?? ""))) {
4366
+ return {
4367
+ ...element,
4368
+ id,
4369
+ dataPath: `listTable.pages.${index}.rows`
4370
+ };
4371
+ }
4372
+ if (element.type === "text" && element.id.startsWith("list-table-page-number-")) {
4373
+ return {
4374
+ ...element,
4375
+ id,
4376
+ text: `\u7B2C ${index + 1} \u9875 / \u5171 ${pageCount} \u9875`
4377
+ };
4378
+ }
4379
+ return { ...element, id };
4380
+ })
4381
+ };
4382
+ },
4383
+ options
4279
4384
  );
4280
- const [formItemSpacing, setFormItemSpacing] = useState(
4281
- cached?.formItemSpacing ?? initialConfig?.formItemSpacing ?? 4
4385
+ const document2 = { ...template, pages };
4386
+ return {
4387
+ document: document2,
4388
+ data: await createTableDataAsync(source, document2, rows, options)
4389
+ };
4390
+ }
4391
+ function getPrintSourceFormat(source) {
4392
+ return JSON.stringify(
4393
+ source.columns.map((column) => String(column.key)).sort()
4282
4394
  );
4283
- const [cacheEnabled, setCacheEnabled] = useState(Boolean(props.storageKey));
4395
+ }
4396
+ function ReactListTablePrintDesignDialog(props) {
4397
+ const [fullscreen, setFullscreen] = useState(true);
4398
+ const [source, setSource] = useState(null);
4284
4399
  const [error, setError] = useState("");
4285
- const [previewHtml, setPreviewHtml] = useState("");
4286
- const [previewOpen, setPreviewOpen] = useState(false);
4287
- const [printFullscreen, setPrintFullscreen] = useState(false);
4288
- const [previewFullscreen, setPreviewFullscreen] = useState(false);
4289
- const [previewMode, setPreviewMode] = useState("render");
4290
- const [previewViewport, setPreviewViewport] = useState(() => ({
4291
- width: typeof window === "undefined" ? 1280 : window.innerWidth,
4292
- height: typeof window === "undefined" ? 900 : window.innerHeight
4293
- }));
4294
- const selectedColumnKeys = new Set(selectedKeys.map(String));
4295
- const layoutWarning = getBrowserPrintLayoutWarning({
4296
- columns: columns.filter((column) => selectedColumnKeys.has(column.key)),
4297
- pageMargins,
4298
- paperSize,
4299
- orientation
4300
- });
4301
- const rowLayoutWarning = getBrowserPrintRowLayoutWarning({
4302
- pageMargins,
4303
- paperSize,
4304
- orientation,
4305
- rowsPerPage,
4306
- rowHeight,
4307
- includeSummary,
4308
- summaryPosition,
4309
- showPageNumbers,
4310
- showPrintTime,
4311
- titleFontSize,
4312
- subtitleFontSize,
4313
- subtitle
4314
- });
4315
- const printLayoutWarning = [layoutWarning, rowLayoutWarning].filter(Boolean).join("\uFF1B");
4316
- const [paperWidth, paperHeight] = BROWSER_PRINT_PAPER_SIZES[paperSize];
4317
- const previewPaperWidth = orientation === "portrait" ? paperWidth : paperHeight;
4318
- const previewPaperHeight = orientation === "portrait" ? paperHeight : paperWidth;
4319
- const previewListHeight = Math.max(
4320
- 240,
4321
- previewFullscreen ? previewViewport.height - 168 : Math.min(previewViewport.height - 260, 900)
4322
- );
4323
- const previewListWidth = Math.max(
4324
- 320,
4325
- (previewFullscreen ? previewViewport.width : Math.min(previewViewport.width * 0.96, 1280)) - 48
4326
- );
4327
- const pageWidthPixels = previewPaperWidth * 96 / 25.4;
4328
- const pageHeightPixels = previewPaperHeight * 96 / 25.4;
4329
- const previewScale = Math.min(
4330
- 1,
4331
- (previewListWidth - 24) / pageWidthPixels,
4332
- (previewListHeight - 24) / pageHeightPixels
4400
+ const [attempt, setAttempt] = useState(0);
4401
+ const [emptyDocument] = useState(
4402
+ () => createPrintDesignDocument({ name: "\u8868\u683C\u6253\u5370\u6A21\u677F" })
4333
4403
  );
4334
- const previewItemHeight = Math.ceil(pageHeightPixels * previewScale + 24);
4335
- const previewPages = useMemo(() => {
4336
- if (!previewHtml || typeof DOMParser === "undefined") return [];
4337
- const parsed = new DOMParser().parseFromString(previewHtml, "text/html");
4338
- const styles = Array.from(parsed.head.querySelectorAll("style")).map((style) => style.outerHTML).join("");
4339
- const pages = Array.from(parsed.body.children).filter(
4340
- (element) => element.classList.contains("page")
4341
- );
4342
- const pageStyles = `<style>html,body{box-sizing:border-box;width:${pageWidthPixels}px;height:${pageHeightPixels}px;max-width:none;max-height:none;margin:0;padding:0;overflow:hidden;background:#fff}body{display:block}.page{box-sizing:border-box;flex:0 0 auto;width:${previewPaperWidth}mm;max-width:none;height:${previewPaperHeight}mm;margin:0;overflow:hidden}</style>`;
4343
- return pages.map((page, index) => ({
4344
- index,
4345
- srcDoc: `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8">${styles}${pageStyles}</head><body>${page.outerHTML}</body></html>`
4346
- }));
4347
- }, [
4348
- pageHeightPixels,
4349
- pageWidthPixels,
4350
- previewHtml,
4351
- previewPaperHeight,
4352
- previewPaperWidth
4353
- ]);
4354
4404
  useEffect(() => {
4355
- const updateViewport = () => {
4356
- setPreviewViewport({
4357
- width: window.innerWidth,
4358
- height: window.innerHeight
4405
+ const controller = new AbortController();
4406
+ setSource(null);
4407
+ setError("");
4408
+ const timer = setTimeout(() => {
4409
+ void Promise.resolve().then(
4410
+ () => props.loadSource({
4411
+ rowLimit: getPrintDesignSampleSize(),
4412
+ includeSummary: false,
4413
+ signal: controller.signal
4414
+ })
4415
+ ).then((value) => {
4416
+ if (!controller.signal.aborted) setSource(value);
4417
+ }).catch((cause) => {
4418
+ if (!controller.signal.aborted)
4419
+ setError(
4420
+ cause instanceof Error ? cause.message : "\u8BFB\u53D6\u6253\u5370\u6570\u636E\u5931\u8D25"
4421
+ );
4359
4422
  });
4423
+ }, 0);
4424
+ return () => {
4425
+ clearTimeout(timer);
4426
+ controller.abort();
4360
4427
  };
4361
- window.addEventListener("resize", updateViewport);
4362
- return () => window.removeEventListener("resize", updateViewport);
4363
- }, []);
4364
- const autoCalculateColumnWidths = () => {
4365
- const selectedColumns = selectPrintColumns(columns, selectedKeys);
4366
- const calculated = calculateBrowserPrintColumnWidths(selectedColumns, {
4367
- pageMargins,
4368
- paperSize,
4369
- orientation
4370
- });
4371
- if (!calculated) {
4372
- setError("\u5F53\u524D\u7EB8\u5F20\u53EF\u7528\u5BBD\u5EA6\u4E0D\u8DB3\u4EE5\u5BB9\u7EB3\u6240\u9009\u5217\uFF08\u6BCF\u5217\u81F3\u5C11 10mm\uFF09");
4373
- return;
4374
- }
4375
- const widths = new Map(calculated.map(({ key, width }) => [key, width]));
4376
- setColumns(
4377
- (current) => current.map(
4378
- (column) => widths.has(column.key) ? { ...column, width: widths.get(column.key) } : column
4379
- )
4380
- );
4381
- setError("");
4382
- };
4383
- const autoCalculateRowsPerPage = () => {
4384
- const calculated = calculateBrowserPrintRowsPerPage({
4385
- pageMargins,
4386
- paperSize,
4387
- orientation,
4388
- rowHeight,
4389
- includeSummary,
4390
- summaryPosition,
4391
- showPageNumbers,
4392
- showPrintTime,
4393
- titleFontSize,
4394
- subtitleFontSize,
4395
- subtitle
4396
- });
4397
- if (!calculated) {
4398
- setError("\u5F53\u524D\u7EB8\u5F20\u53EF\u7528\u9AD8\u5EA6\u4E0D\u8DB3\u4EE5\u5BB9\u7EB3\u4E00\u884C");
4399
- return;
4400
- }
4401
- setRowsPerPage(calculated);
4402
- setError("");
4403
- };
4404
- useEffect(() => {
4405
- if (autoCalculateRows) autoCalculateRowsPerPage();
4406
- }, [
4407
- autoCalculateRows,
4408
- rowHeight,
4409
- includeSummary,
4410
- summaryPosition,
4411
- showPageNumbers,
4412
- showPrintTime,
4413
- titleFontSize,
4414
- subtitleFontSize,
4415
- subtitle,
4416
- paperSize,
4417
- orientation,
4418
- pageMargins
4419
- ]);
4420
- useEffect(() => {
4421
- if (showPageNumbers && showPrintTime && pageNumberPosition === printTimePosition) {
4422
- setPrintTimePosition(pageNumberPosition === "left" ? "center" : "left");
4428
+ }, [props.loadSource, attempt]);
4429
+ return /* @__PURE__ */ jsx(
4430
+ ReactModal,
4431
+ {
4432
+ open: true,
4433
+ fullscreen,
4434
+ closable: false,
4435
+ fullscreenable: false,
4436
+ footer: false,
4437
+ width: "min(96vw, 1440px)",
4438
+ style: { height: fullscreen ? "100%" : "min(88vh, 900px)" },
4439
+ bodyStyle: {
4440
+ padding: 0,
4441
+ overflow: "hidden",
4442
+ display: "flex",
4443
+ position: "relative"
4444
+ },
4445
+ onCancel: props.onClose,
4446
+ children: source ? /* @__PURE__ */ jsx(
4447
+ LoadedPrintDesigner,
4448
+ {
4449
+ ...props,
4450
+ source,
4451
+ fullscreen,
4452
+ onFullscreenChange: setFullscreen
4453
+ }
4454
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
4455
+ /* @__PURE__ */ jsx(
4456
+ ReactPrintDesign,
4457
+ {
4458
+ document: emptyDocument,
4459
+ fullscreen,
4460
+ onClose: props.onClose,
4461
+ style: { flex: 1 }
4462
+ }
4463
+ ),
4464
+ /* @__PURE__ */ jsxs(
4465
+ "div",
4466
+ {
4467
+ role: "status",
4468
+ style: {
4469
+ position: "absolute",
4470
+ inset: 0,
4471
+ background: "rgba(255,255,255,0.85)",
4472
+ display: "flex",
4473
+ alignItems: "center",
4474
+ justifyContent: "center",
4475
+ gap: 12
4476
+ },
4477
+ children: [
4478
+ /* @__PURE__ */ jsx("span", { children: error || "\u6B63\u5728\u8BFB\u53D6\u7B2C\u4E00\u9875\u6570\u636E\u2026" }),
4479
+ error ? /* @__PURE__ */ jsx(ReactButton, { onClick: () => setAttempt((value) => value + 1), children: "\u91CD\u8BD5" }) : null,
4480
+ /* @__PURE__ */ jsx(ReactButton, { onClick: props.onClose, children: "\u5173\u95ED" })
4481
+ ]
4482
+ }
4483
+ )
4484
+ ] })
4423
4485
  }
4424
- }, [showPageNumbers, showPrintTime, pageNumberPosition, printTimePosition]);
4425
- const createCurrentPrintOptions = () => configurePrintTable(
4426
- props.source,
4427
- columns,
4428
- selectedKeys.map(String),
4429
- rowsPerPage,
4486
+ );
4487
+ }
4488
+ function LoadedPrintDesigner(props) {
4489
+ const document2 = useMemo(
4490
+ () => createTableDocument({ ...props.source, rows: [] }),
4491
+ [props.source]
4492
+ );
4493
+ const [boundDocument, setBoundDocument] = useState(document2);
4494
+ const rows = useMemo(() => mapPrintSourceRows(props.source), [props.source]);
4495
+ const defaultTemplates = useMemo(
4496
+ () => [
4497
+ document2,
4498
+ ...props.printPresets?.map(
4499
+ (preset) => createTableDocument({ ...props.source, rows: [] }, preset)
4500
+ ) ?? [],
4501
+ ...props.printTemplates ?? []
4502
+ ],
4503
+ [document2, props.printPresets, props.printTemplates, props.source]
4504
+ );
4505
+ const data = useMemo(
4506
+ () => createTableData(props.source, boundDocument, rows),
4507
+ [props.source, boundDocument, rows]
4508
+ );
4509
+ const loadPreviewData = useCallback(
4510
+ async (template, options) => {
4511
+ const source = await props.loadSource(options);
4512
+ options.signal?.throwIfAborted();
4513
+ return createTablePreviewSource(source, template, options);
4514
+ },
4515
+ [props.loadSource]
4516
+ );
4517
+ return /* @__PURE__ */ jsx(
4518
+ ReactPrintDesign,
4430
4519
  {
4431
- includeSummary,
4432
- rowHeight,
4433
- title,
4434
- subtitle,
4435
- titleFontSize,
4436
- subtitleFontSize,
4437
- summaryPosition,
4438
- showPageNumbers,
4439
- pageNumberPosition,
4440
- showPrintTime,
4441
- printTimePosition,
4442
- pageMargins,
4443
- paperSize,
4444
- orientation,
4445
- useCellBorders,
4446
- borderColor,
4447
- textColor,
4448
- headerBackgroundColor,
4449
- summaryBackgroundColor
4520
+ document: document2,
4521
+ defaultTemplates,
4522
+ data,
4523
+ dataFormatKey: getPrintSourceFormat(props.source),
4524
+ onChange: setBoundDocument,
4525
+ storeKey: props.storeKey,
4526
+ loadPreviewData,
4527
+ fullscreen: props.fullscreen,
4528
+ onFullscreenChange: props.onFullscreenChange,
4529
+ onClose: props.onClose,
4530
+ style: { flex: 1 }
4450
4531
  }
4451
4532
  );
4452
- const applyPrintPreset = (name) => {
4453
- setSelectedPresetName(name);
4454
- setError("");
4455
- if (!name) {
4456
- const defaultColumns = createPrintColumns(
4457
- props.source.columns,
4458
- props.source.summaryColumns
4459
- );
4460
- setColumns(defaultColumns);
4461
- setSelectedKeys(
4462
- resolvePrintSelectedKeys(props.source.columns, void 0, null)
4463
- );
4464
- setRowsPerPage(30);
4465
- setRowHeight(8);
4466
- setAutoCalculateRows(false);
4467
- setTitle("\u8868\u683C\u6253\u5370");
4468
- setSubtitle("");
4469
- setTitleFontSize(18);
4470
- setSubtitleFontSize(12);
4471
- setIncludeSummary(
4472
- Boolean(
4473
- props.source.summaryRow || props.source.summaryColumns?.some(
4474
- (column) => column.summary.type !== "none"
4475
- )
4476
- )
4477
- );
4478
- setSummaryPosition("last-page");
4479
- setShowPageNumbers(false);
4480
- setPageNumberPosition("right");
4481
- setShowPrintTime(false);
4482
- setPrintTimePosition("left");
4483
- setPageMargins({ top: 12, right: 12, bottom: 12, left: 12 });
4484
- setPaperSize("a4");
4485
- setOrientation("portrait");
4486
- setUseCellBorders(true);
4487
- setBorderColor("#000000");
4488
- setTextColor("#000000");
4489
- setHeaderBackgroundColor("#f2f5f8");
4490
- setSummaryBackgroundColor("#f2f5f8");
4491
- setShowSectionDividers(true);
4492
- setSectionDividerTopSpacing(4);
4493
- setSectionDividerBottomSpacing(4);
4494
- setFormItemSpacing(4);
4495
- return;
4496
- }
4497
- const preset = props.printPresets?.find((item) => item.name === name);
4498
- if (!preset) return;
4499
- const config = preset.config;
4500
- const baseColumns = createPrintColumns(
4501
- props.source.columns,
4502
- props.source.summaryColumns
4503
- );
4504
- setColumns(
4505
- restorePrintColumns(
4506
- baseColumns,
4507
- config.columns ? { columns: config.columns } : null
4508
- )
4509
- );
4510
- setSelectedKeys(
4511
- resolvePrintSelectedKeys(props.source.columns, config, null)
4512
- );
4513
- setRowsPerPage(config.rowsPerPage ?? 30);
4514
- setRowHeight(config.rowHeight ?? 8);
4515
- setAutoCalculateRows(config.autoCalculateRowsPerPage ?? false);
4516
- setTitle(config.title ?? "\u8868\u683C\u6253\u5370");
4517
- setSubtitle(config.subtitle ?? "");
4518
- setTitleFontSize(config.titleFontSize ?? 18);
4519
- setSubtitleFontSize(config.subtitleFontSize ?? 12);
4520
- setIncludeSummary(
4521
- config.includeSummary ?? Boolean(
4522
- props.source.summaryRow || props.source.summaryColumns?.some(
4523
- (column) => column.summary.type !== "none"
4533
+ }
4534
+ var PRINT_PROGRESS_MIN_DURATION = 500;
4535
+ function createPrintProgress(container, onCancel) {
4536
+ const host = container.ownerDocument.createElement("div");
4537
+ host.dataset.listTablePrintProgress = "";
4538
+ container.append(host);
4539
+ const root = createRoot(host);
4540
+ const startedAt = performance.now();
4541
+ let disposed = false;
4542
+ let open = true;
4543
+ let message = "\u6B63\u5728\u8BFB\u53D6\u6253\u5370\u6570\u636E";
4544
+ let percent = 0;
4545
+ let failed = false;
4546
+ let closeTimer;
4547
+ let resolveClose;
4548
+ let configuring = false;
4549
+ let batchSize = 50;
4550
+ let action;
4551
+ const confirm = () => {
4552
+ const pending = action;
4553
+ action = void 0;
4554
+ configuring = false;
4555
+ render();
4556
+ pending?.resolve();
4557
+ };
4558
+ const dispose = () => {
4559
+ if (disposed) return;
4560
+ disposed = true;
4561
+ clearTimeout(closeTimer);
4562
+ root.unmount();
4563
+ host.remove();
4564
+ resolveClose?.();
4565
+ action?.resolve();
4566
+ action = void 0;
4567
+ };
4568
+ const render = () => {
4569
+ if (disposed) return;
4570
+ flushSync(
4571
+ () => root.render(
4572
+ /* @__PURE__ */ jsxs(
4573
+ ReactModal,
4574
+ {
4575
+ open,
4576
+ title: configuring ? "\u76F4\u63A5\u6253\u5370\u8BBE\u7F6E" : failed ? "\u6253\u5370\u5931\u8D25" : "\u6B63\u5728\u51C6\u5907\u6253\u5370",
4577
+ width: 440,
4578
+ centered: true,
4579
+ maskClosable: false,
4580
+ fullscreenable: false,
4581
+ getContainer: container,
4582
+ onCancel,
4583
+ afterClose: () => queueMicrotask(dispose),
4584
+ footer: /* @__PURE__ */ jsxs(Fragment, { children: [
4585
+ /* @__PURE__ */ jsx(ReactButton, { onClick: onCancel, children: failed ? "\u5173\u95ED" : "\u53D6\u6D88\u6253\u5370" }),
4586
+ action && /* @__PURE__ */ jsx(
4587
+ ReactButton,
4588
+ {
4589
+ disabled: configuring && (!batchSize || !Number.isInteger(batchSize) || batchSize < 1),
4590
+ onClick: confirm,
4591
+ children: action.label
4592
+ }
4593
+ )
4594
+ ] }),
4595
+ children: [
4596
+ /* @__PURE__ */ jsx("p", { role: "status", "aria-live": "polite", children: message }),
4597
+ configuring ? /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 12 }, children: [
4598
+ /* @__PURE__ */ jsx("span", { style: { whiteSpace: "nowrap", flexShrink: 0 }, children: "\u6BCF\u6B21\u6253\u5370\u9875\u6570" }),
4599
+ /* @__PURE__ */ jsx(
4600
+ ReactInputNumber,
4601
+ {
4602
+ "aria-label": "\u6BCF\u6B21\u6253\u5370\u9875\u6570",
4603
+ style: { flex: 1, minWidth: 0 },
4604
+ min: 1,
4605
+ precision: 0,
4606
+ value: batchSize,
4607
+ onChange: (value) => {
4608
+ batchSize = value;
4609
+ render();
4610
+ }
4611
+ }
4612
+ )
4613
+ ] }) : /* @__PURE__ */ jsx(
4614
+ ReactProgress,
4615
+ {
4616
+ percent,
4617
+ label: "\u6253\u5370\u51C6\u5907\u8FDB\u5EA6",
4618
+ status: failed ? "exception" : "normal"
4619
+ }
4620
+ )
4621
+ ]
4622
+ }
4524
4623
  )
4525
4624
  )
4526
4625
  );
4527
- setSummaryPosition(config.summaryPosition ?? "last-page");
4528
- setShowPageNumbers(config.showPageNumbers ?? false);
4529
- setPageNumberPosition(config.pageNumberPosition ?? "right");
4530
- setShowPrintTime(config.showPrintTime ?? false);
4531
- setPrintTimePosition(config.printTimePosition ?? "left");
4532
- setPageMargins(
4533
- config.pageMargins ?? { top: 12, right: 12, bottom: 12, left: 12 }
4534
- );
4535
- setPaperSize(config.paperSize ?? "a4");
4536
- setOrientation(config.orientation ?? "portrait");
4537
- setUseCellBorders(config.useCellBorders ?? true);
4538
- setBorderColor(config.borderColor ?? "#000000");
4539
- setTextColor(config.textColor ?? "#000000");
4540
- setHeaderBackgroundColor(config.headerBackgroundColor ?? "#f2f5f8");
4541
- setSummaryBackgroundColor(config.summaryBackgroundColor ?? "#f2f5f8");
4542
- setShowSectionDividers(config.showSectionDividers ?? true);
4543
- setSectionDividerTopSpacing(config.sectionDividerTopSpacing ?? 4);
4544
- setSectionDividerBottomSpacing(config.sectionDividerBottomSpacing ?? 4);
4545
- setFormItemSpacing(config.formItemSpacing ?? 4);
4546
4626
  };
4547
- const previewPrintHtml = async () => {
4548
- if (!selectedKeys.length) {
4549
- setError("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u5217");
4550
- return;
4551
- }
4552
- if (!Number.isInteger(rowsPerPage) || rowsPerPage < 1) {
4553
- setError("\u6BCF\u9875\u884C\u6570\u5FC5\u987B\u662F\u6B63\u6574\u6570");
4554
- return;
4555
- }
4556
- if (!Number.isFinite(rowHeight) || rowHeight <= 0) {
4557
- setError("\u884C\u9AD8\u5FC5\u987B\u662F\u6B63\u6570");
4558
- return;
4559
- }
4560
- try {
4561
- const options = await createCurrentPrintOptions();
4562
- setPreviewHtml(createBrowserPrintPreviewHtml(options));
4563
- setPreviewMode("render");
4564
- setPreviewOpen(true);
4565
- setError("");
4566
- } catch (cause) {
4567
- setError(cause instanceof Error ? cause.message : "\u751F\u6210\u6253\u5370\u9884\u89C8\u5931\u8D25");
4568
- }
4569
- };
4570
- const confirm = async () => {
4571
- const selected = selectedKeys.map(String);
4572
- if (!selected.length) return setError("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u5217");
4573
- if (!Number.isInteger(rowsPerPage) || rowsPerPage < 1)
4574
- return setError("\u6BCF\u9875\u884C\u6570\u5FC5\u987B\u662F\u6B63\u6574\u6570");
4575
- if (!Number.isFinite(rowHeight) || rowHeight <= 0)
4576
- return setError("\u884C\u9AD8\u5FC5\u987B\u662F\u6B63\u6570");
4577
- if (Object.values(pageMargins).some(
4578
- (margin) => !Number.isFinite(margin) || margin < 0
4579
- )) {
4580
- return setError("\u6253\u5370\u8FB9\u8DDD\u5FC5\u987B\u662F\u975E\u8D1F\u6570");
4581
- }
4582
- if (columns.some(
4583
- (column) => !Number.isFinite(column.width) || column.width < 10
4584
- )) {
4585
- return setError("\u5217\u5BBD\u4E0D\u80FD\u5C0F\u4E8E 10 \u6BEB\u7C73");
4586
- }
4587
- const options = await createCurrentPrintOptions();
4588
- if (!printBrowserTable(options))
4589
- return setError("\u6D4F\u89C8\u5668\u963B\u6B62\u4E86\u6253\u5370\u7A97\u53E3\uFF0C\u8BF7\u5141\u8BB8\u6B64\u9875\u9762\u6253\u5F00\u5F39\u7A97");
4590
- if (cacheEnabled) {
4591
- writePrintSettings(props.storageKey, {
4592
- version: 2,
4593
- columns,
4594
- selectedKeys: selected,
4595
- rowsPerPage,
4596
- presetName: selectedPresetName || void 0,
4597
- rowHeight,
4598
- autoCalculateRowsPerPage: autoCalculateRows,
4599
- title,
4600
- subtitle,
4601
- titleFontSize,
4602
- subtitleFontSize,
4603
- includeSummary,
4604
- summaryPosition,
4605
- showPageNumbers,
4606
- pageNumberPosition,
4607
- showPrintTime,
4608
- printTimePosition,
4609
- pageMargins,
4610
- paperSize,
4611
- orientation,
4612
- useCellBorders,
4613
- borderColor,
4614
- textColor,
4615
- headerBackgroundColor,
4616
- summaryBackgroundColor,
4617
- showSectionDividers,
4618
- sectionDividerTopSpacing,
4619
- sectionDividerBottomSpacing,
4620
- formItemSpacing
4627
+ render();
4628
+ return {
4629
+ host,
4630
+ async configure(defaultPages) {
4631
+ configuring = true;
4632
+ batchSize = Number.isFinite(defaultPages) ? Math.max(1, Math.floor(defaultPages)) : 50;
4633
+ message = "\u6309\u6279\u6B21\u751F\u6210\u6253\u5370\u9875\u9762\uFF0C\u907F\u514D\u4E00\u6B21\u52A0\u8F7D\u8FC7\u591A\u5185\u5BB9\u3002";
4634
+ await new Promise((resolve) => {
4635
+ action = { label: "\u5F00\u59CB\u6253\u5370", resolve };
4636
+ render();
4621
4637
  });
4622
- }
4623
- props.onClose();
4624
- };
4625
- const tableColumns = [
4626
- ROW_SELECTION_COLUMN,
4627
- { key: "title", dataIndex: "title", title: "\u6253\u5370\u5217", width: 260 },
4628
- {
4629
- key: "width",
4630
- dataIndex: "width",
4631
- title: "\u5217\u5BBD\uFF08mm\uFF09",
4632
- width: 110,
4633
- editable: true,
4634
- formItemProps: { valueType: "number", clickCount: 1, min: 10 }
4638
+ return batchSize ?? 50;
4635
4639
  },
4636
- {
4637
- key: "align",
4638
- dataIndex: "align",
4639
- title: "\u5BF9\u9F50\u65B9\u5F0F",
4640
- width: 100,
4641
- editable: true,
4642
- formItemProps: {
4643
- valueType: "select",
4644
- clickCount: 1,
4645
- options: [
4646
- { value: "left", label: "\u5DE6\u5BF9\u9F50" },
4647
- { value: "center", label: "\u5C45\u4E2D" },
4648
- { value: "right", label: "\u53F3\u5BF9\u9F50" }
4649
- ]
4650
- },
4651
- render: (value) => value === "center" ? "\u5C45\u4E2D" : value === "right" ? "\u53F3\u5BF9\u9F50" : "\u5DE6\u5BF9\u9F50"
4640
+ async continueBatch(firstPage, lastPage) {
4641
+ message = `\u7B2C ${firstPage}\u2013${lastPage} \u9875\u7684\u6253\u5370\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u662F\u5426\u7EE7\u7EED\u4E0B\u4E00\u6279\uFF1F`;
4642
+ await new Promise((resolve) => {
4643
+ action = { label: "\u7EE7\u7EED\u4E0B\u4E00\u6279", resolve };
4644
+ render();
4645
+ });
4652
4646
  },
4653
- {
4654
- key: "overflow",
4655
- dataIndex: "overflow",
4656
- title: "\u8D85\u5BBD\u5904\u7406",
4657
- width: 105,
4658
- editable: true,
4659
- formItemProps: {
4660
- valueType: "select",
4661
- clickCount: 1,
4662
- options: [
4663
- { value: "ellipsis", label: "\u7701\u7565\u53F7" },
4664
- { value: "clip", label: "\u88C1\u5207" },
4665
- { value: "wrap", label: "\u6362\u884C" }
4666
- ]
4667
- },
4668
- render: (value) => value === "ellipsis" ? "\u7701\u7565\u53F7" : value === "clip" ? "\u88C1\u5207" : "\u6362\u884C"
4647
+ update(text, value) {
4648
+ if (percent === value && message === text) return;
4649
+ message = text;
4650
+ percent = value;
4651
+ render();
4669
4652
  },
4670
- ...includeSummary ? [
4671
- {
4672
- key: "summary-group",
4673
- dataIndex: "summaryType",
4674
- title: "\u6C47\u603B",
4675
- children: [
4676
- {
4677
- key: "summaryType",
4678
- dataIndex: "summaryType",
4679
- title: "\u6C47\u603B\u65B9\u5F0F",
4680
- width: 110,
4681
- editable: true,
4682
- formItemProps: {
4683
- valueType: "select",
4684
- clickCount: 1,
4685
- options: summaryOptions2.filter(
4686
- (option) => option.value !== "original" || props.source.summaryColumns?.some(
4687
- (column) => column.summary.type === "original"
4688
- )
4689
- )
4690
- },
4691
- render: (value) => summaryOptions2.find((option) => option.value === value)?.label ?? "\u65E0\u6C47\u603B"
4692
- },
4693
- {
4694
- key: "showTypePrefix",
4695
- dataIndex: "showTypePrefix",
4696
- title: "\u6C47\u603B\u524D\u7F00",
4697
- width: 90,
4698
- editable: true,
4699
- formItemProps: {
4700
- valueType: "select",
4701
- clickCount: 1,
4702
- options: booleanOptions2
4703
- },
4704
- render: (value) => value === "yes" ? "\u662F" : "\u5426"
4705
- },
4706
- {
4707
- key: "precision",
4708
- dataIndex: "precision",
4709
- title: "\u5C0F\u6570\u4F4D\u6570",
4710
- width: 90,
4711
- editable: true,
4712
- formItemProps: {
4713
- valueType: "number",
4714
- clickCount: 1,
4715
- min: 0,
4716
- max: 20
4717
- }
4718
- },
4719
- {
4720
- key: "roundingMode",
4721
- dataIndex: "roundingMode",
4722
- title: "\u53D6\u6574\u65B9\u5F0F",
4723
- width: 100,
4724
- editable: true,
4725
- formItemProps: {
4726
- valueType: "select",
4727
- clickCount: 1,
4728
- options: roundingOptions2
4729
- },
4730
- render: (value) => roundingOptions2.find((option) => option.value === value)?.label ?? "\u56DB\u820D\u4E94\u5165"
4731
- },
4732
- {
4733
- key: "preserveTrailingZeros",
4734
- dataIndex: "preserveTrailingZeros",
4735
- title: "\u4FDD\u7559\u65E0\u6548\u96F6",
4736
- width: 100,
4737
- editable: true,
4738
- formItemProps: {
4739
- valueType: "select",
4740
- clickCount: 1,
4741
- options: booleanOptions2
4742
- },
4743
- render: (value) => value === "yes" ? "\u662F" : "\u5426"
4744
- }
4745
- ]
4746
- }
4747
- ] : []
4748
- ];
4749
- const sectionDivider = showSectionDividers ? /* @__PURE__ */ jsx(
4750
- "div",
4751
- {
4752
- "aria-hidden": "true",
4753
- style: {
4754
- height: 1,
4755
- marginTop: sectionDividerTopSpacing,
4756
- marginBottom: sectionDividerBottomSpacing,
4757
- background: "#e2e8f0"
4653
+ error(text) {
4654
+ message = text;
4655
+ failed = true;
4656
+ render();
4657
+ },
4658
+ close(immediate = false) {
4659
+ if (disposed) return Promise.resolve();
4660
+ if (immediate) {
4661
+ dispose();
4662
+ return Promise.resolve();
4758
4663
  }
4759
- }
4760
- ) : null;
4761
- return /* @__PURE__ */ jsxs(
4762
- ReactModal,
4763
- {
4764
- open: true,
4765
- title: /* @__PURE__ */ jsxs(
4766
- "span",
4767
- {
4768
- style: {
4769
- display: "flex",
4770
- alignItems: "baseline",
4771
- gap: 12,
4772
- minWidth: 0
4773
- },
4774
- children: [
4775
- /* @__PURE__ */ jsx("span", { children: "\u6253\u5370\u8868\u683C\u914D\u7F6E" }),
4776
- printLayoutWarning ? /* @__PURE__ */ jsxs(
4777
- "span",
4778
- {
4779
- role: "status",
4780
- title: `${printLayoutWarning}\uFF0C\u4ECD\u53EF\u7EE7\u7EED\u6253\u5370\uFF1B\u6253\u5370\u7ED3\u679C\u53EF\u80FD\u88AB\u88C1\u5207\u6216\u5206\u9875\u5F02\u5E38\u3002`,
4781
- style: {
4782
- color: "#b45309",
4783
- fontSize: 12,
4784
- fontWeight: 400,
4785
- whiteSpace: "nowrap",
4786
- overflow: "hidden",
4787
- textOverflow: "ellipsis",
4788
- minWidth: 0
4789
- },
4790
- children: [
4791
- printLayoutWarning,
4792
- "\uFF0C\u4ECD\u53EF\u7EE7\u7EED\u6253\u5370"
4793
- ]
4794
- }
4795
- ) : null
4796
- ]
4797
- }
4798
- ),
4799
- width: "min(98vw, 1680px)",
4800
- fullscreenable: true,
4801
- fullscreen: printFullscreen,
4802
- onFullscreenChange: setPrintFullscreen,
4803
- bodyStyle: { display: "flex", minHeight: 0 },
4804
- okText: "\u6253\u5370",
4805
- cancelText: "\u53D6\u6D88",
4806
- onCancel: props.onClose,
4807
- footer: /* @__PURE__ */ jsxs(
4808
- "div",
4809
- {
4810
- style: {
4811
- display: "flex",
4812
- justifyContent: "flex-end",
4813
- alignItems: "center",
4814
- gap: 8,
4815
- width: "100%"
4664
+ return new Promise((resolve) => {
4665
+ resolveClose = resolve;
4666
+ closeTimer = setTimeout(
4667
+ () => {
4668
+ open = false;
4669
+ render();
4816
4670
  },
4817
- children: [
4818
- /* @__PURE__ */ jsx(ReactButton, { type: "default", onClick: props.onClose, children: "\u53D6\u6D88" }),
4819
- /* @__PURE__ */ jsx(ReactButton, { type: "default", onClick: previewPrintHtml, children: "\u6253\u5370\u9884\u89C8" }),
4820
- /* @__PURE__ */ jsx(ReactButton, { type: "primary", onClick: confirm, children: "\u6253\u5370" })
4821
- ]
4671
+ Math.max(
4672
+ 0,
4673
+ PRINT_PROGRESS_MIN_DURATION - (performance.now() - startedAt)
4674
+ )
4675
+ );
4676
+ });
4677
+ }
4678
+ };
4679
+ }
4680
+ var PRINT_ROWS_WORKER_SOURCE = `
4681
+ self.onmessage = function(event) {
4682
+ try {
4683
+ const { keys, rows } = event.data;
4684
+ self.postMessage({ rows: rows.map(row => Object.fromEntries(keys.map((key, index) => [key, row[index] ?? ""]))) });
4685
+ } catch (error) {
4686
+ self.postMessage({ error: String(error) });
4687
+ }
4688
+ };`;
4689
+ async function preparePrintRows(source, options = {}) {
4690
+ const keys = source.columns.map((column) => column.key);
4691
+ let worker;
4692
+ let url;
4693
+ try {
4694
+ options.signal?.throwIfAborted();
4695
+ url = URL.createObjectURL(
4696
+ new Blob([PRINT_ROWS_WORKER_SOURCE], { type: "text/javascript" })
4697
+ );
4698
+ worker = new Worker(url);
4699
+ const result = new Array(source.rows.length);
4700
+ for (let offset = 0; offset < source.rows.length; offset += 2e3) {
4701
+ options.signal?.throwIfAborted();
4702
+ const rows = await new Promise(
4703
+ (resolve, reject) => {
4704
+ const cleanup = () => {
4705
+ clearTimeout(timeout);
4706
+ options.signal?.removeEventListener("abort", abort);
4707
+ };
4708
+ const fail = (error) => {
4709
+ cleanup();
4710
+ reject(error);
4711
+ };
4712
+ const abort = () => fail(options.signal?.reason);
4713
+ const timeout = setTimeout(
4714
+ () => fail(new Error("\u6253\u5370 Worker \u54CD\u5E94\u8D85\u65F6")),
4715
+ 15e3
4716
+ );
4717
+ options.signal?.addEventListener("abort", abort, { once: true });
4718
+ worker.onerror = () => fail(new Error("\u6253\u5370 Worker \u6267\u884C\u5931\u8D25"));
4719
+ worker.onmessageerror = () => fail(new Error("\u6253\u5370 Worker \u6570\u636E\u4F20\u8F93\u5931\u8D25"));
4720
+ worker.onmessage = (event) => {
4721
+ cleanup();
4722
+ if (event.data.error) reject(new Error(event.data.error));
4723
+ else resolve(event.data.rows);
4724
+ };
4725
+ try {
4726
+ worker.postMessage({
4727
+ keys,
4728
+ rows: source.rows.slice(offset, offset + 2e3)
4729
+ });
4730
+ } catch (error) {
4731
+ fail(error);
4732
+ }
4822
4733
  }
4734
+ );
4735
+ for (let index = 0; index < rows.length; index++)
4736
+ result[offset + index] = rows[index];
4737
+ options.onProgress?.(
4738
+ Math.min(offset + rows.length, source.rows.length),
4739
+ source.rows.length
4740
+ );
4741
+ }
4742
+ return result;
4743
+ } catch {
4744
+ worker?.terminate();
4745
+ worker = void 0;
4746
+ options.signal?.throwIfAborted();
4747
+ return await mapInBatches(
4748
+ source.rows.length,
4749
+ (index) => Object.fromEntries(
4750
+ keys.map((key, column) => [key, source.rows[index][column] ?? ""])
4823
4751
  ),
4824
- children: [
4825
- /* @__PURE__ */ jsx(
4826
- "div",
4827
- {
4828
- style: {
4829
- display: "flex",
4830
- flex: "1 1 auto",
4831
- minHeight: 0,
4832
- flexDirection: "column"
4833
- },
4834
- children: /* @__PURE__ */ jsxs(
4835
- "div",
4836
- {
4837
- style: {
4838
- display: "grid",
4839
- flex: "1 1 auto",
4840
- minHeight: 0,
4841
- gridTemplateColumns: "minmax(340px, 380px) minmax(0, 1fr)",
4842
- gap: 20,
4843
- alignItems: "stretch"
4844
- },
4845
- children: [
4846
- /* @__PURE__ */ jsx(
4847
- "div",
4848
- {
4849
- style: {
4850
- display: "grid",
4851
- gap: 0,
4852
- padding: "4px 4px 0 0",
4853
- minHeight: 0,
4854
- overflowY: "auto"
4855
- },
4856
- children: /* @__PURE__ */ jsxs(
4857
- ReactForm,
4858
- {
4859
- layout: "horizontal",
4860
- noWrap: true,
4861
- itemSpacing: `${formItemSpacing}px`,
4862
- labelWidth: 88,
4863
- labelAlign: "left",
4864
- style: { display: "contents" },
4865
- children: [
4866
- props.printPresets?.length ? /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6253\u5370\u914D\u7F6E", children: /* @__PURE__ */ jsx(
4867
- ReactSelect,
4868
- {
4869
- value: selectedPresetName,
4870
- "aria-label": "\u6253\u5370\u914D\u7F6E\u9884\u8BBE",
4871
- options: [
4872
- { value: "", label: "\u9ED8\u8BA4\u914D\u7F6E" },
4873
- ...props.printPresets.map((preset) => ({
4874
- value: preset.name,
4875
- label: preset.name
4876
- }))
4877
- ],
4878
- onChange: (value) => applyPrintPreset(String(value ?? "")),
4879
- style: { width: "100%" }
4880
- }
4881
- ) }) : null,
4882
- props.storageKey ? /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u7F13\u5B58\u5230\u672C\u5730", children: /* @__PURE__ */ jsx(
4883
- ReactCheckbox,
4884
- {
4885
- checked: cacheEnabled,
4886
- "aria-label": "\u7F13\u5B58\u6253\u5370\u914D\u7F6E\u5230\u672C\u5730",
4887
- onChange: (event) => {
4888
- setCacheEnabled(event.target.checked);
4889
- if (!event.target.checked)
4890
- writePrintSettings(props.storageKey, null);
4891
- }
4892
- }
4893
- ) }) : null,
4894
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u5206\u9694\u7EBF\u5360\u4F4D", children: /* @__PURE__ */ jsx(
4895
- ReactCheckbox,
4896
- {
4897
- checked: showSectionDividers,
4898
- "aria-label": "\u663E\u793A\u5206\u7EC4\u5206\u9694\u7EBF",
4899
- onChange: (event) => setShowSectionDividers(event.target.checked)
4900
- }
4901
- ) }),
4902
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u884C\u95F4\u8DDD\uFF08px\uFF09", children: /* @__PURE__ */ jsx(
4903
- ReactNumberInput,
4904
- {
4905
- min: 0,
4906
- max: 40,
4907
- step: 1,
4908
- value: formItemSpacing,
4909
- "aria-label": "\u8868\u5355\u884C\u95F4\u8DDD",
4910
- onChange: (value) => setFormItemSpacing(value ?? 0),
4911
- style: { width: "100%" }
4912
- }
4913
- ) }),
4914
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u5206\u9694\u7EBF\u4E0A\u4E0B\u95F4\u8DDD\uFF08px\uFF09", children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
4915
- /* @__PURE__ */ jsx("span", { children: "\u4E0A" }),
4916
- /* @__PURE__ */ jsx(
4917
- ReactNumberInput,
4918
- {
4919
- min: 0,
4920
- max: 40,
4921
- step: 1,
4922
- value: sectionDividerTopSpacing,
4923
- "aria-label": "\u5206\u9694\u7EBF\u4E0A\u95F4\u8DDD",
4924
- onChange: (value) => setSectionDividerTopSpacing(value ?? 0),
4925
- style: { width: 64 }
4926
- }
4927
- ),
4928
- /* @__PURE__ */ jsx("span", { children: "\u4E0B" }),
4929
- /* @__PURE__ */ jsx(
4930
- ReactNumberInput,
4931
- {
4932
- min: 0,
4933
- max: 40,
4934
- step: 1,
4935
- value: sectionDividerBottomSpacing,
4936
- "aria-label": "\u5206\u9694\u7EBF\u4E0B\u95F4\u8DDD",
4937
- onChange: (value) => setSectionDividerBottomSpacing(value ?? 0),
4938
- style: { width: 64 }
4939
- }
4940
- )
4941
- ] }) }),
4942
- sectionDivider,
4943
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6807\u9898", children: /* @__PURE__ */ jsx(
4944
- ReactInput,
4945
- {
4946
- value: title,
4947
- "aria-label": "\u6253\u5370\u6807\u9898",
4948
- allowClear: true,
4949
- onChange: (event) => setTitle(event.target.value),
4950
- style: { width: "100%", minWidth: 0 }
4951
- }
4952
- ) }),
4953
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u5B50\u6807\u9898", children: /* @__PURE__ */ jsx(
4954
- ReactInput,
4955
- {
4956
- value: subtitle,
4957
- "aria-label": "\u6253\u5370\u5B50\u6807\u9898",
4958
- allowClear: true,
4959
- onChange: (event) => setSubtitle(event.target.value),
4960
- style: { width: "100%", minWidth: 0 }
4961
- }
4962
- ) }),
4963
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6807\u9898\u5B57\u53F7", children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, minWidth: 0 }, children: [
4964
- /* @__PURE__ */ jsx(
4965
- ReactNumberInput,
4966
- {
4967
- min: 1,
4968
- step: 1,
4969
- value: titleFontSize,
4970
- "aria-label": "\u6807\u9898\u5B57\u53F7",
4971
- onChange: (value) => setTitleFontSize(value ?? 18),
4972
- style: { flex: "1 1 0", minWidth: 0 }
4973
- }
4974
- ),
4975
- /* @__PURE__ */ jsx(
4976
- ReactNumberInput,
4977
- {
4978
- min: 1,
4979
- step: 1,
4980
- value: subtitleFontSize,
4981
- "aria-label": "\u5B50\u6807\u9898\u5B57\u53F7",
4982
- onChange: (value) => setSubtitleFontSize(value ?? 12),
4983
- style: { flex: "1 1 0", minWidth: 0 }
4984
- }
4985
- )
4986
- ] }) }),
4987
- sectionDivider,
4988
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u7EB8\u5F20", children: /* @__PURE__ */ jsx(
4989
- ReactSelect,
4990
- {
4991
- value: paperSize,
4992
- options: [
4993
- { value: "a0", label: "A0" },
4994
- { value: "a1", label: "A1" },
4995
- { value: "a2", label: "A2" },
4996
- { value: "a3", label: "A3" },
4997
- { value: "a4", label: "A4" },
4998
- { value: "a5", label: "A5" },
4999
- { value: "a6", label: "A6" },
5000
- { value: "b4", label: "B4" },
5001
- { value: "b5", label: "B5" },
5002
- { value: "letter", label: "Letter" },
5003
- { value: "legal", label: "Legal" },
5004
- { value: "tabloid", label: "Tabloid" },
5005
- { value: "executive", label: "Executive" },
5006
- { value: "folio", label: "Folio" }
5007
- ],
5008
- onChange: (value) => setPaperSize(value),
5009
- style: { width: "100%" }
5010
- }
5011
- ) }),
5012
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6253\u5370\u65B9\u5411", children: /* @__PURE__ */ jsxs(
5013
- ReactRadio.Group,
5014
- {
5015
- value: orientation,
5016
- "aria-label": "\u6253\u5370\u65B9\u5411",
5017
- onChange: (event) => setOrientation(event.target.value),
5018
- style: { display: "flex", gap: 10, flexWrap: "nowrap" },
5019
- children: [
5020
- /* @__PURE__ */ jsx(ReactRadio, { value: "portrait", children: "\u7EB5\u5411" }),
5021
- /* @__PURE__ */ jsx(ReactRadio, { value: "landscape", children: "\u6A2A\u5411" })
5022
- ]
5023
- }
5024
- ) }),
5025
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6253\u5370\u8FB9\u8DDD\uFF08mm\uFF09", children: /* @__PURE__ */ jsx(
5026
- "div",
5027
- {
5028
- style: {
5029
- display: "grid",
5030
- gridTemplateColumns: "repeat(4, minmax(0, 1fr))",
5031
- gap: 8
5032
- },
5033
- children: [
5034
- ["top", "\u4E0A"],
5035
- ["right", "\u53F3"],
5036
- ["bottom", "\u4E0B"],
5037
- ["left", "\u5DE6"]
5038
- ].map(([side, label]) => /* @__PURE__ */ jsxs(
5039
- "div",
5040
- {
5041
- style: {
5042
- display: "grid",
5043
- gridTemplateColumns: "20px minmax(0, 1fr)",
5044
- gap: 4,
5045
- alignItems: "center"
5046
- },
5047
- children: [
5048
- /* @__PURE__ */ jsx("span", { children: label }),
5049
- /* @__PURE__ */ jsx(
5050
- ReactNumberInput,
5051
- {
5052
- min: 0,
5053
- step: 1,
5054
- value: pageMargins[side],
5055
- onChange: (value) => setPageMargins((current) => ({
5056
- ...current,
5057
- [side]: value ?? 0
5058
- })),
5059
- style: { width: "100%" }
5060
- }
5061
- )
5062
- ]
5063
- },
5064
- side
5065
- ))
5066
- }
5067
- ) }),
5068
- sectionDivider,
5069
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u5355\u5143\u683C\u8FB9\u6846", children: /* @__PURE__ */ jsx(
5070
- ReactCheckbox,
5071
- {
5072
- checked: useCellBorders,
5073
- "aria-label": "\u5355\u5143\u683C\u8FB9\u6846",
5074
- onChange: (event) => setUseCellBorders(event.target.checked)
5075
- }
5076
- ) }),
5077
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u8FB9\u6846\u989C\u8272", children: /* @__PURE__ */ jsx(
5078
- ReactColorPicker,
5079
- {
5080
- value: borderColor,
5081
- disabled: !useCellBorders,
5082
- onChange: setBorderColor,
5083
- style: { width: "100%" }
5084
- }
5085
- ) }),
5086
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6587\u5B57\u989C\u8272", children: /* @__PURE__ */ jsx(
5087
- ReactColorPicker,
5088
- {
5089
- value: textColor,
5090
- onChange: setTextColor,
5091
- style: { width: "100%" }
5092
- }
5093
- ) }),
5094
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u8868\u5934\u80CC\u666F\u8272", children: /* @__PURE__ */ jsx(
5095
- ReactColorPicker,
5096
- {
5097
- value: headerBackgroundColor,
5098
- onChange: setHeaderBackgroundColor,
5099
- style: { width: "100%" }
5100
- }
5101
- ) }),
5102
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6C47\u603B\u884C\u80CC\u666F\u8272", children: /* @__PURE__ */ jsx(
5103
- ReactColorPicker,
5104
- {
5105
- value: summaryBackgroundColor,
5106
- onChange: setSummaryBackgroundColor,
5107
- style: { width: "100%" }
5108
- }
5109
- ) }),
5110
- sectionDivider,
5111
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6253\u5370\u6C47\u603B\u884C", children: /* @__PURE__ */ jsx(
5112
- ReactCheckbox,
5113
- {
5114
- checked: includeSummary,
5115
- "aria-label": "\u6253\u5370\u6C47\u603B\u884C",
5116
- onChange: (event) => setIncludeSummary(event.target.checked)
5117
- }
5118
- ) }),
5119
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6C47\u603B\u4F4D\u7F6E", children: /* @__PURE__ */ jsxs(
5120
- ReactRadio.Group,
5121
- {
5122
- value: summaryPosition,
5123
- "aria-label": "\u6C47\u603B\u4F4D\u7F6E",
5124
- onChange: (event) => setSummaryPosition(
5125
- event.target.value
5126
- ),
5127
- style: { display: "flex", gap: 10, flexWrap: "nowrap" },
5128
- children: [
5129
- /* @__PURE__ */ jsx(ReactRadio, { value: "every-page", disabled: !includeSummary, children: "\u6BCF\u9875" }),
5130
- /* @__PURE__ */ jsx(ReactRadio, { value: "last-page", disabled: !includeSummary, children: "\u6700\u540E\u4E00\u9875" })
5131
- ]
5132
- }
5133
- ) }),
5134
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6253\u5370\u65F6\u95F4", children: /* @__PURE__ */ jsxs(
5135
- "span",
5136
- {
5137
- style: {
5138
- display: "flex",
5139
- alignItems: "center",
5140
- gap: 8,
5141
- minWidth: 0,
5142
- whiteSpace: "nowrap"
5143
- },
5144
- children: [
5145
- /* @__PURE__ */ jsx(
5146
- ReactCheckbox,
5147
- {
5148
- checked: showPrintTime,
5149
- "aria-label": "\u663E\u793A\u6253\u5370\u65F6\u95F4",
5150
- onChange: (event) => {
5151
- const checked = event.target.checked;
5152
- setShowPrintTime(checked);
5153
- if (checked && showPageNumbers && pageNumberPosition === printTimePosition) {
5154
- setPrintTimePosition(
5155
- pageNumberPosition === "left" ? "center" : "left"
5156
- );
5157
- }
5158
- }
5159
- }
5160
- ),
5161
- "\u663E\u793A"
5162
- ]
5163
- }
5164
- ) }),
5165
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u65F6\u95F4\u4F4D\u7F6E", children: /* @__PURE__ */ jsxs(
5166
- ReactRadio.Group,
5167
- {
5168
- value: printTimePosition,
5169
- "aria-label": "\u6253\u5370\u65F6\u95F4\u4F4D\u7F6E",
5170
- onChange: (event) => {
5171
- const next = event.target.value;
5172
- if (showPageNumbers && next === pageNumberPosition) return;
5173
- setPrintTimePosition(next);
5174
- },
5175
- style: { display: "flex", gap: 10, flexWrap: "nowrap" },
5176
- children: [
5177
- /* @__PURE__ */ jsx(
5178
- ReactRadio,
5179
- {
5180
- value: "left",
5181
- disabled: !showPrintTime || showPageNumbers && pageNumberPosition === "left",
5182
- children: "\u5DE6\u4FA7"
5183
- }
5184
- ),
5185
- /* @__PURE__ */ jsx(
5186
- ReactRadio,
5187
- {
5188
- value: "center",
5189
- disabled: !showPrintTime || showPageNumbers && pageNumberPosition === "center",
5190
- children: "\u4E2D\u95F4"
5191
- }
5192
- ),
5193
- /* @__PURE__ */ jsx(
5194
- ReactRadio,
5195
- {
5196
- value: "right",
5197
- disabled: !showPrintTime || showPageNumbers && pageNumberPosition === "right",
5198
- children: "\u53F3\u4FA7"
5199
- }
5200
- )
5201
- ]
5202
- }
5203
- ) }),
5204
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u5206\u9875\u4FE1\u606F", children: /* @__PURE__ */ jsxs("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [
5205
- /* @__PURE__ */ jsx(
5206
- ReactCheckbox,
5207
- {
5208
- checked: showPageNumbers,
5209
- "aria-label": "\u663E\u793A\u5206\u9875\u4FE1\u606F",
5210
- onChange: (event) => setShowPageNumbers(event.target.checked)
5211
- }
5212
- ),
5213
- "\u663E\u793A"
5214
- ] }) }),
5215
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u5206\u9875\u4F4D\u7F6E", children: /* @__PURE__ */ jsxs(
5216
- ReactRadio.Group,
5217
- {
5218
- value: pageNumberPosition,
5219
- "aria-label": "\u5206\u9875\u4FE1\u606F\u4F4D\u7F6E",
5220
- onChange: (event) => (() => {
5221
- const next = event.target.value;
5222
- setPageNumberPosition(next);
5223
- if (showPrintTime && next === printTimePosition) {
5224
- setPrintTimePosition(
5225
- next === "left" ? "center" : "left"
5226
- );
5227
- }
5228
- })(),
5229
- style: { display: "flex", gap: 10, flexWrap: "nowrap" },
5230
- children: [
5231
- /* @__PURE__ */ jsx(
5232
- ReactRadio,
5233
- {
5234
- value: "left",
5235
- disabled: !showPageNumbers || showPrintTime && printTimePosition === "left",
5236
- children: "\u5DE6\u4FA7"
5237
- }
5238
- ),
5239
- /* @__PURE__ */ jsx(
5240
- ReactRadio,
5241
- {
5242
- value: "center",
5243
- disabled: !showPageNumbers || showPrintTime && printTimePosition === "center",
5244
- children: "\u4E2D\u95F4"
5245
- }
5246
- ),
5247
- /* @__PURE__ */ jsx(
5248
- ReactRadio,
5249
- {
5250
- value: "right",
5251
- disabled: !showPageNumbers || showPrintTime && printTimePosition === "right",
5252
- children: "\u53F3\u4FA7"
5253
- }
5254
- )
5255
- ]
5256
- }
5257
- ) }),
5258
- sectionDivider,
5259
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u884C\u9AD8\uFF08mm\uFF09", children: /* @__PURE__ */ jsx(
5260
- ReactNumberInput,
5261
- {
5262
- min: 1,
5263
- step: 1,
5264
- value: rowHeight,
5265
- onChange: (value) => {
5266
- setRowHeight(value ?? 0);
5267
- setError("");
5268
- },
5269
- style: { width: "100%" }
5270
- }
5271
- ) }),
5272
- /* @__PURE__ */ jsx(ReactForm.Item, { label: "\u6BCF\u9875\u884C\u6570", children: /* @__PURE__ */ jsxs(
5273
- "span",
5274
- {
5275
- style: {
5276
- display: "flex",
5277
- alignItems: "center",
5278
- gap: 8,
5279
- whiteSpace: "nowrap"
5280
- },
5281
- children: [
5282
- /* @__PURE__ */ jsx(
5283
- ReactNumberInput,
5284
- {
5285
- min: 1,
5286
- step: 1,
5287
- value: rowsPerPage,
5288
- onChange: (value) => {
5289
- setRowsPerPage(value ?? 0);
5290
- setError("");
5291
- },
5292
- style: { flex: "1 1 auto", minWidth: 0 }
5293
- }
5294
- ),
5295
- /* @__PURE__ */ jsx(
5296
- ReactCheckbox,
5297
- {
5298
- checked: autoCalculateRows,
5299
- "aria-label": "\u81EA\u52A8\u8BA1\u7B97\u6BCF\u9875\u884C\u6570",
5300
- onChange: (event) => setAutoCalculateRows(event.target.checked)
5301
- }
5302
- ),
5303
- "\u81EA\u52A8\u8BA1\u7B97"
5304
- ]
5305
- }
5306
- ) })
5307
- ]
5308
- }
5309
- )
5310
- }
5311
- ),
5312
- /* @__PURE__ */ jsxs(
5313
- "div",
5314
- {
5315
- style: {
5316
- minWidth: 0,
5317
- minHeight: 0,
5318
- display: "flex",
5319
- flexDirection: "column"
5320
- },
5321
- children: [
5322
- /* @__PURE__ */ jsx(
5323
- "div",
5324
- {
5325
- style: {
5326
- display: "flex",
5327
- justifyContent: "flex-end",
5328
- marginBottom: 8
5329
- },
5330
- children: /* @__PURE__ */ jsx(
5331
- ReactButton,
5332
- {
5333
- type: "default",
5334
- onClick: autoCalculateColumnWidths,
5335
- disabled: selectedKeys.length === 0,
5336
- children: "\u81EA\u52A8\u8BA1\u7B97\u5217\u5BBD"
5337
- }
5338
- )
5339
- }
5340
- ),
5341
- /* @__PURE__ */ jsx(
5342
- "div",
5343
- {
5344
- style: {
5345
- flex: "1 1 auto",
5346
- minHeight: 280,
5347
- height: printFullscreen ? "100%" : "min(58vh, 560px)"
5348
- },
5349
- children: props.renderTable({
5350
- tableId,
5351
- columns: tableColumns,
5352
- dataSource: columns,
5353
- rowKey: "key",
5354
- rowHeight: 40,
5355
- headerRowHeight: 38,
5356
- toolbar: false,
5357
- contextMenu: false,
5358
- pagination: false,
5359
- draggable: true,
5360
- rowSelection: {
5361
- type: "checkbox",
5362
- selectedRowKeys: selectedKeys,
5363
- selectRowByClick: false,
5364
- onChange: (keys) => {
5365
- setSelectedKeys(keys);
5366
- setError("");
5367
- }
5368
- },
5369
- rowDrag: {
5370
- enabled: true,
5371
- droppable: true,
5372
- allowSelfDrop: true,
5373
- mode: "move",
5374
- onDrop: ({ rows: moved, insertIndex, source }) => {
5375
- if (source.tableId !== tableId || insertIndex == null)
5376
- return;
5377
- const keys = new Set(moved.map((row) => row.key));
5378
- setColumns((current) => {
5379
- const remaining = current.filter(
5380
- (row) => !keys.has(row.key)
5381
- );
5382
- const preceding = current.slice(0, insertIndex).filter((row) => !keys.has(row.key)).length;
5383
- remaining.splice(
5384
- preceding,
5385
- 0,
5386
- ...current.filter((row) => keys.has(row.key))
5387
- );
5388
- return remaining;
5389
- });
5390
- }
5391
- },
5392
- style: { width: "100%", height: "100%" }
5393
- })
5394
- }
5395
- )
5396
- ]
5397
- }
5398
- )
5399
- ]
5400
- }
5401
- )
5402
- }
5403
- ),
5404
- error ? /* @__PURE__ */ jsx("div", { role: "alert", style: { color: "#c2410c", marginTop: 8 }, children: error }) : null,
5405
- /* @__PURE__ */ jsx(
5406
- ReactModal,
5407
- {
5408
- open: previewOpen,
5409
- title: "\u6253\u5370\u9884\u89C8",
5410
- width: "min(96vw, 1280px)",
5411
- fullscreenable: true,
5412
- fullscreen: previewFullscreen,
5413
- onFullscreenChange: setPreviewFullscreen,
5414
- bodyStyle: { display: "flex", minHeight: 0 },
5415
- footer: /* @__PURE__ */ jsxs(
5416
- "div",
5417
- {
5418
- style: {
5419
- display: "flex",
5420
- justifyContent: "flex-end",
5421
- alignItems: "center",
5422
- gap: 8,
5423
- width: "100%"
5424
- },
5425
- children: [
5426
- /* @__PURE__ */ jsx(
5427
- ReactButton,
5428
- {
5429
- type: previewMode === "render" ? "primary" : "default",
5430
- onClick: () => setPreviewMode("render"),
5431
- children: "\u6548\u679C"
5432
- }
5433
- ),
5434
- /* @__PURE__ */ jsx(
5435
- ReactButton,
5436
- {
5437
- type: previewMode === "source" ? "primary" : "default",
5438
- onClick: () => setPreviewMode("source"),
5439
- children: "HTML"
5440
- }
5441
- ),
5442
- /* @__PURE__ */ jsx(ReactButton, { type: "default", onClick: previewPrintHtml, children: "\u5237\u65B0\u9884\u89C8" })
5443
- ]
5444
- }
5445
- ),
5446
- onCancel: () => setPreviewOpen(false),
5447
- children: /* @__PURE__ */ jsx(
5448
- "div",
5449
- {
5450
- style: {
5451
- display: "flex",
5452
- flex: 1,
5453
- minHeight: 0,
5454
- width: "100%",
5455
- justifyContent: "center"
5456
- },
5457
- children: previewMode === "render" ? /* @__PURE__ */ jsx(
5458
- ReactVirtualList,
5459
- {
5460
- items: previewPages,
5461
- height: previewListHeight,
5462
- itemHeight: previewItemHeight,
5463
- itemKey: (page) => page.index,
5464
- overscan: 1,
5465
- style: {
5466
- width: "100%",
5467
- maxWidth: previewListWidth,
5468
- overflowX: "hidden"
5469
- },
5470
- renderItem: (page, _index, meta) => /* @__PURE__ */ jsx(
5471
- "div",
5472
- {
5473
- style: {
5474
- ...meta.style,
5475
- display: "flex",
5476
- justifyContent: "center",
5477
- alignItems: "flex-start",
5478
- height: previewItemHeight,
5479
- padding: 12,
5480
- boxSizing: "border-box",
5481
- overflow: "hidden"
5482
- },
5483
- children: /* @__PURE__ */ jsx(
5484
- "div",
5485
- {
5486
- style: {
5487
- width: pageWidthPixels * previewScale,
5488
- height: pageHeightPixels * previewScale,
5489
- border: "1px solid #cbd5e1",
5490
- position: "relative",
5491
- overflow: "hidden",
5492
- background: "white"
5493
- },
5494
- children: /* @__PURE__ */ jsx(
5495
- "iframe",
5496
- {
5497
- title: `\u6253\u5370\u6548\u679C\u9884\u89C8\u7B2C ${page.index + 1} \u9875`,
5498
- srcDoc: page.srcDoc,
5499
- sandbox: "",
5500
- style: {
5501
- display: "block",
5502
- position: "absolute",
5503
- left: 0,
5504
- top: 0,
5505
- width: pageWidthPixels,
5506
- height: pageHeightPixels,
5507
- transform: `scale(${previewScale})`,
5508
- transformOrigin: "top left",
5509
- border: 0,
5510
- overflow: "hidden",
5511
- background: "white"
5512
- }
5513
- }
5514
- )
5515
- }
5516
- )
5517
- }
5518
- )
5519
- }
5520
- ) : /* @__PURE__ */ jsx(
5521
- "pre",
5522
- {
5523
- "aria-label": "\u751F\u6210\u7684\u6253\u5370 HTML",
5524
- style: {
5525
- boxSizing: "border-box",
5526
- width: "100%",
5527
- height: previewFullscreen ? "100%" : "min(72vh, 900px)",
5528
- minHeight: 0,
5529
- overflow: "auto",
5530
- margin: 0,
5531
- padding: 12,
5532
- border: "1px solid #cbd5e1",
5533
- background: "#f8fafc",
5534
- color: "#1f2937",
5535
- font: "12px/1.5 ui-monospace, SFMono-Regular, monospace",
5536
- whiteSpace: "pre-wrap",
5537
- overflowWrap: "anywhere"
5538
- },
5539
- children: previewHtml
5540
- }
5541
- )
5542
- }
5543
- )
5544
- }
4752
+ options
4753
+ );
4754
+ } finally {
4755
+ worker?.terminate();
4756
+ if (url) URL.revokeObjectURL(url);
4757
+ }
4758
+ }
4759
+
4760
+ // src/features/print/direct-print.ts
4761
+ async function runDirectPrint(container, loadSource, resolveTemplate = (source) => createTableDocument({ ...source, rows: [] }), validate, defaultPagesPerBatch = 50) {
4762
+ const controller = new AbortController();
4763
+ const { signal } = controller;
4764
+ const ownerDocument = container.ownerDocument;
4765
+ const previousFocus = ownerDocument.activeElement;
4766
+ let frame;
4767
+ const progress = createPrintProgress(container, () => {
4768
+ controller.abort();
4769
+ void cleanup(true);
4770
+ });
4771
+ const cleanup = async (immediate = false) => {
4772
+ frame?.remove();
4773
+ await progress.close(immediate);
4774
+ if (previousFocus?.isConnected)
4775
+ previousFocus.focus({ preventScroll: true });
4776
+ };
4777
+ const timer = setInterval(() => {
4778
+ if (!progress.host.isConnected) controller.abort();
4779
+ }, 250);
4780
+ let maximum = 0;
4781
+ const report = (text, value) => {
4782
+ signal.throwIfAborted();
4783
+ maximum = Math.max(maximum, Math.floor(value));
4784
+ progress.update(text, maximum);
4785
+ };
4786
+ try {
4787
+ const pagesPerBatch = await progress.configure(defaultPagesPerBatch);
4788
+ signal.throwIfAborted();
4789
+ report("\u6B63\u5728\u8BFB\u53D6\u6253\u5370\u6570\u636E", 0);
4790
+ await new Promise((resolve) => setTimeout(resolve, 0));
4791
+ const source = await loadSource({
4792
+ signal,
4793
+ onProgress: (done, total) => report(
4794
+ `\u6B63\u5728\u8BFB\u53D6\u6253\u5370\u6570\u636E ${done}/${total}`,
4795
+ total ? done / total * 30 : 30
4796
+ )
4797
+ });
4798
+ report("\u6B63\u5728\u8F6C\u6362\u6253\u5370\u6570\u636E", 30);
4799
+ const rows = await preparePrintRows(source, {
4800
+ signal,
4801
+ onProgress: (done, total) => report(
4802
+ `\u6B63\u5728\u8F6C\u6362\u6253\u5370\u6570\u636E ${done}/${total}`,
4803
+ 30 + (total ? done / total : 1) * 25
4804
+ )
4805
+ });
4806
+ const template = resolveTemplate(source);
4807
+ validate?.(source, template, createTableData(source, template, rows));
4808
+ report("\u6B63\u5728\u751F\u6210\u5206\u9875", 55);
4809
+ const prepared = await createTablePreviewSource(
4810
+ source,
4811
+ template,
4812
+ {
4813
+ signal,
4814
+ onProgress: (done, total) => report(
4815
+ `\u6B63\u5728\u751F\u6210\u5206\u9875 ${done}/${total}`,
4816
+ 55 + (total ? done / total : 1) * 15
5545
4817
  )
5546
- ]
4818
+ },
4819
+ rows
4820
+ );
4821
+ report("\u6B63\u5728\u751F\u6210\u6253\u5370\u9875\u9762", 70);
4822
+ for await (const batch of transformPrintDesignToHtmlBatches(
4823
+ prepared.document,
4824
+ {
4825
+ pagesPerBatch,
4826
+ data: prepared.data,
4827
+ signal,
4828
+ onProgress: (value) => report("\u6B63\u5728\u751F\u6210\u6253\u5370\u9875\u9762", 70 + value * 29)
4829
+ }
4830
+ )) {
4831
+ report(`\u6B63\u5728\u6253\u5370\u7B2C ${batch.firstPage}\u2013${batch.lastPage} \u9875`, maximum);
4832
+ await new Promise((resolve) => setTimeout(resolve, 0));
4833
+ signal.throwIfAborted();
4834
+ frame = ownerDocument.createElement("iframe");
4835
+ frame.title = "\u8868\u683C\u6253\u5370";
4836
+ frame.setAttribute("aria-hidden", "true");
4837
+ frame.style.cssText = "position:fixed;left:-10000px;top:0;width:1px;height:1px;border:0";
4838
+ container.append(frame);
4839
+ const printWindow = frame.contentWindow;
4840
+ if (!printWindow) throw new Error("\u65E0\u6CD5\u521B\u5EFA\u6253\u5370\u9875\u9762");
4841
+ await new Promise((resolve, reject) => {
4842
+ const abort = () => {
4843
+ dispose();
4844
+ reject(signal.reason);
4845
+ };
4846
+ const dispose = () => {
4847
+ frame?.removeEventListener("load", loaded);
4848
+ signal.removeEventListener("abort", abort);
4849
+ };
4850
+ const loaded = () => {
4851
+ dispose();
4852
+ resolve();
4853
+ };
4854
+ frame.addEventListener("load", loaded, { once: true });
4855
+ signal.addEventListener("abort", abort, { once: true });
4856
+ printWindow.document.open();
4857
+ printWindow.document.write(batch.html);
4858
+ printWindow.document.close();
4859
+ });
4860
+ await Promise.all([
4861
+ ...Array.from(
4862
+ printWindow.document.images,
4863
+ (image) => image.decode?.().catch(() => void 0)
4864
+ ),
4865
+ printWindow.document.fonts?.ready
4866
+ ]);
4867
+ signal.throwIfAborted();
4868
+ await new Promise((resolve, reject) => {
4869
+ const dispose = () => {
4870
+ printWindow.removeEventListener("afterprint", finished);
4871
+ signal.removeEventListener("abort", aborted);
4872
+ };
4873
+ const finished = () => {
4874
+ dispose();
4875
+ resolve();
4876
+ };
4877
+ const aborted = () => {
4878
+ dispose();
4879
+ reject(signal.reason);
4880
+ };
4881
+ printWindow.addEventListener("afterprint", finished, { once: true });
4882
+ signal.addEventListener("abort", aborted, { once: true });
4883
+ try {
4884
+ printWindow.focus();
4885
+ printWindow.print();
4886
+ } catch (error) {
4887
+ dispose();
4888
+ reject(error);
4889
+ }
4890
+ });
4891
+ frame.remove();
4892
+ frame = void 0;
4893
+ if (batch.hasMore) {
4894
+ await progress.continueBatch(batch.firstPage, batch.lastPage);
4895
+ signal.throwIfAborted();
4896
+ report("\u6B63\u5728\u751F\u6210\u4E0B\u4E00\u6279\u6253\u5370\u9875\u9762", maximum);
4897
+ }
5547
4898
  }
5548
- );
4899
+ report("\u6253\u5370\u7A97\u53E3\u5DF2\u5173\u95ED", 100);
4900
+ await cleanup();
4901
+ } catch (error) {
4902
+ frame?.remove();
4903
+ if (!signal.aborted) {
4904
+ progress.error(
4905
+ `\u6253\u5370\u5931\u8D25\uFF1A${error instanceof Error ? error.message : String(error)}`
4906
+ );
4907
+ } else await cleanup(true);
4908
+ } finally {
4909
+ clearInterval(timer);
4910
+ }
5549
4911
  }
5550
4912
 
5551
4913
  // src/features/editor/utils.ts
@@ -5588,12 +4950,13 @@ function getEditorInputStyle(request) {
5588
4950
  height,
5589
4951
  minHeight: height,
5590
4952
  color: request.theme.cellColor ?? request.theme.textColor,
4953
+ textAlign: request.theme.textAlign,
5591
4954
  fontFamily: request.theme.fontFamily,
5592
4955
  fontSize,
5593
4956
  // 用像素 token,确保 select/time/color 触发器与内部 Input 同步缩放。
5594
4957
  ["--canvas-react-control-height"]: `${height}px`,
5595
4958
  ["--canvas-react-font-size"]: fontSize != null ? `${fontSize}px` : void 0,
5596
- ["--canvas-react-control-padding-inline"]: `${paddingInline}px`,
4959
+ ["--canvas-react-control-padding-inline"]: `${Math.max(paddingInline - 1, 0)}px`,
5597
4960
  ["--canvas-react-control-padding-block"]: `${paddingBlock}px`,
5598
4961
  ["--canvas-react-content-zoom"]: String(contentZoom),
5599
4962
  ["--canvas-react-radius-sm"]: "0px",
@@ -5620,7 +4983,7 @@ function resolveEditorOverlayStyle(params) {
5620
4983
  boxSizing: "border-box",
5621
4984
  // 覆盖表单组件 token,使输入框高度/字号/内边距跟随表格缩放。
5622
4985
  ["--canvas-react-control-height"]: `${height}px`,
5623
- ["--canvas-react-control-padding-inline"]: `${resolvedPaddingInline}px`,
4986
+ ["--canvas-react-control-padding-inline"]: `${Math.max(resolvedPaddingInline - 1, 0)}px`,
5624
4987
  ["--canvas-react-control-padding-block"]: `${resolvedPaddingBlock}px`,
5625
4988
  ["--canvas-react-content-zoom"]: String(resolvedContentZoom),
5626
4989
  ["--canvas-react-radius-sm"]: "0px",
@@ -5634,6 +4997,16 @@ function resolveEditorOverlayStyle(params) {
5634
4997
  function ReactTableEditorOverlay(props) {
5635
4998
  const { request } = props;
5636
4999
  const editorHostRef = useRef(null);
5000
+ const wheelCommittedRef = useRef(false);
5001
+ const editorRequest = {
5002
+ ...request,
5003
+ onCommit: (options) => {
5004
+ if (!wheelCommittedRef.current) request.onCommit(options);
5005
+ }
5006
+ };
5007
+ useEffect(() => {
5008
+ wheelCommittedRef.current = false;
5009
+ }, [request.editorKey]);
5637
5010
  const style = resolveEditorOverlayStyle({
5638
5011
  rect: request.rect,
5639
5012
  hostRect: request.hostRect,
@@ -5650,7 +5023,26 @@ function ReactTableEditorOverlay(props) {
5650
5023
  }
5651
5024
  focusAndSelectEditorInput(input, request.selectOnFocus);
5652
5025
  }, [request]);
5653
- return /* @__PURE__ */ jsx(ReactContentScaleProvider, { scale: contentZoom, children: /* @__PURE__ */ jsx("div", { ref: editorHostRef, children: renderEditorByType(request, style) }, request.editorKey) });
5026
+ useEffect(() => {
5027
+ const host = editorHostRef.current;
5028
+ if (!host) return;
5029
+ const valueType = request.config.valueType ?? "text";
5030
+ if (valueType !== "text" && valueType !== "number" && valueType !== "input-range") {
5031
+ return;
5032
+ }
5033
+ const handleWheel = (event) => {
5034
+ event.preventDefault();
5035
+ event.stopPropagation();
5036
+ if (!wheelCommittedRef.current) {
5037
+ wheelCommittedRef.current = true;
5038
+ request.onCommit();
5039
+ }
5040
+ request.onWheel?.(event);
5041
+ };
5042
+ host.addEventListener("wheel", handleWheel, { passive: false });
5043
+ return () => host.removeEventListener("wheel", handleWheel);
5044
+ }, [request]);
5045
+ return /* @__PURE__ */ jsx(ReactContentScaleProvider, { scale: contentZoom, children: /* @__PURE__ */ jsx("div", { ref: editorHostRef, children: renderEditorByType(editorRequest, style) }, request.editorKey) });
5654
5046
  }
5655
5047
  function renderEditorByType(request, style) {
5656
5048
  switch (request.config.valueType ?? "text") {
@@ -5743,9 +5135,10 @@ function NumberEditor(props) {
5743
5135
  setValue(toNumberValue(request.value));
5744
5136
  }, [request.value]);
5745
5137
  return /* @__PURE__ */ jsx("div", { ref: hostRef, style, children: /* @__PURE__ */ jsx(
5746
- ReactNumberInput,
5138
+ ReactInputNumber,
5747
5139
  {
5748
5140
  value,
5141
+ initialText: typeof request.value === "string" ? request.value : void 0,
5749
5142
  min: toNumericProp(request.config.min),
5750
5143
  max: toNumericProp(request.config.max),
5751
5144
  step: toNumericProp(request.config.step),
@@ -6623,47 +6016,6 @@ function ReactTableDangerConfirmView(props) {
6623
6016
  }
6624
6017
  );
6625
6018
  }
6626
- function ReactTableTooltipView(props) {
6627
- const { request } = props;
6628
- const tooltipRef = useRef(null);
6629
- useLayoutEffect(() => {
6630
- const tooltip = tooltipRef.current;
6631
- if (!tooltip || !request || typeof window === "undefined") return;
6632
- const width = tooltip.getBoundingClientRect().width;
6633
- const viewportWidth = window.innerWidth;
6634
- tooltip.style.left = `${Math.max(8, Math.min(request.x - width / 2, viewportWidth - width - 8))}px`;
6635
- }, [request]);
6636
- if (!request) {
6637
- return null;
6638
- }
6639
- const zoom = typeof request.contentZoom === "number" && Number.isFinite(request.contentZoom) && request.contentZoom > 0 ? request.contentZoom : 1;
6640
- const fontSize = 14 * zoom;
6641
- const paddingBlock = 6 * zoom;
6642
- const paddingInline = 10 * zoom;
6643
- const borderRadius = 6 * zoom;
6644
- const maxWidth = 320 * zoom;
6645
- const style = {
6646
- position: "fixed",
6647
- left: request.x,
6648
- top: request.y,
6649
- maxWidth: request.variant === "resize" ? "calc(100vw - 16px)" : `min(${maxWidth}px, calc(100vw - 16px))`,
6650
- padding: `${paddingBlock}px ${paddingInline}px`,
6651
- borderRadius,
6652
- background: request.variant === "error" ? "#ffffff" : "rgba(17, 24, 39, 0.92)",
6653
- color: request.variant === "error" ? "#ff4d4f" : "#fff",
6654
- fontSize,
6655
- lineHeight: 1.5,
6656
- boxShadow: request.variant === "error" ? "0 10px 30px rgba(255, 77, 79, 0.16)" : "0 10px 30px rgba(15, 23, 42, 0.18)",
6657
- border: request.variant === "error" ? "1px solid rgba(255, 77, 79, 0.2)" : "none",
6658
- pointerEvents: "none",
6659
- zIndex: 1200,
6660
- whiteSpace: request.variant === "resize" ? "nowrap" : "pre-wrap",
6661
- wordBreak: "break-word",
6662
- overflow: "hidden",
6663
- textOverflow: request.variant === "resize" ? "ellipsis" : void 0
6664
- };
6665
- return /* @__PURE__ */ jsx("div", { ref: tooltipRef, style, children: request.content });
6666
- }
6667
6019
  function ToolbarIcon(props) {
6668
6020
  const { path, ...svgProps } = props;
6669
6021
  return /* @__PURE__ */ jsx(
@@ -6702,245 +6054,8 @@ function HolderOutlined() {
6702
6054
  return /* @__PURE__ */ jsx(ToolbarIcon, { path: "M300 276.5a56 56 0 1056-97 56 56 0 00-56 97zm0 284a56 56 0 1056-97 56 56 0 00-56 97zM640 228a56 56 0 10112 0 56 56 0 00-112 0zm0 284a56 56 0 10112 0 56 56 0 00-112 0zM300 844.5a56 56 0 1056-97 56 56 0 00-56 97zM640 796a56 56 0 10112 0 56 56 0 00-112 0z" });
6703
6055
  }
6704
6056
 
6705
- // src/components/fullscreen-portal.ts
6706
- function resolveFullscreenPortalContainer(host) {
6707
- const fullscreenElement = document.fullscreenElement;
6708
- return fullscreenElement instanceof HTMLElement && host && fullscreenElement.contains(host) ? fullscreenElement : document.body;
6709
- }
6710
- function SortableList(props) {
6711
- const { items, onOrderChange } = props;
6712
- const rootRef = useRef(null);
6713
- const dragRef = useRef(null);
6714
- const [dragState, setDragState] = useState(null);
6715
- useEffect(
6716
- () => () => {
6717
- document.body.classList.remove("react-list-table-sorting");
6718
- document.body.style.removeProperty("user-select");
6719
- },
6720
- []
6721
- );
6722
- useEffect(() => {
6723
- if (!dragState) return;
6724
- const scrollContainer = props.scrollContainerRef?.current;
6725
- let frameId = 0;
6726
- let lastTime = null;
6727
- const updateTarget = () => {
6728
- const current = dragRef.current;
6729
- if (!current) return;
6730
- const target = resolveClosestSortableItem({
6731
- items,
6732
- parentKey: current.source.parentKey,
6733
- layouts: current.layouts,
6734
- // 保留拖拽开始时的布局,补偿滚动位移,避免受行交换动画影响。
6735
- overlayCenterY: current.pointerY - current.offsetY + current.height / 2 + current.rootTop - (rootRef.current?.getBoundingClientRect().top ?? current.rootTop)
6736
- });
6737
- if (target) {
6738
- current.target = target;
6739
- }
6740
- setDragState({
6741
- sourceKey: current.source.key,
6742
- targetKey: current.target.key,
6743
- pointerY: current.pointerY
6744
- });
6745
- };
6746
- const handleMove = (event) => {
6747
- const current = dragRef.current;
6748
- if (!current || event.pointerId !== current.pointerId) return;
6749
- current.pointerY = event.clientY;
6750
- updateTarget();
6751
- };
6752
- const tick = (time) => {
6753
- const current = dragRef.current;
6754
- if (!current) return;
6755
- const elapsed = lastTime == null ? 0 : Math.min(time - lastTime, 50);
6756
- lastTime = time;
6757
- if (scrollContainer) {
6758
- const rect = scrollContainer.getBoundingClientRect();
6759
- const top = rect.top + scrollContainer.clientTop;
6760
- const height = scrollContainer.clientHeight;
6761
- const edge = Math.min(40, height / 2);
6762
- const bottom = top + height;
6763
- const direction = edge <= 0 ? 0 : current.pointerY < top + edge ? -Math.min(1, (top + edge - current.pointerY) / edge) : current.pointerY > bottom - edge ? Math.min(1, (current.pointerY - bottom + edge) / edge) : 0;
6764
- if (direction) {
6765
- scrollContainer.scrollTop = Math.max(
6766
- 0,
6767
- Math.min(
6768
- scrollContainer.scrollHeight - height,
6769
- scrollContainer.scrollTop + direction * elapsed * 0.5
6770
- )
6771
- );
6772
- updateTarget();
6773
- }
6774
- }
6775
- frameId = requestAnimationFrame(tick);
6776
- };
6777
- const finish = (commit) => {
6778
- const current = dragRef.current;
6779
- cancelAnimationFrame(frameId);
6780
- if (commit && current && current.source.key !== current.target.key) {
6781
- const siblings = items.filter(
6782
- (item) => item.parentKey === current.source.parentKey
6783
- );
6784
- const orderedKeys = resolveNextSortableOrder(
6785
- siblings.map((item) => item.key),
6786
- current.source.key,
6787
- current.target.key
6788
- );
6789
- if (orderedKeys) {
6790
- onOrderChange(current.source.parentKey, orderedKeys);
6791
- }
6792
- }
6793
- dragRef.current = null;
6794
- setDragState(null);
6795
- document.body.classList.remove("react-list-table-sorting");
6796
- document.body.style.removeProperty("user-select");
6797
- };
6798
- const handleEnd = (event) => {
6799
- if (event.pointerId !== dragRef.current?.pointerId) return;
6800
- handleMove(event);
6801
- finish(true);
6802
- };
6803
- const handleCancel = (event) => {
6804
- if (event.pointerId === dragRef.current?.pointerId) finish(false);
6805
- };
6806
- const handleBlur = () => finish(false);
6807
- frameId = requestAnimationFrame(tick);
6808
- scrollContainer?.addEventListener("scroll", updateTarget);
6809
- window.addEventListener("pointermove", handleMove);
6810
- window.addEventListener("pointerup", handleEnd);
6811
- window.addEventListener("pointercancel", handleCancel);
6812
- window.addEventListener("blur", handleBlur);
6813
- return () => {
6814
- cancelAnimationFrame(frameId);
6815
- scrollContainer?.removeEventListener("scroll", updateTarget);
6816
- window.removeEventListener("pointermove", handleMove);
6817
- window.removeEventListener("pointerup", handleEnd);
6818
- window.removeEventListener("pointercancel", handleCancel);
6819
- window.removeEventListener("blur", handleBlur);
6820
- };
6821
- }, [dragState != null, items, onOrderChange, props.scrollContainerRef]);
6822
- const sourceIndex = dragState ? items.findIndex((item) => item.key === dragState.sourceKey) : -1;
6823
- const targetIndex = dragState ? items.findIndex((item) => item.key === dragState.targetKey) : -1;
6824
- return /* @__PURE__ */ jsxs("div", { ref: rootRef, children: [
6825
- items.map((item, index) => {
6826
- const style = resolveSortableTransform(
6827
- index,
6828
- sourceIndex,
6829
- targetIndex,
6830
- dragRef.current?.height ?? 0
6831
- );
6832
- return /* @__PURE__ */ jsx(
6833
- "div",
6834
- {
6835
- "data-sortable-key": item.key,
6836
- className: dragState?.sourceKey === item.key ? "dragging" : "",
6837
- style,
6838
- children: props.renderItem(item, {
6839
- onPointerDown: (event) => {
6840
- if (event.button !== 0 || dragRef.current) return;
6841
- const row = event.currentTarget.closest(
6842
- "[data-sortable-key]"
6843
- );
6844
- if (!row) return;
6845
- const rect = row.getBoundingClientRect();
6846
- dragRef.current = {
6847
- source: item,
6848
- target: item,
6849
- layouts: collectSortableItemLayouts(rootRef.current),
6850
- offsetY: event.clientY - rect.top,
6851
- width: rect.width,
6852
- left: rect.left,
6853
- height: rect.height,
6854
- pointerId: event.pointerId,
6855
- pointerY: event.clientY,
6856
- rootTop: rootRef.current?.getBoundingClientRect().top ?? 0
6857
- };
6858
- setDragState({
6859
- sourceKey: item.key,
6860
- targetKey: item.key,
6861
- pointerY: event.clientY
6862
- });
6863
- document.body.classList.add("react-list-table-sorting");
6864
- document.body.style.userSelect = "none";
6865
- event.preventDefault();
6866
- }
6867
- })
6868
- },
6869
- item.key
6870
- );
6871
- }),
6872
- dragState && dragRef.current ? createPortal(
6873
- /* @__PURE__ */ jsx(
6874
- "div",
6875
- {
6876
- className: "react-list-table-sort-overlay",
6877
- style: {
6878
- left: dragRef.current.left,
6879
- top: dragState.pointerY - dragRef.current.offsetY,
6880
- width: dragRef.current.width,
6881
- height: dragRef.current.height
6882
- },
6883
- children: props.renderItem(dragRef.current.source, {
6884
- onPointerDown: (event) => event.preventDefault()
6885
- })
6886
- }
6887
- ),
6888
- resolveFullscreenPortalContainer(rootRef.current)
6889
- ) : null
6890
- ] });
6891
- }
6892
- function resolveNextSortableOrder(currentKeys, sourceKey, targetKey) {
6893
- const sourceIndex = currentKeys.indexOf(sourceKey);
6894
- const targetIndex = currentKeys.indexOf(targetKey);
6895
- if (sourceIndex < 0 || targetIndex < 0 || sourceIndex === targetIndex) {
6896
- return null;
6897
- }
6898
- const nextKeys = [...currentKeys];
6899
- const [source] = nextKeys.splice(sourceIndex, 1);
6900
- nextKeys.splice(targetIndex, 0, source);
6901
- return nextKeys;
6902
- }
6903
- function collectSortableItemLayouts(root) {
6904
- if (!root) return [];
6905
- return Array.from(
6906
- root.querySelectorAll("[data-sortable-key]")
6907
- ).map((element) => {
6908
- const rect = element.getBoundingClientRect();
6909
- return {
6910
- key: element.dataset.sortableKey ?? "",
6911
- centerY: rect.top + rect.height / 2
6912
- };
6913
- });
6914
- }
6915
- function resolveClosestSortableItem(params) {
6916
- let closestItem = null;
6917
- let closestDistance = Number.POSITIVE_INFINITY;
6918
- params.layouts.forEach((layout) => {
6919
- const item = params.items.find((candidate) => candidate.key === layout.key);
6920
- if (!item || item.parentKey !== params.parentKey) return;
6921
- const distance = Math.abs(params.overlayCenterY - layout.centerY);
6922
- if (distance < closestDistance) {
6923
- closestDistance = distance;
6924
- closestItem = item;
6925
- }
6926
- });
6927
- return closestItem;
6928
- }
6929
- function resolveSortableTransform(index, sourceIndex, targetIndex, height) {
6930
- let translateY = 0;
6931
- if (sourceIndex < targetIndex && index > sourceIndex && index <= targetIndex) {
6932
- translateY = -height;
6933
- } else if (sourceIndex > targetIndex && index >= targetIndex && index < sourceIndex) {
6934
- translateY = height;
6935
- }
6936
- return {
6937
- transform: translateY ? `translate3d(0, ${translateY}px, 0)` : void 0,
6938
- transition: sourceIndex >= 0 ? "transform 180ms ease" : void 0
6939
- };
6940
- }
6941
-
6942
6057
  // src/features/toolbar/style.css
6943
- styleInject(".react-list-table-shell {\n background: #fff;\n font-size: 14px;\n}\n.react-list-table-titlebar {\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n box-sizing: border-box;\n background: #fff;\n}\n.react-list-table-shell-fullscreen {\n width: 100% !important;\n height: 100% !important;\n}\n.react-list-table-shell-fullscreen .canvas-list-table-root {\n height: 100% !important;\n}\n.react-list-table-title {\n min-width: 0;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-tools {\n display: flex;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-list-table-tool-wrap {\n position: relative;\n display: inline-flex;\n}\n.react-list-table-tool {\n width: 20px;\n height: 24px;\n padding: 0;\n border: 0;\n color: inherit;\n background: transparent;\n font: 16px/24px sans-serif;\n cursor: pointer;\n}\n.react-list-table-tool:hover {\n color: #1677ff;\n}\n.react-list-table-tool-menu,\n.react-list-table-column-setting {\n position: absolute;\n z-index: 120;\n top: 28px;\n right: 0;\n min-width: 112px;\n padding: 6px;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n background: #fff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: #374151;\n font-size: 14px;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu {\n width: max-content;\n min-width: 0;\n padding: 8px;\n border-radius: 8px;\n}\n.react-list-table-tool-menu-check {\n width: 16px;\n flex: 0 0 16px;\n margin-right: 6px;\n text-align: center;\n}\n.react-list-table-tool-menu button:hover {\n background: #f3f4f6;\n}\n.react-list-table-column-setting {\n width: 328px;\n max-height: 360px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.react-list-table-column-setting-head {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 4px 6px 8px;\n border-bottom: 1px solid #f0f0f0;\n flex: 0 0 auto;\n}\n.react-list-table-column-setting-head button {\n border: 0;\n color: #1677ff;\n background: transparent;\n cursor: pointer;\n}\n.react-list-table-column-setting-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.react-list-table-column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n padding: 7px 6px;\n box-sizing: border-box;\n cursor: pointer;\n}\n.react-list-table-column-position {\n flex: 0 0 28px;\n text-align: center;\n color: #64748b;\n font-variant-numeric: tabular-nums;\n}\n.react-list-table-column-main,\n.react-list-table-column-actions {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.react-list-table-column-main {\n min-width: 0;\n}\n.react-list-table-column-actions {\n flex: 0 0 auto;\n}\n.react-list-table-column-actions > .canvas-react-select-wrap {\n flex: 0 0 96px;\n width: 96px;\n height: 24px;\n}\n.react-list-table-column-label {\n min-width: 64px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-column-width-input {\n height: 22px;\n padding-inline: 5px;\n}\n.react-list-table-column-fixed-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-sort-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-setting label.dragging {\n opacity: 0.45;\n background: #e6f4ff;\n}\n.react-list-table-column-setting [data-sortable-key].dragging {\n opacity: 0.28;\n}\n.react-list-table-column-drag {\n touch-action: none;\n color: #94a3b8;\n display: inline-flex;\n font-size: 14px;\n cursor: grab;\n user-select: none;\n}\nbody.react-list-table-sorting,\nbody.react-list-table-sorting * {\n cursor: grabbing !important;\n}\n.react-list-table-sort-overlay {\n position: fixed;\n z-index: 1400;\n display: block;\n padding: 0;\n box-sizing: border-box;\n color: #334155;\n background: #fff;\n border: 1px solid #91caff;\n border-radius: 6px;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.18);\n pointer-events: none;\n}\n.react-list-table-sort-overlay .react-list-table-column-item {\n width: 100%;\n height: 100%;\n}\n");
6058
+ styleInject(".react-list-table-shell {\n background: #fff;\n font-size: 14px;\n}\n.react-list-table-titlebar {\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 0;\n box-sizing: border-box;\n background: #fff;\n}\n.react-list-table-shell-fullscreen {\n width: 100% !important;\n height: 100% !important;\n}\n.react-list-table-shell-fullscreen .canvas-list-table-root {\n height: 100% !important;\n}\n.react-list-table-title {\n min-width: 0;\n color: #1f2937;\n font-size: 14px;\n font-weight: 600;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-tools {\n display: flex;\n align-items: center;\n gap: 14px;\n color: #475569;\n}\n.react-list-table-tool-wrap {\n position: relative;\n display: inline-flex;\n}\n.react-list-table-tool {\n width: 20px;\n height: 24px;\n padding: 0;\n border: 0;\n color: inherit;\n background: transparent;\n font: 16px/24px sans-serif;\n cursor: pointer;\n}\n.react-list-table-tool:hover {\n color: #1677ff;\n}\n.react-list-table-tool-menu,\n.react-list-table-column-setting {\n position: absolute;\n z-index: 120;\n top: 28px;\n right: 0;\n min-width: 112px;\n padding: 6px;\n border: 1px solid #e5e7eb;\n border-radius: 6px;\n background: #fff;\n box-shadow: 0 8px 24px rgba(15, 23, 42, 0.14);\n}\n.react-list-table-tool-menu button {\n display: flex;\n align-items: center;\n justify-content: flex-start;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 0;\n border-radius: 8px;\n background: transparent;\n color: #374151;\n font-size: 14px;\n text-align: left;\n cursor: pointer;\n}\n.react-list-table-tool-menu {\n width: max-content;\n min-width: 0;\n padding: 8px;\n border-radius: 8px;\n}\n.react-list-table-tool-menu-check {\n width: 16px;\n flex: 0 0 16px;\n margin-right: 6px;\n text-align: center;\n}\n.react-list-table-tool-menu button:hover {\n background: #f3f4f6;\n}\n.react-list-table-column-setting {\n width: 328px;\n max-height: 360px;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n.react-list-table-column-setting-head {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n padding: 4px 6px 8px;\n border-bottom: 1px solid #f0f0f0;\n flex: 0 0 auto;\n}\n.react-list-table-column-setting-head button {\n border: 0;\n color: #1677ff;\n background: transparent;\n cursor: pointer;\n}\n.react-list-table-column-setting-body {\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n}\n.react-list-table-column-item {\n display: flex;\n align-items: center;\n gap: 8px;\n justify-content: space-between;\n padding: 7px 6px;\n box-sizing: border-box;\n cursor: pointer;\n}\n.react-list-table-column-position {\n flex: 0 0 28px;\n text-align: center;\n color: #64748b;\n font-variant-numeric: tabular-nums;\n}\n.react-list-table-column-main,\n.react-list-table-column-actions {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n.react-list-table-column-main {\n min-width: 0;\n}\n.react-list-table-column-actions {\n flex: 0 0 auto;\n}\n.react-list-table-column-actions > .canvas-react-select-wrap {\n flex: 0 0 96px;\n width: 96px;\n height: 24px;\n}\n.react-list-table-column-label {\n min-width: 64px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.react-list-table-column-width-input {\n height: 22px;\n padding-inline: 5px;\n}\n.react-list-table-column-fixed-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-sort-input {\n height: 22px;\n padding-inline: 6px;\n font-size: 14px;\n}\n.react-list-table-column-drag {\n touch-action: none;\n color: #94a3b8;\n display: inline-flex;\n font-size: 14px;\n cursor: grab;\n user-select: none;\n}\n.canvas-react-sort-overlay .react-list-table-column-item {\n width: 100%;\n height: 100%;\n}\n");
6944
6059
  function ReactTableToolbar(props) {
6945
6060
  const { toolbar, title } = props;
6946
6061
  const [densityOpen, setDensityOpen] = useState(false);
@@ -7071,18 +6186,17 @@ function ReactTableToolbar(props) {
7071
6186
  children: [
7072
6187
  /* @__PURE__ */ jsxs("div", { className: "react-list-table-column-setting-head", children: [
7073
6188
  /* @__PURE__ */ jsx("strong", { children: "\u5217\u8BBE\u7F6E" }),
7074
- /* @__PURE__ */ jsx(
6189
+ props.columnSettingsCacheAvailable ? /* @__PURE__ */ jsx(
7075
6190
  ReactCheckbox,
7076
6191
  {
7077
6192
  checked: props.cacheColumnSettings === true,
7078
- disabled: !props.columnSettingsCacheAvailable,
7079
- title: props.columnSettingsCacheAvailable ? "\u7F13\u5B58\u5217\u5BBD\u3001\u663E\u9690\u3001\u56FA\u5B9A\u548C\u987A\u5E8F" : "\u914D\u7F6E storageKey \u540E\u53EF\u7F13\u5B58\u5217\u8BBE\u7F6E",
6193
+ title: "\u7F13\u5B58\u5217\u5BBD\u3001\u663E\u9690\u3001\u56FA\u5B9A\u548C\u987A\u5E8F",
7080
6194
  onChange: (event) => props.onColumnSettingsCacheChange?.(
7081
6195
  event.target.checked
7082
6196
  ),
7083
6197
  children: "\u7F13\u5B58\u5230\u672C\u5730"
7084
6198
  }
7085
- ),
6199
+ ) : null,
7086
6200
  /* @__PURE__ */ jsx("span", { style: { marginLeft: "auto" }, children: config.columnSettingHeaderExtra }),
7087
6201
  /* @__PURE__ */ jsx("button", { type: "button", onClick: props.onResetColumns, children: "\u91CD\u7F6E" })
7088
6202
  ] }),
@@ -7092,13 +6206,15 @@ function ReactTableToolbar(props) {
7092
6206
  ref: columnSettingsBodyRef,
7093
6207
  className: "react-list-table-column-setting-body canvas-react-scrollbar",
7094
6208
  children: /* @__PURE__ */ jsx(
7095
- SortableList,
6209
+ ReactSortableList,
7096
6210
  {
7097
6211
  scrollContainerRef: columnSettingsBodyRef,
7098
- items: flattenColumns(props.columns).map((column, index) => ({
7099
- ...column,
7100
- position: index + 1
7101
- })),
6212
+ items: flattenColumns(props.columns).map(
6213
+ (column, index) => ({
6214
+ ...column,
6215
+ position: index + 1
6216
+ })
6217
+ ),
7102
6218
  onOrderChange: props.onColumnOrderChange,
7103
6219
  renderItem: (column, handleProps) => /* @__PURE__ */ jsxs("div", { className: "react-list-table-column-item", children: [
7104
6220
  /* @__PURE__ */ jsx(
@@ -7146,13 +6262,12 @@ function ReactTableToolbar(props) {
7146
6262
  ),
7147
6263
  !column.isGroup ? /* @__PURE__ */ jsxs("span", { className: "react-list-table-column-actions", children: [
7148
6264
  /* @__PURE__ */ jsx(
7149
- ReactNumberInput,
6265
+ ReactInputNumber,
7150
6266
  {
7151
6267
  className: "react-list-table-column-width-input",
7152
6268
  min: column.minWidth,
7153
6269
  max: column.maxWidth,
7154
6270
  value: column.width,
7155
- allowClear: false,
7156
6271
  "aria-label": `${column.label}\u5217\u5BBD`,
7157
6272
  style: { width: 80, height: 24 },
7158
6273
  onChange: (width) => {
@@ -7525,6 +6640,12 @@ function resolveReactListTableDebugOptions(debug, inheritedEnabled) {
7525
6640
  };
7526
6641
  }
7527
6642
 
6643
+ // src/components/fullscreen-portal.ts
6644
+ function resolveFullscreenPortalContainer(host) {
6645
+ const fullscreenElement = document.fullscreenElement;
6646
+ return fullscreenElement instanceof HTMLElement && host && fullscreenElement.contains(host) ? fullscreenElement : document.body;
6647
+ }
6648
+
7528
6649
  // src/components/column-manager-state.ts
7529
6650
  function applyColumnManagerState(columns, visibility, orders, config, parentKey = "root") {
7530
6651
  const order = orders.get(parentKey);
@@ -7898,13 +7019,61 @@ function ReactListTableInner(props, ref) {
7898
7019
  closeDangerConfirmRequest
7899
7020
  } = useReactListTableState(options);
7900
7021
  const hasPagination = paginationConfig != null;
7901
- const [printSource, setPrintSource] = useState(null);
7022
+ const [printRequest, setPrintRequest] = useState(null);
7023
+ const [savedPrintTemplates, setSavedPrintTemplates] = useState([]);
7024
+ const printStoreKey = storageKey ? `list:${storageKey}` : void 0;
7025
+ const printMenuOpen = Boolean(headerContextMenuRequest);
7026
+ useEffect(() => {
7027
+ if (!printMenuOpen || !printStoreKey) {
7028
+ setSavedPrintTemplates([]);
7029
+ return;
7030
+ }
7031
+ let disposed = false;
7032
+ void readPrintDesignTemplates(printStoreKey).then((versions) => {
7033
+ if (!disposed) setSavedPrintTemplates(versions);
7034
+ }).catch(() => {
7035
+ if (!disposed) setSavedPrintTemplates([]);
7036
+ });
7037
+ return () => {
7038
+ disposed = true;
7039
+ };
7040
+ }, [printMenuOpen, printStoreKey]);
7041
+ const latestSavedTemplates = [
7042
+ ...new Set(savedPrintTemplates.map((item) => item.templateId))
7043
+ ].map((templateId) => {
7044
+ const versions = savedPrintTemplates.filter(
7045
+ (item) => item.templateId === templateId
7046
+ );
7047
+ return versions[versions.length - 1];
7048
+ });
7049
+ const printTemplateChoices = [
7050
+ { key: "base", label: "\u8868\u683C\u6253\u5370\u6A21\u677F" },
7051
+ ...props.printPresets?.map((item, index) => ({
7052
+ key: `preset:${index}`,
7053
+ label: item.name
7054
+ })) ?? [],
7055
+ ...props.printTemplates?.map((item, index) => ({
7056
+ key: `document:${index}`,
7057
+ label: item.name
7058
+ })) ?? [],
7059
+ ...latestSavedTemplates.map((item) => ({
7060
+ key: `saved:${item.id}`,
7061
+ label: `${item.document.name} (v${item.version})`
7062
+ }))
7063
+ ];
7064
+ const [cacheConfigOpen, setCacheConfigOpen] = useState(false);
7902
7065
  const paginationLayout = resolveReactListTablePaginationLayout(resolvedRowHeight);
7903
7066
  const portalContainer = resolveFullscreenPortalContainer(shellRef.current);
7904
7067
  useImperativeHandle(
7905
7068
  ref,
7906
7069
  () => ({
7907
7070
  validate: () => tableRef.current?.validate() ?? Promise.resolve([]),
7071
+ setValidationErrors: (errors) => {
7072
+ tableRef.current?.setValidationErrors(errors);
7073
+ },
7074
+ clearValidationErrors: (targets) => {
7075
+ tableRef.current?.clearValidationErrors(targets);
7076
+ },
7908
7077
  exportXlsx: (options2) => {
7909
7078
  tableRef.current?.exportXlsx(options2);
7910
7079
  },
@@ -8065,7 +7234,7 @@ function ReactListTableInner(props, ref) {
8065
7234
  }
8066
7235
  ),
8067
7236
  tooltipRequest ? createPortal(
8068
- /* @__PURE__ */ jsx(ReactTableTooltipView, { request: tooltipRequest }),
7237
+ /* @__PURE__ */ jsx(ReactTooltip, { request: tooltipRequest }),
8069
7238
  portalContainer
8070
7239
  ) : null,
8071
7240
  filterPanelRequest ? createPortal(
@@ -8080,29 +7249,58 @@ function ReactListTableInner(props, ref) {
8080
7249
  /* @__PURE__ */ jsx(
8081
7250
  ReactTableContextMenuView,
8082
7251
  {
7252
+ printTemplates: printTemplateChoices,
7253
+ onPrintTemplate: (key) => {
7254
+ const request = headerContextMenuRequest;
7255
+ if (!request?.onGetPrintData) return;
7256
+ request.onClose();
7257
+ void runDirectPrint(
7258
+ portalContainer,
7259
+ request.onGetPrintData,
7260
+ (source) => {
7261
+ const document2 = key.startsWith("saved:") ? savedPrintTemplates.find((item) => item.id === key.slice(6))?.document : key.startsWith("preset:") ? createTableDocument(
7262
+ { ...source, rows: [] },
7263
+ props.printPresets?.[Number(key.slice(7))]
7264
+ ) : key.startsWith("document:") ? props.printTemplates?.[Number(key.slice(9))] : createTableDocument({ ...source, rows: [] });
7265
+ if (!document2) throw new Error("\u627E\u4E0D\u5230\u6253\u5370\u6A21\u677F");
7266
+ return document2;
7267
+ },
7268
+ (source, document2, data) => {
7269
+ if (!isPrintDesignDocumentCompatible(document2, data))
7270
+ throw new Error("\u6253\u5370\u6A21\u677F\u5DF2\u5931\u6548");
7271
+ if (key.startsWith("saved:") && isPrintDesignTemplateInvalid(
7272
+ savedPrintTemplates.find(
7273
+ (item) => item.id === key.slice(6)
7274
+ ),
7275
+ getPrintSourceFormat(source),
7276
+ data
7277
+ ))
7278
+ throw new Error("\u6253\u5370\u6A21\u677F\u5DF2\u5931\u6548");
7279
+ },
7280
+ props.directPrintPagesPerBatch
7281
+ );
7282
+ },
7283
+ onViewCacheConfig: () => {
7284
+ headerContextMenuRequest?.onClose();
7285
+ setCacheConfigOpen(true);
7286
+ },
8083
7287
  onPrint: () => {
8084
7288
  const getPrintData = headerContextMenuRequest?.onGetPrintData;
8085
7289
  if (!getPrintData) return;
8086
- void Promise.resolve(getPrintData()).then(
8087
- (source) => setPrintSource(source)
8088
- );
7290
+ setPrintRequest({ loadSource: getPrintData });
8089
7291
  headerContextMenuRequest?.onClose();
8090
7292
  },
8091
7293
  onDirectPrint: () => {
8092
7294
  const request = headerContextMenuRequest;
8093
7295
  if (!request?.onGetPrintData) return;
8094
7296
  request.onClose();
8095
- void Promise.resolve(request.onGetPrintData()).then(
8096
- (source) => createCachedPrintTableOptions(
8097
- source,
8098
- storageKey ? `list:${storageKey}` : void 0,
8099
- props.printPresets
8100
- )
8101
- ).then((options2) => {
8102
- printBrowserTable(options2);
8103
- }).catch((error) => {
8104
- console.error("\u76F4\u63A5\u6253\u5370\u5931\u8D25", error);
8105
- });
7297
+ void runDirectPrint(
7298
+ portalContainer,
7299
+ request.onGetPrintData,
7300
+ void 0,
7301
+ void 0,
7302
+ props.directPrintPagesPerBatch
7303
+ );
8106
7304
  },
8107
7305
  request: headerContextMenuRequest ? {
8108
7306
  ...headerContextMenuRequest,
@@ -8124,14 +7322,25 @@ function ReactListTableInner(props, ref) {
8124
7322
  ),
8125
7323
  portalContainer
8126
7324
  ),
8127
- printSource ? createPortal(
7325
+ printRequest ? createPortal(
8128
7326
  /* @__PURE__ */ jsx(
8129
- ReactListTablePrintDialog,
7327
+ ReactListTablePrintDesignDialog,
8130
7328
  {
8131
- source: printSource,
7329
+ loadSource: printRequest.loadSource,
7330
+ storeKey: storageKey ? `list:${storageKey}` : void 0,
8132
7331
  printPresets: props.printPresets,
8133
- storageKey: storageKey ? `list:${storageKey}` : void 0,
8134
- onClose: () => setPrintSource(null),
7332
+ printTemplates: props.printTemplates,
7333
+ onClose: () => setPrintRequest(null)
7334
+ }
7335
+ ),
7336
+ portalContainer
7337
+ ) : null,
7338
+ cacheConfigOpen && storageKey ? createPortal(
7339
+ /* @__PURE__ */ jsx(
7340
+ ReactListTableCacheConfigDialog,
7341
+ {
7342
+ storageKey,
7343
+ onClose: () => setCacheConfigOpen(false),
8135
7344
  renderTable: (tableProps) => /* @__PURE__ */ jsx(ReactListTable, { ...tableProps })
8136
7345
  }
8137
7346
  ),