|
|
@@ -0,0 +1,639 @@
|
|
|
+import GC from '@/components/SpreadDesigner/tools/gc'
|
|
|
+
|
|
|
+export interface FormConfig {
|
|
|
+ startRow: number;
|
|
|
+ templateRow: number;
|
|
|
+ dataArray: any[];
|
|
|
+ rowCount: number;
|
|
|
+ endRow?: number;
|
|
|
+ tableName?: string;
|
|
|
+ tableInstance?: any;
|
|
|
+ bindingPath?: string;
|
|
|
+ sheet?: any; // Sheet 引用
|
|
|
+}
|
|
|
+
|
|
|
+export interface FormInfo {
|
|
|
+ formName: string;
|
|
|
+ formConfig: FormConfig;
|
|
|
+ index: number;
|
|
|
+ currentRow: number;
|
|
|
+}
|
|
|
+
|
|
|
+export default class DynamicFormManager {
|
|
|
+ private spread: any;
|
|
|
+ private sheet: any;
|
|
|
+ private formConfigs: Map<string, FormConfig>;
|
|
|
+ private autoScanEnabled: boolean = true;
|
|
|
+
|
|
|
+ constructor(spread: any) {
|
|
|
+ this.spread = spread;
|
|
|
+ this.sheet = spread.getActiveSheet();
|
|
|
+ this.formConfigs = new Map();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新 Table 的数据源
|
|
|
+ */
|
|
|
+ private refreshTableDataSource(formConfig: FormConfig): void {
|
|
|
+ if (!formConfig.tableInstance || !formConfig.bindingPath || !formConfig.sheet) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const sheet = formConfig.sheet;
|
|
|
+ const table = formConfig.tableInstance;
|
|
|
+ const currentDataSource = sheet.getDataSource();
|
|
|
+
|
|
|
+ if (currentDataSource) {
|
|
|
+ const sourceData = currentDataSource.getSource();
|
|
|
+
|
|
|
+ // 更新绑定路径对应的数据数组
|
|
|
+ sourceData[formConfig.bindingPath] = [...formConfig.dataArray];
|
|
|
+
|
|
|
+ // 强制刷新步骤
|
|
|
+ this.spread.suspendPaint();
|
|
|
+
|
|
|
+ // 清空并重新设置数据源
|
|
|
+ sheet.setDataSource(null);
|
|
|
+ sheet.setDataSource(currentDataSource);
|
|
|
+
|
|
|
+ // 更新 Table 的 range 以匹配新的行数
|
|
|
+ const range = table.range();
|
|
|
+ const newRange = new GC.Spread.Sheets.Range(
|
|
|
+ range.row,
|
|
|
+ range.col,
|
|
|
+ formConfig.dataArray.length + 1, // +1 包含表头
|
|
|
+ range.colCount
|
|
|
+ );
|
|
|
+ table.range(newRange);
|
|
|
+
|
|
|
+ this.spread.resumePaint();
|
|
|
+
|
|
|
+ // console.log(`✅ 已刷新 Table ${formConfig.tableName}, 新行数: ${formConfig.dataArray.length}`);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('刷新 Table 数据源失败:', error);
|
|
|
+ this.spread.resumePaint();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建新数据对象(空数据)
|
|
|
+ */
|
|
|
+ private createNewDataObject(referenceData: any, defaultData: any): any {
|
|
|
+ const newData: any = {};
|
|
|
+
|
|
|
+ if (referenceData && Object.keys(referenceData).length > 0) {
|
|
|
+ for (const key in referenceData) {
|
|
|
+ if (defaultData.hasOwnProperty(key)) {
|
|
|
+ newData[key] = defaultData[key];
|
|
|
+ } else {
|
|
|
+ const value = referenceData[key];
|
|
|
+ if (typeof value === 'number') {
|
|
|
+ newData[key] = 0;
|
|
|
+ } else if (typeof value === 'string') {
|
|
|
+ newData[key] = '';
|
|
|
+ } else if (Array.isArray(value)) {
|
|
|
+ newData[key] = [];
|
|
|
+ } else if (value === null) {
|
|
|
+ newData[key] = null;
|
|
|
+ } else if (typeof value === 'object') {
|
|
|
+ newData[key] = {};
|
|
|
+ } else {
|
|
|
+ newData[key] = '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return { ...defaultData };
|
|
|
+ }
|
|
|
+
|
|
|
+ return newData;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 复制行格式和公式
|
|
|
+ */
|
|
|
+ private copyRowFormat(sourceRow: number, targetRow: number): void {
|
|
|
+ const colCount = this.sheet.getColumnCount();
|
|
|
+
|
|
|
+ for (let col = 0; col < colCount; col++) {
|
|
|
+ const style = this.sheet.getStyle(sourceRow, col);
|
|
|
+ if (style) {
|
|
|
+ this.sheet.setStyle(targetRow, col, style);
|
|
|
+ }
|
|
|
+
|
|
|
+ const formula = this.sheet.getFormula(sourceRow, col);
|
|
|
+ if (formula) {
|
|
|
+ this.sheet.setFormula(targetRow, col, formula);
|
|
|
+ }
|
|
|
+
|
|
|
+ const validation = this.sheet.getDataValidator(sourceRow, col);
|
|
|
+ if (validation) {
|
|
|
+ this.sheet.setDataValidator(targetRow, col, validation);
|
|
|
+ }
|
|
|
+
|
|
|
+ const cellType = this.sheet.getCellType(sourceRow, col);
|
|
|
+ if (cellType) {
|
|
|
+ this.sheet.setCellType(targetRow, col, cellType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新后续表单的起始行
|
|
|
+ */
|
|
|
+ private updateSubsequentForms(currentFormName: string, offset: number): void {
|
|
|
+ let foundCurrent = false;
|
|
|
+
|
|
|
+ for (const [formName, config] of this.formConfigs) {
|
|
|
+ if (foundCurrent && !config.tableInstance) {
|
|
|
+ config.startRow += offset;
|
|
|
+ config.endRow = config.startRow + config.rowCount - 1;
|
|
|
+ // console.log(` 更新表单 ${formName}: 新起始行=${config.startRow}`);
|
|
|
+ }
|
|
|
+ if (formName === currentFormName) {
|
|
|
+ foundCurrent = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 刷新表单数据到 SpreadJS
|
|
|
+ */
|
|
|
+ refreshFormData(formName: string, columnMapping?: Map<number, string>): void {
|
|
|
+ const config = this.formConfigs.get(formName);
|
|
|
+ if (!config) {
|
|
|
+ console.warn(`表单 ${formName} 未注册`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ config.dataArray.forEach((data, index) => {
|
|
|
+ const row = config.startRow + index;
|
|
|
+
|
|
|
+ if (columnMapping) {
|
|
|
+ columnMapping.forEach((fieldName, col) => {
|
|
|
+ if (data.hasOwnProperty(fieldName)) {
|
|
|
+ this.sheet.setValue(row, col, data[fieldName]);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ this.spread.refresh();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取所有已注册的表单信息
|
|
|
+ */
|
|
|
+ getRegisteredForms(): string[] {
|
|
|
+ return Array.from(this.formConfigs.keys());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启用/禁用自动扫描
|
|
|
+ */
|
|
|
+ setAutoScanEnabled(enabled: boolean): void {
|
|
|
+ this.autoScanEnabled = enabled;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清除所有表单注册
|
|
|
+ */
|
|
|
+ clearAllForms(): void {
|
|
|
+ this.formConfigs.clear();
|
|
|
+ // console.log('✅ 已清除所有表单注册');
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 自动扫描所有 Sheet 中的 Table 并注册为动态表单
|
|
|
+ */
|
|
|
+ autoRegisterTablesFromAllSheets(): void {
|
|
|
+ const sheets = this.spread.sheets;
|
|
|
+ for (const sheet of sheets) {
|
|
|
+ this.autoRegisterTablesFromSheet(sheet);
|
|
|
+ }
|
|
|
+ // console.log(`✅ 自动扫描完成,共注册 ${this.formConfigs.size} 个动态表单`);
|
|
|
+ }
|
|
|
+
|
|
|
+/**
|
|
|
+ * 从指定 Sheet 自动注册所有 Table
|
|
|
+ * 修复:正确处理 Table 的行索引映射
|
|
|
+ */
|
|
|
+autoRegisterTablesFromSheet(sheet: any): void {
|
|
|
+ const tables = sheet.tables?.all() || [];
|
|
|
+ // console.log(`🔍 开始扫描 Sheet: ${sheet.name()},发现 ${tables.length} 个表格`);
|
|
|
+
|
|
|
+ for (let i = 0; i < tables.length; i++) {
|
|
|
+ const table = tables[i];
|
|
|
+ const tableName = table.name();
|
|
|
+ const range = table.range();
|
|
|
+ const bindingPath = table.bindingPath();
|
|
|
+
|
|
|
+ // console.log(`📊 处理表格 ${i}: ${tableName}, bindingPath: ${bindingPath}`);
|
|
|
+ // console.log(` range.row: ${range.row}, range.rowCount: ${range.rowCount}`);
|
|
|
+
|
|
|
+ if (!bindingPath) {
|
|
|
+ console.warn(`⚠️ Table ${tableName} 没有绑定路径,跳过注册`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const dataSource = sheet.getDataSource();
|
|
|
+ if (!dataSource) {
|
|
|
+ console.warn(`⚠️ Table ${tableName} 的 Sheet 没有数据源,跳过注册`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const dataArray = this.extractTableDataArray(bindingPath, dataSource);
|
|
|
+ // console.log(`📦 Table ${tableName} 数据:`, dataArray);
|
|
|
+
|
|
|
+ // 🔧 修复:range.row 就是表格的起始行(包含表头)
|
|
|
+ // 如果 range.row = 6,表头在第7行(6+1),数据从索引6开始就是第一行数据
|
|
|
+ // SpreadJS 的 Table range 通常包含表头,所以:
|
|
|
+ // - 表头行 = range.row(如果Table有表头的话)
|
|
|
+ // - 数据起始行 = range.row(实际数据行的索引)
|
|
|
+
|
|
|
+ const startRow = range.row; // 数据起始行就是 range.row
|
|
|
+ const templateRow = startRow; // 模板行就是第一行数据
|
|
|
+ const rowCount = dataArray && dataArray.length > 0 ? dataArray.length : 0;
|
|
|
+
|
|
|
+ // 使用 Sheet 名称 + Table 名称作为唯一标识
|
|
|
+ const uniqueFormName = `${sheet.name()}_${tableName}`;
|
|
|
+
|
|
|
+ this.formConfigs.set(uniqueFormName, {
|
|
|
+ startRow, // 数据起始行
|
|
|
+ templateRow, // 模板行(第一行数据)
|
|
|
+ dataArray: dataArray || [],
|
|
|
+ rowCount: rowCount,
|
|
|
+ endRow: startRow + rowCount - 1, // 数据结束行
|
|
|
+ tableName,
|
|
|
+ tableInstance: table,
|
|
|
+ bindingPath,
|
|
|
+ sheet: sheet
|
|
|
+ });
|
|
|
+
|
|
|
+ // console.log(`✅ 自动注册 Table: ${uniqueFormName}`);
|
|
|
+ // console.log(` 绑定路径: ${bindingPath}`);
|
|
|
+ // console.log(` 数据起始行: ${startRow} (显示为第 ${startRow + 1} 行)`);
|
|
|
+ // console.log(` 数据结束行: ${startRow + rowCount - 1} (显示为第 ${startRow + rowCount} 行)`);
|
|
|
+ // console.log(` 数据行数: ${rowCount}`);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 提取 Table 绑定的数据数组
|
|
|
+ */
|
|
|
+ private extractTableDataArray(bindingPath: string, dataSource: any): any[] {
|
|
|
+ try {
|
|
|
+ if (!bindingPath || !dataSource) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ const sourceData = dataSource.getSource();
|
|
|
+
|
|
|
+ if (!sourceData) {
|
|
|
+ console.error('数据源的 getSource() 返回空');
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ const dataArray = sourceData[bindingPath];
|
|
|
+
|
|
|
+ // console.log(`�� 从绑定路径 "${bindingPath}" 获取数据:`, dataArray);
|
|
|
+
|
|
|
+ if (Array.isArray(dataArray)) {
|
|
|
+ return dataArray;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.warn(`⚠️ 绑定路径 "${bindingPath}" 的数据不是数组类型:`, typeof dataArray);
|
|
|
+ return [];
|
|
|
+ } catch (error) {
|
|
|
+ console.error('提取 Table 数据数组失败:', error);
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 监听数据源变化,自动更新表单注册
|
|
|
+ */
|
|
|
+ setupAutoUpdateOnDataSourceChange(sheet: any): void {
|
|
|
+ const originalSetDataSource = sheet.setDataSource.bind(sheet);
|
|
|
+ sheet.setDataSource = (dataSource: any) => {
|
|
|
+ originalSetDataSource(dataSource);
|
|
|
+
|
|
|
+ if (this.autoScanEnabled) {
|
|
|
+ this.clearFormsInSheet(sheet);
|
|
|
+ this.autoRegisterTablesFromSheet(sheet);
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 清除指定 Sheet 中的表单注册
|
|
|
+ */
|
|
|
+ private clearFormsInSheet(sheet: any): void {
|
|
|
+ const tables = sheet.tables?.all() || [];
|
|
|
+ const sheetName = sheet.name();
|
|
|
+ for (const table of tables) {
|
|
|
+ const uniqueFormName = `${sheetName}_${table.name()}`;
|
|
|
+ this.formConfigs.delete(uniqueFormName);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手动注册动态表单(保留原有方法以兼容手动注册场景)
|
|
|
+ */
|
|
|
+ registerForm(formName: string, startRow: number, templateRow: number, dataArray: any[]): void {
|
|
|
+ if (dataArray.length === 0) {
|
|
|
+ console.warn(`表单 ${formName} 的数据数组为空,至少需要一条数据`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.formConfigs.set(formName, {
|
|
|
+ startRow,
|
|
|
+ templateRow,
|
|
|
+ dataArray,
|
|
|
+ rowCount: dataArray.length,
|
|
|
+ endRow: startRow + dataArray.length - 1
|
|
|
+ });
|
|
|
+
|
|
|
+ // console.log(`✅ 已注册表单: ${formName}, 起始行索引: ${startRow} (显示为第 ${startRow + 1} 行), 行数: ${dataArray.length}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前激活单元格所属的动态表单
|
|
|
+ * 修复:增加调试信息,显示人类可读的行号
|
|
|
+ */
|
|
|
+ getActiveFormInfo(): FormInfo | null {
|
|
|
+ const activeSheet = this.spread.getActiveSheet();
|
|
|
+ const activeRow = activeSheet.getActiveRowIndex(); // 这是编程索引(从0开始)
|
|
|
+ const activeSheetName = activeSheet.name();
|
|
|
+
|
|
|
+ // console.log(`�� 当前激活 Sheet: ${activeSheetName}, 行索引: ${activeRow} (显示为第 ${activeRow + 1} 行)`);
|
|
|
+ // console.log('已注册表单:', this.formConfigs);
|
|
|
+
|
|
|
+ for (const [formName, config] of this.formConfigs) {
|
|
|
+ // 检查 Sheet 是否匹配
|
|
|
+ const configSheetName = config.sheet ? config.sheet.name() : null;
|
|
|
+
|
|
|
+ if (configSheetName !== activeSheetName) {
|
|
|
+ // console.log(` 跳过表单 ${formName}: Sheet 不匹配 (${configSheetName} !== ${activeSheetName})`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const endRow = config.startRow + config.rowCount - 1;
|
|
|
+
|
|
|
+ // console.log(` 检查表单 ${formName}: startRow=${config.startRow} (显示第 ${config.startRow + 1} 行), endRow=${endRow} (显示第 ${endRow + 1} 行)`);
|
|
|
+
|
|
|
+ if (activeRow >= config.startRow && activeRow <= endRow) {
|
|
|
+ const index = activeRow - config.startRow;
|
|
|
+ // console.log(`✅ 找到表单: ${formName}, 数据索引: ${index}`);
|
|
|
+
|
|
|
+ return {
|
|
|
+ formName,
|
|
|
+ formConfig: config,
|
|
|
+ index,
|
|
|
+ currentRow: activeRow
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.warn(`⚠️ 当前单元格(Sheet: ${activeSheetName}, 行索引: ${activeRow}, 显示第 ${activeRow + 1} 行)不在任何动态表单中`);
|
|
|
+ // console.log('已注册的表单:', Array.from(this.formConfigs.keys()));
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 在当前激活行下方添加新行
|
|
|
+ */
|
|
|
+ addRowBelowActive(defaultData: any = {}): boolean {
|
|
|
+ const formInfo = this.getActiveFormInfo();
|
|
|
+ if (!formInfo) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const { formName, formConfig, index, currentRow } = formInfo;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const newData = this.createNewDataObject(
|
|
|
+ formConfig.dataArray.length > 0 ? formConfig.dataArray[index] : {},
|
|
|
+ defaultData
|
|
|
+ );
|
|
|
+
|
|
|
+ formConfig.dataArray.splice(index + 1, 0, newData);
|
|
|
+
|
|
|
+ formConfig.rowCount++;
|
|
|
+ formConfig.endRow = formConfig.startRow + formConfig.rowCount - 1;
|
|
|
+
|
|
|
+ if (formConfig.tableInstance && formConfig.bindingPath) {
|
|
|
+ this.refreshTableDataSource(formConfig);
|
|
|
+ } else {
|
|
|
+ const insertRow = currentRow + 1;
|
|
|
+ this.sheet.addRows(insertRow, 1);
|
|
|
+ this.copyRowFormat(formConfig.templateRow, insertRow);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.updateSubsequentForms(formName, 1);
|
|
|
+
|
|
|
+ // console.log(`✅ 已在 ${formName} 的第 ${index + 1} 个位置添加新行`);
|
|
|
+
|
|
|
+ this.spread.refresh();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('添加行失败:', error);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 复制当前行到表单末尾
|
|
|
+ */
|
|
|
+/**
|
|
|
+ * 复制当前行到表单末尾
|
|
|
+ * @param defaultData 默认数据
|
|
|
+ * @param count 要生成的行数,默认为 1
|
|
|
+ */
|
|
|
+duplicateRowToEnd(defaultData: any = {}, count: number = 1): boolean {
|
|
|
+ const formInfo = this.getActiveFormInfo();
|
|
|
+ if (!formInfo) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ const { formName, formConfig, index } = formInfo;
|
|
|
+
|
|
|
+ // 验证 count 参数
|
|
|
+ if (count < 1) {
|
|
|
+ console.warn('⚠️ 行数必须大于 0');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 批量创建新行数据
|
|
|
+ const newRows = [];
|
|
|
+ for (let i = 0; i < count; i++) {
|
|
|
+ const copiedData = this.createNewDataObject(
|
|
|
+ formConfig.dataArray.length > 0 ? formConfig.dataArray[index] : {},
|
|
|
+ defaultData
|
|
|
+ );
|
|
|
+ newRows.push(copiedData);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加到数据数组
|
|
|
+ formConfig.dataArray.push(...newRows);
|
|
|
+
|
|
|
+ formConfig.rowCount += count;
|
|
|
+ formConfig.endRow = formConfig.startRow + formConfig.rowCount - 1;
|
|
|
+
|
|
|
+ if (formConfig.tableInstance && formConfig.bindingPath) {
|
|
|
+ this.refreshTableDataSource(formConfig);
|
|
|
+ } else {
|
|
|
+ const insertRow = formConfig.startRow + formConfig.rowCount - count;
|
|
|
+ this.sheet.addRows(insertRow, count);
|
|
|
+
|
|
|
+ // 批量复制格式
|
|
|
+ for (let i = 0; i < count; i++) {
|
|
|
+ this.copyRowFormat(formConfig.templateRow, insertRow + i);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.updateSubsequentForms(formName, count);
|
|
|
+
|
|
|
+ // console.log(`✅ 已将 ${formName} 的第 ${index} 行复制 ${count} 次到末尾`);
|
|
|
+
|
|
|
+ this.spread.refresh();
|
|
|
+
|
|
|
+ return true;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('复制行失败:', error);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 批量删除选中的行
|
|
|
+ * @returns 成功删除的行数
|
|
|
+ */
|
|
|
+deleteSelectedRows(): number {
|
|
|
+ const activeSheet = this.spread.getActiveSheet();
|
|
|
+ const selections = activeSheet.getSelections();
|
|
|
+
|
|
|
+ if (!selections || selections.length === 0) {
|
|
|
+ console.warn('⚠️ 没有选中任何单元格');
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 收集所有选中的行索引(去重并排序)
|
|
|
+ const selectedRows = new Set<number>();
|
|
|
+ selections.forEach(selection => {
|
|
|
+ const startRow = selection.row;
|
|
|
+ const rowCount = selection.rowCount;
|
|
|
+ for (let i = 0; i < rowCount; i++) {
|
|
|
+ selectedRows.add(startRow + i);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 按行索引从大到小排序(从后往前删除,避免索引变化)
|
|
|
+ const sortedRows = Array.from(selectedRows).sort((a, b) => b - a);
|
|
|
+
|
|
|
+ console.log(`�� 选中了 ${sortedRows.length} 行,准备删除:`, sortedRows.map(r => r + 1));
|
|
|
+
|
|
|
+ // 按表单分组
|
|
|
+ const rowsByForm = new Map<string, { config: FormConfig; rows: number[] }>();
|
|
|
+
|
|
|
+ sortedRows.forEach(rowIndex => {
|
|
|
+ const formInfo = this.getFormInfoByRow(rowIndex);
|
|
|
+ if (formInfo) {
|
|
|
+ if (!rowsByForm.has(formInfo.formName)) {
|
|
|
+ rowsByForm.set(formInfo.formName, {
|
|
|
+ config: formInfo.formConfig,
|
|
|
+ rows: []
|
|
|
+ });
|
|
|
+ }
|
|
|
+ rowsByForm.get(formInfo.formName)!.rows.push(rowIndex);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ let deletedCount = 0;
|
|
|
+
|
|
|
+ // 遍历每个表单进行批量删除
|
|
|
+ rowsByForm.forEach((formData, formName) => {
|
|
|
+ const { config, rows } = formData;
|
|
|
+
|
|
|
+ // 检查是否会删除所有行
|
|
|
+ if (rows.length >= config.rowCount) {
|
|
|
+ console.warn(`⚠️ 表单 ${formName} 至少需要保留一行,跳过删除`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 转换为数据索引(从后往前)
|
|
|
+ const dataIndices = rows
|
|
|
+ .map(rowIndex => rowIndex - config.startRow)
|
|
|
+ .sort((a, b) => b - a);
|
|
|
+
|
|
|
+ // 从数据数组中删除
|
|
|
+ dataIndices.forEach(index => {
|
|
|
+ config.dataArray.splice(index, 1);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新配置
|
|
|
+ const deletedInThisForm = dataIndices.length;
|
|
|
+ config.rowCount -= deletedInThisForm;
|
|
|
+ config.endRow = config.startRow + config.rowCount - 1;
|
|
|
+
|
|
|
+ // 刷新表格
|
|
|
+ if (config.tableInstance && config.bindingPath) {
|
|
|
+ this.refreshTableDataSource(config);
|
|
|
+ } else {
|
|
|
+ // 非 Table 模式:从后往前逐行删除
|
|
|
+ rows.forEach(rowIndex => {
|
|
|
+ this.sheet.deleteRows(rowIndex, 1);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ this.updateSubsequentForms(formName, -deletedInThisForm);
|
|
|
+
|
|
|
+ deletedCount += deletedInThisForm;
|
|
|
+ console.log(`✅ 表单 ${formName} 删除了 ${deletedInThisForm} 行`);
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`删除表单 ${formName} 的行时出错:`, error);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (deletedCount > 0) {
|
|
|
+ this.spread.refresh();
|
|
|
+ console.log(`✅ 总共删除了 ${deletedCount} 行`);
|
|
|
+ }
|
|
|
+
|
|
|
+ return deletedCount;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 根据行索引获取表单信息(辅助方法)
|
|
|
+ */
|
|
|
+private getFormInfoByRow(rowIndex: number): FormInfo | null {
|
|
|
+ const activeSheet = this.spread.getActiveSheet();
|
|
|
+ const activeSheetName = activeSheet.name();
|
|
|
+
|
|
|
+ for (const [formName, config] of this.formConfigs) {
|
|
|
+ const configSheetName = config.sheet ? config.sheet.name() : null;
|
|
|
+
|
|
|
+ if (configSheetName !== activeSheetName) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ const endRow = config.startRow + config.rowCount - 1;
|
|
|
+
|
|
|
+ if (rowIndex >= config.startRow && rowIndex <= endRow) {
|
|
|
+ const index = rowIndex - config.startRow;
|
|
|
+ return {
|
|
|
+ formName,
|
|
|
+ formConfig: config,
|
|
|
+ index,
|
|
|
+ currentRow: rowIndex
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+}
|
|
|
+}
|