@visactor/vtable-plugins 1.22.8-alpha.13 → 1.22.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/cjs/excel-import/excel.d.ts +23 -0
  2. package/cjs/excel-import/excel.js +204 -0
  3. package/cjs/excel-import/excel.js.map +1 -0
  4. package/cjs/excel-import/index.d.ts +3 -0
  5. package/cjs/excel-import/index.js +39 -0
  6. package/cjs/excel-import/index.js.map +1 -0
  7. package/cjs/excel-import/types.d.ts +44 -0
  8. package/cjs/excel-import/types.js +6 -0
  9. package/cjs/excel-import/types.js.map +1 -0
  10. package/cjs/excel-import/vtable-sheet.d.ts +2 -0
  11. package/cjs/excel-import/vtable-sheet.js +31 -0
  12. package/cjs/excel-import/vtable-sheet.js.map +1 -0
  13. package/cjs/excel-import.d.ts +12 -22
  14. package/cjs/excel-import.js +107 -40
  15. package/cjs/excel-import.js.map +1 -1
  16. package/cjs/filter/filter-toolbar.d.ts +1 -0
  17. package/cjs/filter/filter-toolbar.js +5 -4
  18. package/cjs/filter/filter-toolbar.js.map +1 -1
  19. package/cjs/filter/index.js +1 -2
  20. package/cjs/filter/value-filter.js +1 -1
  21. package/cjs/filter/value-filter.js.map +1 -1
  22. package/cjs/master-detail-plugin/checkbox.js +1 -0
  23. package/cjs/master-detail-plugin/index.js +1 -1
  24. package/cjs/master-detail-plugin/subtable.js +1 -1
  25. package/cjs/master-detail-plugin/table-api-extensions.js +1 -1
  26. package/cjs/master-detail-plugin/types.js +1 -1
  27. package/cjs/table-export/excel/index.d.ts +4 -0
  28. package/cjs/table-export/excel/index.js +72 -36
  29. package/cjs/table-export/excel/index.js.map +1 -1
  30. package/cjs/table-export/index.d.ts +2 -2
  31. package/cjs/table-export/index.js +6 -1
  32. package/cjs/table-export/index.js.map +1 -1
  33. package/cjs/table-export.js +4 -1
  34. package/cjs/table-export.js.map +1 -1
  35. package/cjs/table-series-number.js +1 -0
  36. package/cjs/table-series-number.js.map +1 -1
  37. package/dist/vtable-plugins.js +591 -122
  38. package/dist/vtable-plugins.min.js +3 -3
  39. package/es/excel-import/excel.d.ts +23 -0
  40. package/es/excel-import/excel.js +190 -0
  41. package/es/excel-import/excel.js.map +1 -0
  42. package/es/excel-import/index.d.ts +3 -0
  43. package/es/excel-import/index.js +4 -0
  44. package/es/excel-import/index.js.map +1 -0
  45. package/es/excel-import/types.d.ts +44 -0
  46. package/es/excel-import/types.js +2 -0
  47. package/es/excel-import/types.js.map +1 -0
  48. package/es/excel-import/vtable-sheet.d.ts +2 -0
  49. package/es/excel-import/vtable-sheet.js +25 -0
  50. package/es/excel-import/vtable-sheet.js.map +1 -0
  51. package/es/excel-import.d.ts +12 -22
  52. package/es/excel-import.js +110 -40
  53. package/es/excel-import.js.map +1 -1
  54. package/es/filter/filter-toolbar.d.ts +1 -0
  55. package/es/filter/filter-toolbar.js +6 -4
  56. package/es/filter/filter-toolbar.js.map +1 -1
  57. package/es/filter/index.js +1 -2
  58. package/es/filter/value-filter.js +1 -1
  59. package/es/filter/value-filter.js.map +1 -1
  60. package/es/master-detail-plugin/checkbox.js +2 -1
  61. package/es/master-detail-plugin/index.js +1 -1
  62. package/es/master-detail-plugin/subtable.js +1 -1
  63. package/es/master-detail-plugin/table-api-extensions.js +1 -1
  64. package/es/master-detail-plugin/types.js +1 -1
  65. package/es/table-export/excel/index.d.ts +4 -0
  66. package/es/table-export/excel/index.js +69 -33
  67. package/es/table-export/excel/index.js.map +1 -1
  68. package/es/table-export/index.d.ts +2 -2
  69. package/es/table-export/index.js +2 -2
  70. package/es/table-export/index.js.map +1 -1
  71. package/es/table-export.js +5 -2
  72. package/es/table-export.js.map +1 -1
  73. package/es/table-series-number.js +1 -0
  74. package/es/table-series-number.js.map +1 -1
  75. package/package.json +8 -8
@@ -8622,12 +8622,304 @@
8622
8622
  * @copyright Louis-Dominique Dubeau
8623
8623
  */function n(e){return e>=65&&e<=90||95===e||e>=97&&e<=122||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=767||e>=880&&e<=893||e>=895&&e<=8191||e>=8204&&e<=8205||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039;}Object.defineProperty(r,"__esModule",{value:!0}),r.NC_NAME_START_CHAR="A-Z_a-z\xc0-\xd6\xd8-\xf6\xf8-\u02ff\u0370-\u037d\u037f-\u1fff\u200c-\u200d\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd\ud800\udc00-\udb7f\udfff",r.NC_NAME_CHAR="-"+r.NC_NAME_START_CHAR+".0-9\xb7\u0300-\u036f\u203f-\u2040",r.NC_NAME_START_CHAR_RE=new RegExp("^["+r.NC_NAME_START_CHAR+"]$","u"),r.NC_NAME_CHAR_RE=new RegExp("^["+r.NC_NAME_CHAR+"]$","u"),r.NC_NAME_RE=new RegExp("^["+r.NC_NAME_START_CHAR+"]["+r.NC_NAME_CHAR+"]*$","u"),r.isNCNameStartChar=n,r.isNCNameChar=function(e){return n(e)||45===e||46===e||e>=48&&e<=57||183===e||e>=768&&e<=879||e>=8255&&e<=8256;};},{}]},{},[15])(15);});})(exceljs_min);var exceljs_minExports=exceljs_min.exports;var ExcelJS = /*@__PURE__*/getDefaultExportFromCjs(exceljs_minExports);
