import 'dart:io'; import 'package:excel/excel.dart'; import 'package:path_provider/path_provider.dart'; /// Excel 模板服务 /// /// 生成 .xlsx 模板用于程序/步骤批量导入。 /// 模板包含两张表: /// - Programs:程序基础信息 /// - Steps:步骤参数(通过 program_code 与 Programs 关联) class ExcelTemplateService { static final ExcelTemplateService instance = ExcelTemplateService._internal(); ExcelTemplateService._internal(); /// 工作表名称 static const String sheetPrograms = 'Programs'; static const String sheetSteps = 'Steps'; /// 生成模板并保存到应用文档目录,返回文件对象 Future generateTemplate() async { final excel = Excel.createExcel(); final bytes = _buildTemplateBytes(excel); final dir = await getApplicationDocumentsDirectory(); final file = File('${dir.path}/program_template.xlsx'); await file.writeAsBytes(bytes, flush: true); return file; } /// 构建模板字节流(可在测试中直接调用) List _buildTemplateBytes(Excel excel) { // 默认创建的第一个 sheet 重命名 final defaultSheet = excel.getDefaultSheet(); if (defaultSheet != null && defaultSheet != sheetPrograms) { excel.rename(defaultSheet, sheetPrograms); } _writeProgramsSheet(excel); _writeStepsSheet(excel); return excel.encode()!; } void _writeProgramsSheet(Excel excel) { final sheet = excel[sheetPrograms]; final headerStyle = CellStyle( bold: true, backgroundColorHex: ExcelColor.fromHexString('FFD9E1F2'), horizontalAlign: HorizontalAlign.Center, ); // 表头 final headers = ['code', 'name', 'temperature', 'airflowTime', 'status']; sheet.appendRow(headers.map((h) => TextCellValue(h)).toList()); for (var i = 0; i < headers.length; i++) { sheet .cell(CellIndex.indexByColumnRow(columnIndex: i, rowIndex: 0)) .cellStyle = headerStyle; } // 示例行 final sample1 = ['P001', '示例程序-标准流程', 50, 60, 1]; final sample2 = ['P002', '示例程序-快速流程', 45, 30, 1]; sheet.appendRow(sample1.map((v) => TextCellValue(v.toString())).toList()); sheet.appendRow(sample2.map((v) => TextCellValue(v.toString())).toList()); // 说明行(用前缀标记,避免被解析) final note = '说明:code 必填且唯一;name 必填;temperature/airflowTime 整数;status:1启用 0停用'; sheet.appendRow([TextCellValue(note)]); // 列宽 sheet.setColumnWidth(0, 14); sheet.setColumnWidth(1, 26); sheet.setColumnWidth(2, 14); sheet.setColumnWidth(3, 14); sheet.setColumnWidth(4, 10); } void _writeStepsSheet(Excel excel) { final sheet = excel[sheetSteps]; final headerStyle = CellStyle( bold: true, backgroundColorHex: ExcelColor.fromHexString('FFD9E1F2'), horizontalAlign: HorizontalAlign.Center, ); final headers = [ 'program_code', 'step_no', 'position', 'name', 'mixTime', 'magnetTime', 'volume', 'blowTime', 'speed', ]; sheet.appendRow(headers.map((h) => TextCellValue(h)).toList()); for (var i = 0; i < headers.length; i++) { sheet .cell(CellIndex.indexByColumnRow(columnIndex: i, rowIndex: 0)) .cellStyle = headerStyle; } // 示例行:P001 / P002 各两步 final samples = >[ ['P001', 1, 'A1', '加样', 30, 0, 500, 0, 5], ['P001', 2, 'A2', '混合', 60, 10, 300, 30, 5], ['P002', 1, 'B1', '混合', 20, 5, 400, 0, 4], ]; for (final row in samples) { sheet.appendRow(row.map((v) { if (v is num) return IntCellValue(v.toInt()); return TextCellValue(v.toString()); }).toList()); } // 说明行 final note = '说明:program_code 对应 Programs.code;step_no 整数从 1 开始;position 形如 A1/B2;mixTime/magnetTime/volume/blowTime 单位秒或微升;speed 1-10'; sheet.appendRow([TextCellValue(note)]); // 列宽 sheet.setColumnWidth(0, 14); sheet.setColumnWidth(1, 10); sheet.setColumnWidth(2, 12); sheet.setColumnWidth(3, 14); sheet.setColumnWidth(4, 10); sheet.setColumnWidth(5, 12); sheet.setColumnWidth(6, 10); sheet.setColumnWidth(7, 10); sheet.setColumnWidth(8, 8); } }