8624
8624
 
8625
+ async function importCsvFile(file, options) {
8626
+ const text = await file.text();
8627
+ const delimiter = options?.delimiter || ',';
8628
+ const lines = text.split(/\r?\n/).filter(line => line.trim().length > 0);
8629
+ if (lines.length === 0) {
8630
+ throw new Error('CSV 文件为空');
8631
+ }
8632
+ const data = [];
8633
+ for (const line of lines) {
8634
+ const row = parseCsvLine(line, delimiter);
8635
+ data.push(row);
8636
+ }
8637
+ const rowCount = data.length;
8638
+ const columnCount = Math.max(...data.map(row => row.length), 0);
8639
+ const sheetTitle = file.name.replace(/\.(csv|txt)$/i, '') || 'Sheet1';
8640
+ const sheetKey = `sheet_${Date.now()}_0`;
8641
+ const sheetData = {
8642
+ sheetTitle,
8643
+ sheetKey,
8644
+ data,
8645
+ columnCount,
8646
+ rowCount
8647
+ };
8648
+ return {
8649
+ sheets: [sheetData]
8650
+ };
8651
+ }
8652
+ function parseCsvLine(line, delimiter) {
8653
+ const result = [];
8654
+ let current = '';
8655
+ let inQuotes = false;
8656
+ for (let i = 0; i < line.length; i++) {
8657
+ const char = line[i];
8658
+ const nextChar = line[i + 1];
8659
+ if (char === '"') {
8660
+ if (inQuotes && nextChar === '"') {
8661
+ current += '"';
8662
+ i++;
8663
+ }
8664
+ else {
8665
+ inQuotes = !inQuotes;
8666
+ }
8667
+ }
8668
+ else if (char === delimiter && !inQuotes) {
8669
+ result.push(current.trim());
8670
+ current = '';
8671
+ }
8672
+ else {
8673
+ current += char;
8674
+ }
8675
+ }
8676
+ result.push(current.trim());
8677
+ return result;
8678
+ }
8679
+ async function importExcelMultipleSheets(file, options) {
8680
+ const workbook = new ExcelJS.Workbook();
8681
+ await workbook.xlsx.load(await file.arrayBuffer());
8682
+ if (!workbook.worksheets || workbook.worksheets.length === 0) {
8683
+ throw new Error('Excel 文件无有效工作表');
8684
+ }
8685
+ let sheetIndices;
8686
+ if (options?.sheetIndices && options.sheetIndices.length > 0) {
8687
+ sheetIndices = options.sheetIndices.filter(index => index >= 0 && index < workbook.worksheets.length);
8688
+ }
8689
+ else {
8690
+ sheetIndices = Array.from({ length: workbook.worksheets.length }, (_, i) => i);
8691
+ }
8692
+ const sheets = [];
8693
+ for (const sheetIndex of sheetIndices) {
8694
+ const worksheet = workbook.worksheets[sheetIndex];
8695
+ if (!worksheet) {
8696
+ continue;
8697
+ }
8698
+ try {
8699
+ const sheetData = await parseWorksheetToSheetData(worksheet, sheetIndex, options);
8700
+ sheets.push(sheetData);
8701
+ }
8702
+ catch (error) {
8703
+ }
8704
+ }
8705
+ if (sheets.length === 0) {
8706
+ throw new Error('没有成功解析任何工作表');
8707
+ }
8708
+ return { sheets };
8709
+ }
8710
+ async function parseWorksheetToSheetData(worksheet, sheetIndex, options) {
8711
+ const sheetTitle = worksheet.name || `Sheet${sheetIndex + 1}`;
8712
+ const sheetKey = `sheet_${Date.now()}_${sheetIndex}`;
8713
+ const rowCount = worksheet.actualRowCount || 0;
8714
+ const columnCount = worksheet.actualColumnCount || 0;
8715
+ if (rowCount === 0 || columnCount === 0) {
8716
+ const cellMerge = parseMergedCells(worksheet, []);
8717
+ return {
8718
+ sheetTitle,
8719
+ sheetKey,
8720
+ data: [],
8721
+ columnCount: 0,
8722
+ rowCount: 0,
8723
+ ...(cellMerge.length > 0 ? { cellMerge } : {})
8724
+ };
8725
+ }
8726
+ const data = [];
8727
+ for (let rowNumber = 1; rowNumber <= rowCount; rowNumber++) {
8728
+ const row = worksheet.getRow(rowNumber);
8729
+ const rowData = [];
8730
+ for (let colNumber = 1; colNumber <= columnCount; colNumber++) {
8731
+ const cell = row.getCell(colNumber);
8732
+ let cellValue = cell.value;
8733
+ if (cellValue && typeof cellValue === 'object') {
8734
+ if ('richText' in cellValue) {
8735
+ cellValue = cellValue.richText
8736
+ .map((rt) => rt.text)
8737
+ .join('');
8738
+ }
8739
+ else if ('result' in cellValue) {
8740
+ cellValue = cellValue.result;
8741
+ }
8742
+ else if ('text' in cellValue && 'hyperlink' in cellValue) {
8743
+ cellValue = cellValue.text;
8744
+ }
8745
+ else if (cellValue instanceof Date) {
8746
+ cellValue = cellValue.toISOString();
8747
+ }
8748
+ }
8749
+ rowData.push(cellValue ?? null);
8750
+ }
8751
+ data.push(rowData);
8752
+ }
8753
+ const cellMerge = parseMergedCells(worksheet, data);
8754
+ return {
8755
+ sheetTitle,
8756
+ sheetKey,
8757
+ data,
8758
+ columnCount,
8759
+ rowCount,
8760
+ ...(cellMerge.length > 0 ? { cellMerge } : {})
8761
+ };
8762
+ }
8763
+ function parseMergedCells(worksheet, data) {
8764
+ const cellMerge = [];
8765
+ try {
8766
+ const worksheetAny = worksheet;
8767
+ const merges = worksheetAny.model?.merges ||
8768
+ worksheetAny._merges ||
8769
+ {};
8770
+ for (const [masterCell, range] of Object.entries(merges)) {
8771
+ try {
8772
+ let startCol;
8773
+ let startRow;
8774
+ let endCol;
8775
+ let endRow;
8776
+ if (typeof range === 'string') {
8777
+ const rangeMatch = range.match(/^([A-Z]+\d+):([A-Z]+\d+)$/i);
8778
+ if (!rangeMatch) {
8779
+ continue;
8780
+ }
8781
+ const startAddr = parseCellAddress(rangeMatch[1]);
8782
+ const endAddr = parseCellAddress(rangeMatch[2]);
8783
+ if (!startAddr || !endAddr) {
8784
+ continue;
8785
+ }
8786
+ startCol = startAddr.col;
8787
+ startRow = startAddr.row;
8788
+ endCol = endAddr.col;
8789
+ endRow = endAddr.row;
8790
+ }
8791
+ else if (typeof range === 'object' && range !== null) {
8792
+ const rangeObj = range;
8793
+ if (rangeObj.tl && rangeObj.br) {
8794
+ const startAddr = parseCellAddress(rangeObj.tl);
8795
+ const endAddr = parseCellAddress(rangeObj.br);
8796
+ if (!startAddr || !endAddr) {
8797
+ continue;
8798
+ }
8799
+ startCol = startAddr.col;
8800
+ startRow = startAddr.row;
8801
+ endCol = endAddr.col;
8802
+ endRow = endAddr.row;
8803
+ }
8804
+ else if (typeof rangeObj.top === 'number' &&
8805
+ typeof rangeObj.left === 'number' &&
8806
+ typeof rangeObj.bottom === 'number' &&
8807
+ typeof rangeObj.right === 'number') {
8808
+ startRow = rangeObj.top - 1;
8809
+ startCol = rangeObj.left - 1;
8810
+ endRow = rangeObj.bottom - 1;
8811
+ endCol = rangeObj.right - 1;
8812
+ }
8813
+ else {
8814
+ continue;
8815
+ }
8816
+ }
8817
+ else {
8818
+ continue;
8819
+ }
8820
+ let text;
8821
+ if (startRow >= 0 && startRow < data.length && startCol >= 0 && startCol < data[startRow].length) {
8822
+ const cellValue = data[startRow][startCol];
8823
+ if (cellValue !== null && cellValue !== undefined) {
8824
+ text = String(cellValue);
8825
+ }
8826
+ }
8827
+ cellMerge.push({
8828
+ text,
8829
+ range: {
8830
+ start: {
8831
+ col: startCol,
8832
+ row: startRow
8833
+ },
8834
+ end: {
8835
+ col: endCol,
8836
+ row: endRow
8837
+ },
8838
+ isCustom: true
8839
+ }
8840
+ });
8841
+ }
8842
+ catch (error) {
8843
+ }
8844
+ }
8845
+ }
8846
+ catch (error) {
8847
+ }
8848
+ return cellMerge;
8849
+ }
8850
+ function parseCellAddress(address) {
8851
+ try {
8852
+ const match = address.match(/^([A-Z]+)(\d+)$/i);
8853
+ if (!match) {
8854
+ return null;
8855
+ }
8856
+ const colLetters = match[1].toUpperCase();
8857
+ const rowNumber = parseInt(match[2], 10);
8858
+ let col = 0;
8859
+ for (let i = 0; i < colLetters.length; i++) {
8860
+ col = col * 26 + (colLetters.charCodeAt(i) - 64);
8861
+ }
8862
+ col = col - 1;
8863
+ const row = rowNumber - 1;
8864
+ return { col, row };
8865
+ }
8866
+ catch (error) {
8867
+ return null;
8868
+ }
8869
+ }
8870
+
8871
+ function applyImportToVTableSheet(vtableSheetInstance, result, options) {
8872
+ const clearExisting = options?.clearExisting !== false;
8873
+ if (result.sheets.length === 0) {
8874
+ return;
8875
+ }
8876
+ if (typeof vtableSheetInstance.addSheet !== 'function') {
8877
+ throw new Error('VTableSheet 实例方法不完整');
8878
+ }
8879
+ const existingSheets = clearExisting ? [...vtableSheetInstance.getAllSheets()] : [];
8880
+ const existingSheetKeys = existingSheets.map((s) => s.sheetKey);
8881
+ const importedSheetKeys = [];
8882
+ for (let i = 0; i < result.sheets.length; i++) {
8883
+ const sheetData = result.sheets[i];
8884
+ let sheetKey = sheetData.sheetKey;
8885
+ let suffix = 1;
8886
+ while (vtableSheetInstance.getSheet(sheetKey) || importedSheetKeys.includes(sheetKey)) {
8887
+ sheetKey = `${sheetData.sheetKey}_${suffix}`;
8888
+ suffix++;
8889
+ }
8890
+ const sheetDefine = {
8891
+ sheetKey: sheetKey,
8892
+ sheetTitle: sheetData.sheetTitle,
8893
+ data: sheetData.data,
8894
+ rowCount: Math.max(sheetData.rowCount, 100),
8895
+ columnCount: Math.max(sheetData.columnCount, 26),
8896
+ cellMerge: sheetData.cellMerge,
8897
+ active: false
8898
+ };
8899
+ vtableSheetInstance.addSheet(sheetDefine);
8900
+ importedSheetKeys.push(sheetKey);
8901
+ }
8902
+ if (clearExisting && existingSheetKeys.length > 0) {
8903
+ for (const oldSheetKey of existingSheetKeys) {
8904
+ if (importedSheetKeys.includes(oldSheetKey)) {
8905
+ continue;
8906
+ }
8907
+ if (vtableSheetInstance.getSheet(oldSheetKey) && vtableSheetInstance.getSheetCount() > 1) {
8908
+ vtableSheetInstance.removeSheet(oldSheetKey);
8909
+ }
8910
+ }
8911
+ }
8912
+ if (importedSheetKeys.length > 0) {
8913
+ vtableSheetInstance.activateSheet(importedSheetKeys[0]);
8914
+ }
8915
+ }
8916
+
8625
8917
  class ExcelImportPlugin {
8626
8918
  id = `excel-import-plugin`;
8627
8919
  name = 'ExcelImportPlugin';
8628
8920
  runTime = [VTable.TABLE_EVENT_TYPE.INITIALIZED];
8629
8921
  options;
8630
- _tableInstance = null;
8922
+ table = null;
8631
8923
  constructor(options) {
8632
8924
  this.options = {
8633
8925
  autoTable: true,
@@ -8637,6 +8929,8 @@
8637
8929
  batchSize: 1000,
8638
8930
  enableBatchProcessing: true,
8639
8931
  asyncDelay: 5,
8932
+ importAllSheets: false,
8933
+ clearExisting: true,
8640
8934
  ...options
8641
8935
  };
8642
8936
  if (options?.id) {
@@ -8644,64 +8938,55 @@
8644
8938
  }
8645
8939
  }
8646
8940
  run(...args) {
8647
- const tableInstance = args[2];
8648
- this._tableInstance = tableInstance;
8649
- this._tableInstance.importFile = () => {
8650
- this.import('file');
8651
- };
8652
- }
8653
- release() {
8654
- this._tableInstance = null;
8655
- }
8656
- async importFile() {
8657
- return this.import('file');
8658
- }
8659
- async import(type, source, options) {
8660
- const mergedOptions = { ...this.options, ...options };
8661
- if (type === 'file') {
8662
- return this._importFromFileDialog(mergedOptions);
8941
+ const runTime = args[1];
8942
+ if (runTime === VTable.TABLE_EVENT_TYPE.INITIALIZED) {
8943
+ this.table = args[2];
8944
+ this.table.importFile = () => {
8945
+ this.import('file');
8946
+ };
8947
+ if (this.table.__vtableSheet) {
8948
+ const vtableSheet = this.table.__vtableSheet;
8949
+ if (!vtableSheet._importFile) {
8950
+ vtableSheet._importFile = async (options) => {
8951
+ const mergedOptions = { ...this.options, ...options };
8952
+ return this._importFileFromDialogForVTableSheet(vtableSheet, mergedOptions);
8953
+ };
8954
+ }
8955
+ }
8663
8956
  }
8664
- if (typeof source === 'string') {
8665
- return this._importFromString(type, source, mergedOptions);
8957
+ }
8958
+ async _importFile(options) {
8959
+ if (!this.table) {
8960
+ throw new Error('表格实例不存在或已销毁,无法导入数据!');
8666
8961
  }
8667
- if (Array.isArray(source) || typeof source === 'object') {
8668
- if (type !== 'json') {
8669
- throw new Error('只有JSON格式支持从对象导入');
8670
- }
8671
- return this._importFromData('json', source, mergedOptions);
8962
+ const mergedOptions = { ...this.options, ...options };
8963
+ const isVTableSheet = this._detectVTableSheet(this.table);
8964
+ if (isVTableSheet) {
8965
+ return this._importMultipleSheetsFromFileDialog({
8966
+ ...mergedOptions,
8967
+ importAllSheets: true
8968
+ });
8672
8969
  }
8673
- throw new Error('Invalid import source');
8970
+ throw new Error('ListTable sheet 导入功能需要进一步重构');
8674
8971
  }
8675
- async _importFromFileDialog(options) {
8972
+ _selectFile(accept = '.xlsx,.xls,.csv,.txt,.json,.html,.htm') {
8676
8973
  return new Promise((resolve, reject) => {
8677
- if (!this._tableInstance) {
8678
- reject(new Error('表格实例不存在或已销毁,无法导入数据!'));
8679
- return;
8680
- }
8681
8974
  const input = document.createElement('input');
8682
8975
  input.type = 'file';
8976
+ input.accept = accept;
8683
8977
  input.style.display = 'none';
8684
8978
  document.body.appendChild(input);
8685
- input.addEventListener('change', async (e) => {
8979
+ input.addEventListener('change', () => {
8686
8980
  try {
8687
- const file = e.target.files?.[0];
8981
+ const file = input.files?.[0];
8688
8982
  if (!file) {
8689
8983
  document.body.removeChild(input);
8690
8984
  reject(new Error('未选择文件'));
8691
8985
  return;
8692
8986
  }
8693
- const result = await this._parseFile(file, options);
8694
- if (options.autoTable && this._tableInstance) {
8695
- if (options.autoColumns) {
8696
- this._tableInstance.updateOption(Object.assign({}, this._tableInstance.options, {
8697
- columns: result.columns
8698
- }));
8699
- }
8700
- this._tableInstance.setRecords(result.records);
8701
- }
8702
8987
  input.value = '';
8703
8988
  document.body.removeChild(input);
8704
- resolve(result);
8989
+ resolve(file);
8705
8990
  }
8706
8991
  catch (error) {
8707
8992
  document.body.removeChild(input);
@@ -8711,6 +8996,116 @@
8711
8996
  input.click();
8712
8997
  });
8713
8998
  }
8999
+ async _importFileFromDialogForVTableSheet(vtableSheetInstance, options) {
9000
+ try {
9001
+ const file = await this._selectFile('.xlsx,.xls,.csv,.txt');
9002
+ const fileExtension = this._getFileExtension(file.name).toLowerCase();
9003
+ let result;
9004
+ if (fileExtension === 'csv' || fileExtension === 'txt') {
9005
+ result = await importCsvFile(file, options);
9006
+ }
9007
+ else if (fileExtension === 'xlsx' || fileExtension === 'xls') {
9008
+ result = await importExcelMultipleSheets(file, options);
9009
+ }
9010
+ else {
9011
+ throw new Error('不支持的文件类型,仅支持 Excel (.xlsx, .xls) 和 CSV (.csv, .txt)');
9012
+ }
9013
+ if (options.autoTable !== false && result.sheets.length > 0) {
9014
+ applyImportToVTableSheet(vtableSheetInstance, result, options);
9015
+ }
9016
+ return result;
9017
+ }
9018
+ catch (error) {
9019
+ throw error;
9020
+ }
9021
+ }
9022
+ async _importMultipleSheetsFromFileDialog(options) {
9023
+ try {
9024
+ const file = await this._selectFile('.xlsx,.xls,.csv,.txt');
9025
+ const fileExtension = this._getFileExtension(file.name).toLowerCase();
9026
+ let result;
9027
+ if (fileExtension === 'csv' || fileExtension === 'txt') {
9028
+ result = await importCsvFile(file, options);
9029
+ }
9030
+ else if (fileExtension === 'xlsx' || fileExtension === 'xls') {
9031
+ result = await importExcelMultipleSheets(file, options);
9032
+ }
9033
+ else {
9034
+ throw new Error('不支持的文件类型,仅支持 Excel (.xlsx, .xls) 和 CSV (.csv, .txt)');
9035
+ }
9036
+ if (options.autoTable !== false && this.table && this._detectVTableSheet(this.table)) {
9037
+ const vtableSheet = this.table.__vtableSheet;
9038
+ if (vtableSheet) {
9039
+ applyImportToVTableSheet(vtableSheet, result, options);
9040
+ }
9041
+ }
9042
+ return result;
9043
+ }
9044
+ catch (error) {
9045
+ throw error;
9046
+ }
9047
+ }
9048
+ _detectVTableSheet(instance) {
9049
+ const inst = instance;
9050
+ return !!(inst &&
9051
+ typeof inst.updateOption === 'function' &&
9052
+ inst.options &&
9053
+ typeof inst.options === 'object' &&
9054
+ inst.options !== null &&
9055
+ Array.isArray(inst.options.sheets));
9056
+ }
9057
+ _getFileExtension(filename) {
9058
+ const parts = filename.split('.');
9059
+ return parts.length > 1 ? parts[parts.length - 1] : '';
9060
+ }
9061
+ release() {
9062
+ if (this.table) {
9063
+ delete this.table.importFile;
9064
+ this.table = null;
9065
+ }
9066
+ }
9067
+ async import(type, source, options) {
9068
+ if (!this.table) {
9069
+ throw new Error('表格实例不存在或已销毁,无法导入数据!');
9070
+ }
9071
+ const mergedOptions = { ...this.options, ...options };
9072
+ const isVTableSheet = this._detectVTableSheet(this.table);
9073
+ if (isVTableSheet && type === 'file') {
9074
+ return this._importMultipleSheetsFromFileDialog({
9075
+ ...mergedOptions,
9076
+ importAllSheets: true
9077
+ });
9078
+ }
9079
+ if (type === 'file') {
9080
+ return this._importFromFileDialog(mergedOptions);
9081
+ }
9082
+ if (typeof source === 'string') {
9083
+ return this._importFromString(type, source, mergedOptions);
9084
+ }
9085
+ if (Array.isArray(source) || typeof source === 'object') {
9086
+ if (type !== 'json') {
9087
+ throw new Error('只有JSON格式支持从对象导入');
9088
+ }
9089
+ return this._importFromData('json', source, mergedOptions);
9090
+ }
9091
+ throw new Error('Invalid import source');
9092
+ }
9093
+ async _importFromFileDialog(options) {
9094
+ if (!this.table) {
9095
+ throw new Error('表格实例不存在或已销毁,无法导入数据!');
9096
+ }
9097
+ const file = await this._selectFile();
9098
+ const result = await this._parseFile(file, options);
9099
+ if (options.autoTable && this.table) {
9100
+ if (options.autoColumns) {
9101
+ this.table.updateOption(Object.assign({}, this.table.options, {
9102
+ columns: result.columns
9103
+ }));
9104
+ }
9105
+ this.table.setRecords(result.records);
9106
+ }
9107
+ return result;
9108
+ }
8714
9109
  async _importFromString(type, data, options) {
8715
9110
  let result;
8716
9111
  switch (type) {
@@ -8726,14 +9121,14 @@
8726
9121
  default:
8727
9122
  throw new Error(`不支持的导入类型: ${type}`);
8728
9123
  }
8729
- if (options.autoTable && this._tableInstance) {
9124
+ if (options.autoTable && this.table) {
8730
9125
  if (options.autoColumns) {
8731
- this._tableInstance.updateOption({
9126
+ this.table.updateOption({
8732
9127
  columns: result.columns,
8733
9128
  plugins: [this]
8734
9129
  });
8735
9130
  }
8736
- this._tableInstance.setRecords(result.records);
9131
+ this.table.setRecords(result.records);
8737
9132
  }
8738
9133
  return result;
8739
9134
  }
@@ -8745,14 +9140,14 @@
8745
9140
  else {
8746
9141
  throw new Error(`不支持的数据类型: ${type}`);
8747
9142
  }
8748
- if (options.autoTable && this._tableInstance) {
9143
+ if (options.autoTable && this.table) {
8749
9144
  if (options.autoColumns) {
8750
- this._tableInstance.updateOption({
9145
+ this.table.updateOption({
8751
9146
  columns: result.columns,
8752
9147
  plugins: [this]
8753
9148
  });
8754
9149
  }
8755
- this._tableInstance.setRecords(result.records);
9150
+ this.table.setRecords(result.records);
8756
9151
  }
8757
9152
  return result;
8758
9153
  }
@@ -8774,10 +9169,6 @@
8774
9169
  throw new Error(`不支持的文件类型: ${fileExtension}`);
8775
9170
  }
8776
9171
  }
8777
- _getFileExtension(filename) {
8778
- const parts = filename.split('.');
8779
- return parts.length > 1 ? parts[parts.length - 1] : '';
8780
- }
8781
9172
  async _parseExcelFile(file, options) {
8782
9173
  const mergedOptions = { ...this.options, ...options };
8783
9174
  return await ExcelImportPlugin.importExcelToVTableData(file, mergedOptions);
@@ -8934,16 +9325,40 @@
8934
9325
  return columns;
8935
9326
  }
8936
9327
  async _parseCSVString(text, options) {
8937
- const lines = text.split('\n').filter(line => line.trim());
9328
+ const delimiter = options.delimiter || ',';
9329
+ const lines = text.split(/\r?\n/).filter(line => line.trim().length > 0);
8938
9330
  if (lines.length === 0) {
8939
9331
  throw new Error('CSV文件为空');
8940
9332
  }
8941
- const delimiter = options.delimiter || ',';
8942
9333
  const parseCSVLine = (line) => {
8943
- return line.split(delimiter).map(cell => cell.trim().replace(/^"|"$/g, ''));
9334
+ const result = [];
9335
+ let current = '';
9336
+ let inQuotes = false;
9337
+ for (let i = 0; i < line.length; i++) {
9338
+ const char = line[i];
9339
+ const nextChar = line[i + 1];
9340
+ if (char === '"') {
9341
+ if (inQuotes && nextChar === '"') {
9342
+ current += '"';
9343
+ i++;
9344
+ }
9345
+ else {
9346
+ inQuotes = !inQuotes;
9347
+ }
9348
+ }
9349
+ else if (char === delimiter && !inQuotes) {
9350
+ result.push(current.trim());
9351
+ current = '';
9352
+ }
9353
+ else {
9354
+ current += char;
9355
+ }
9356
+ }
9357
+ result.push(current.trim());
9358
+ return result;
8944
9359
  };
8945
9360
  const headerRowCount = 1;
8946
- const headerRows = lines.slice(0, headerRowCount).map(parseCSVLine);
9361
+ const headerRows = lines.slice(0, headerRowCount).map(line => parseCSVLine(line).map(cell => String(cell || '')));
8947
9362
  const dataRows = lines.slice(headerRowCount);
8948
9363
  const columns = this._buildColumnsFromHeaders(headerRows);
8949
9364
  const records = [];
@@ -8952,7 +9367,7 @@
8952
9367
  const parsedRow = parseCSVLine(line);
8953
9368
  const record = {};
8954
9369
  parsedRow.forEach((cell, i) => {
8955
- record[`col${i}`] = cell || '';
9370
+ record[`col${i}`] = cell ?? '';
8956
9371
  });
8957
9372
  return record;
8958
9373
  });
@@ -9098,17 +9513,17 @@
9098
9513
  };
9099
9514
  const columnsStr = columns.map(col => formatColumn(col)).join(',\n');
9100
9515
  const recordsStr = records.map(formatRecord).join(',\n');
9101
- return `// VTable 数据导出 - JavaScript 对象字面量格式
9102
- // 生成时间: ${new Date().toLocaleString()}
9103
-
9104
- const vtableData = {
9105
- columns: [
9106
- ${columnsStr}
9107
- ],
9108
- records: [
9109
- ${recordsStr}
9110
- ]
9111
- };
9516
+ return `// VTable 数据导出 - JavaScript 对象字面量格式
9517
+ // 生成时间: ${new Date().toLocaleString()}
9518
+
9519
+ const vtableData = {
9520
+ columns: [
9521
+ ${columnsStr}
9522
+ ],
9523
+ records: [
9524
+ ${recordsStr}
9525
+ ]
9526
+ };
9112
9527
  `;
9113
9528
  }
9114
9529
  _exportToJS(columns, records) {
@@ -9554,6 +9969,10 @@ ${recordsStr}
9554
9969
  }
9555
9970
  }
9556
9971
  syncColWidthToComponent() {
9972
+ const colRange = this.table.getBodyVisibleColRange();
9973
+ if (!colRange) {
9974
+ return;
9975
+ }
9557
9976
  const { colStart, colEnd } = this.table.getBodyVisibleColRange();
9558
9977
  const adjustStartColIndex = colStart;
9559
9978
  const adjustEndColIndex = Math.min(colEnd, this.table.scenegraph.proxy.colEnd);
@@ -10471,9 +10890,7 @@ ${recordsStr}
10471
10890
  toggleSelectAll(fieldId, selected) {
10472
10891
  const options = this.valueFilterOptionList.get(fieldId);
10473
10892
  options.forEach(option => {
10474
- if (option.itemContainer.style.display !== 'none') {
10475
- option.checkbox.checked = selected;
10476
- }
10893
+ option.checkbox.checked = selected;
10477
10894
  });
10478
10895
  }
10479
10896
  syncSelectAllCheckbox(fieldId) {
@@ -11155,6 +11572,7 @@ ${recordsStr}
11155
11572
  clearFilterOptionLink;
11156
11573
  cancelFilterButton;
11157
11574
  applyFilterButton;
11575
+ activeType = 'byValue';
11158
11576
  constructor(table, filterStateManager, pluginOptions) {
11159
11577
  this.table = table;
11160
11578
  this.filterStateManager = filterStateManager;
@@ -11265,12 +11683,20 @@ ${recordsStr}
11265
11683
  applyStyles(this.applyFilterButton, styles.footerButton(true));
11266
11684
  this.valueFilter.updateStyles(styles);
11267
11685
  this.conditionFilter.updateStyles(styles);
11686
+ if (this.activeType === 'byCondition') {
11687
+ this.onTabSwitch('byCondition');
11688
+ }
11689
+ else {
11690
+ this.onTabSwitch('byValue');
11691
+ }
11268
11692
  }
11269
11693
  attachEventListeners() {
11270
11694
  this.filterTabByValue.addEventListener('click', () => {
11695
+ this.activeType = 'byValue';
11271
11696
  this.onTabSwitch('byValue');
11272
11697
  });
11273
11698
  this.filterTabByCondition.addEventListener('click', () => {
11699
+ this.activeType = 'byCondition';
11274
11700
  this.onTabSwitch('byCondition');
11275
11701
  });
11276
11702
  this.cancelFilterButton.addEventListener('click', () => this.hide());
@@ -21506,20 +21932,38 @@ ${recordsStr}
21506
21932
  async function exportVTableToExcel(tableInstance, options, optimization = false) {
21507
21933
  const workbook = new ExcelJS.Workbook();
21508
21934
  const worksheet = workbook.addWorksheet('sheet1');
21935
+ await renderTableToWorksheet(tableInstance, worksheet, workbook, options, optimization);
21936
+ const buffer = await workbook.xlsx.writeBuffer();
21937
+ return buffer;
21938
+ }
21939
+ async function exportMultipleVTablesToExcel(tables, options, optimization = false) {
21940
+ const workbook = new ExcelJS.Workbook();
21941
+ const usedWorksheetNames = new Set();
21942
+ for (let i = 0; i < tables.length; i++) {
21943
+ const { table, name } = tables[i];
21944
+ const safeName = getUniqueWorksheetName(name || `sheet${i + 1}`, usedWorksheetNames);
21945
+ usedWorksheetNames.add(safeName);
21946
+ const worksheet = workbook.addWorksheet(safeName);
21947
+ await renderTableToWorksheet(table, worksheet, workbook, options, optimization);
21948
+ }
21949
+ const buffer = await workbook.xlsx.writeBuffer();
21950
+ return buffer;
21951
+ }
21952
+ async function renderTableToWorksheet(tableInstance, worksheet, workbook, options, optimization = false) {
21509
21953
  const exportAllData = !!options?.exportAllData;
21510
21954
  const { handleRowCount, reset } = handlePaginationExport(tableInstance, exportAllData);
21511
- const minRow = 0;
21512
- const maxRow = handleRowCount();
21513
- const minCol = 0;
21514
- const maxCol = tableInstance.colCount - 1;
21515
- worksheet.properties.defaultRowHeight = 40;
21516
- const columns = [];
21517
- const mergeCells = [];
21518
- const mergeCellSet = new Set();
21519
- const SLICE_SIZE = 100;
21520
- let currentRow = minRow;
21521
- function processSlice(deadline) {
21522
- return new Promise(async (resolve) => {
21955
+ try {
21956
+ const minRow = 0;
21957
+ const maxRow = handleRowCount();
21958
+ const minCol = 0;
21959
+ const maxCol = tableInstance.colCount - 1;
21960
+ worksheet.properties.defaultRowHeight = 40;
21961
+ const columns = [];
21962
+ const mergeCells = [];
21963
+ const mergeCellSet = new Set();
21964
+ const SLICE_SIZE = 100;
21965
+ let currentRow = minRow;
21966
+ const processSlice = async (deadline) => {
21523
21967
  while (currentRow <= maxRow && (!optimization || deadline?.timeRemaining() > 0)) {
21524
21968
  const endRow = Math.min(currentRow + SLICE_SIZE - 1, maxRow);
21525
21969
  for (let col = minCol; col <= maxCol; col++) {
@@ -21546,53 +21990,70 @@ ${recordsStr}
21546
21990
  }
21547
21991
  currentRow = endRow + 1;
21548
21992
  }
21549
- if (currentRow > maxRow) {
21550
- resolve();
21551
- }
21552
- else {
21553
- let nextDeadline;
21554
- if (optimization) {
21555
- nextDeadline = await requestIdleCallbackPromise();
21556
- }
21993
+ if (currentRow <= maxRow) {
21994
+ const nextDeadline = optimization ? await requestIdleCallbackPromise() : undefined;
21557
21995
  await processSlice(nextDeadline);
21558
- resolve();
21559
21996
  }
21560
- });
21561
- }
21562
- await new Promise(async (resolve) => {
21563
- let deadline;
21997
+ };
21564
21998
  if (optimization) {
21565
- deadline = await requestIdleCallbackPromise();
21999
+ const deadline = await requestIdleCallbackPromise();
22000
+ await processSlice(deadline);
21566
22001
  }
21567
- await processSlice(deadline);
21568
- resolve();
21569
- });
21570
- worksheet.columns = columns;
21571
- mergeCells.forEach(mergeCell => {
21572
- worksheet.mergeCells(mergeCell.start.row + 1, mergeCell.start.col + 1, mergeCell.end.row + 1, mergeCell.end.col + 1);
21573
- });
21574
- const frozenView = [];
21575
- if (tableInstance.frozenRowCount > 0) {
21576
- frozenView.push({
21577
- state: 'frozen',
21578
- ySplit: tableInstance.frozenRowCount,
21579
- topLeftCell: encodeCellAddress(0, tableInstance.frozenRowCount)
22002
+ else {
22003
+ await processSlice();
22004
+ }
22005
+ worksheet.columns = columns;
22006
+ mergeCells.forEach(mergeCell => {
22007
+ worksheet.mergeCells(mergeCell.start.row + 1, mergeCell.start.col + 1, mergeCell.end.row + 1, mergeCell.end.col + 1);
21580
22008
  });
22009
+ const frozenView = [];
22010
+ if (tableInstance.frozenRowCount > 0) {
22011
+ frozenView.push({
22012
+ state: 'frozen',
22013
+ ySplit: tableInstance.frozenRowCount,
22014
+ topLeftCell: encodeCellAddress(0, tableInstance.frozenRowCount)
22015
+ });
22016
+ }
22017
+ if (tableInstance.frozenColCount > 0) {
22018
+ frozenView.push({
22019
+ state: 'frozen',
22020
+ xSplit: tableInstance.frozenColCount,
22021
+ topLeftCell: encodeCellAddress(tableInstance.frozenColCount, 0)
22022
+ });
22023
+ }
22024
+ worksheet.views = frozenView;
22025
+ if (options?.excelJSWorksheetCallback) {
22026
+ options.excelJSWorksheetCallback(worksheet);
22027
+ }
21581
22028
  }
21582
- if (tableInstance.frozenColCount > 0) {
21583
- frozenView.push({
21584
- state: 'frozen',
21585
- xSplit: tableInstance.frozenColCount,
21586
- topLeftCell: encodeCellAddress(tableInstance.frozenColCount, 0)
21587
- });
22029
+ finally {
22030
+ reset();
21588
22031
  }
21589
- worksheet.views = frozenView;
21590
- if (options?.excelJSWorksheetCallback) {
21591
- options.excelJSWorksheetCallback(worksheet);
22032
+ }
22033
+ function getUniqueWorksheetName(rawName, used) {
22034
+ const cleanedBase = sanitizeWorksheetName(rawName) || 'sheet';
22035
+ if (!used.has(cleanedBase)) {
22036
+ return cleanedBase;
21592
22037
  }
21593
- const buffer = await workbook.xlsx.writeBuffer();
21594
- reset();
21595
- return buffer;
22038
+ for (let n = 2; n < 10000; n++) {
22039
+ const suffix = `-${n}`;
22040
+ const baseMax = 31 - suffix.length;
22041
+ const base = cleanedBase.length > baseMax ? cleanedBase.slice(0, baseMax) : cleanedBase;
22042
+ const candidate = `${base}${suffix}`;
22043
+ if (!used.has(candidate)) {
22044
+ return candidate;
22045
+ }
22046
+ }
22047
+ return `${cleanedBase.slice(0, 31 - 6)}-${Date.now().toString().slice(-5)}`;
22048
+ }
22049
+ function sanitizeWorksheetName(name) {
22050
+ const trimmed = (name ?? '').toString().trim();
22051
+ const noInvalidChars = trimmed
22052
+ .replace(/[:\\\/\?\*\[\]]/g, ' ')
22053
+ .replace(/\s+/g, ' ')
22054
+ .trim();
22055
+ const nonEmpty = noInvalidChars || 'sheet';
22056
+ return nonEmpty.length > 31 ? nonEmpty.slice(0, 31) : nonEmpty;
21596
22057
  }
21597
22058
  async function addCell(col, row, tableInstance, worksheet, workbook, options) {
21598
22059
  const { layoutMap } = tableInstance.internalProps;
@@ -21974,6 +22435,14 @@ ${recordsStr}
21974
22435
  }
21975
22436
  return exportVTableToExcel(this.table, options, this.pluginOptions.exportOnIdle);
21976
22437
  };
22438
+ if (this.table.__vtableSheet) {
22439
+ if (!this.table.__vtableSheet._exportMutipleTablesToExcel) {
22440
+ this.table.__vtableSheet._exportMutipleTablesToExcel = async (tables) => {
22441
+ const buffer = (await exportMultipleVTablesToExcel(tables, this.pluginOptions.exportExcelOptions));
22442
+ await downloadExcel(buffer, this.pluginOptions.exportExcelOptions.fileName || 'vtable-sheet-export');
22443
+ };
22444
+ }
22445
+ }
21977
22446
  }
21978
22447
  }
21979
22448
  release() {