feat(programs): Excel 导入改为全量覆盖模式
已存在 code 的程序不再跳过,而是: - 用 Excel 中的字段更新 program(保留 id) - 删除该 program 的全部旧步骤 - 按 Excel 中的步骤重新写入 返回值变量名 importedCount -> processedCount 更准确。 Toast 文案同步:成功处理 / Excel 无有效数据。
This commit is contained in:
@@ -441,9 +441,9 @@ class _ProgramsPageState extends ConsumerState<ProgramsPage> {
|
||||
|
||||
if (!context.mounted) return;
|
||||
if (importedCount > 0) {
|
||||
ToastService.showSuccess(context, '成功导入 $importedCount 个程序');
|
||||
ToastService.showSuccess(context, '成功处理 $importedCount 个程序');
|
||||
} else {
|
||||
ToastService.showWarning(context, '未导入新程序(编号可能已存在)');
|
||||
ToastService.showWarning(context, 'Excel 中无有效程序数据');
|
||||
}
|
||||
} catch (e) {
|
||||
if (!context.mounted) return;
|
||||
|
||||
@@ -18,7 +18,11 @@ class ExcelImportService {
|
||||
|
||||
ExcelImportService._internal();
|
||||
|
||||
/// 从 .xlsx 文件导入程序,返回成功导入的程序数量
|
||||
/// 从 .xlsx 文件导入程序,返回成功处理的程序数量(新建 + 覆盖)
|
||||
///
|
||||
/// 行为:
|
||||
/// - code 不存在:新建程序 + 写入步骤
|
||||
/// - code 已存在:全量覆盖程序字段,并删除旧步骤后写入新步骤
|
||||
Future<int> importFromExcel(File file) async {
|
||||
final bytes = await file.readAsBytes();
|
||||
final excel = Excel.decodeBytes(bytes);
|
||||
@@ -33,9 +37,11 @@ class ExcelImportService {
|
||||
throw const ExcelImportException('Programs 表无有效数据');
|
||||
}
|
||||
|
||||
// 读取已有 code,避免重复
|
||||
// 已有 code → 完整 Program(用于覆盖时取 id)
|
||||
final existing = await _programService.getAllPrograms();
|
||||
final existingCodes = existing.map((p) => p.code).toSet();
|
||||
final existingByCode = <String, Program>{
|
||||
for (final p in existing) p.code: p,
|
||||
};
|
||||
|
||||
// 解析步骤
|
||||
final stepsSheet = _findSheet(excel, ExcelTemplateService.sheetSteps);
|
||||
@@ -43,17 +49,37 @@ class ExcelImportService {
|
||||
? <String, List<_RawStep>>{}
|
||||
: _parseSteps(stepsSheet);
|
||||
|
||||
int importedCount = 0;
|
||||
int processedCount = 0;
|
||||
for (final program in programs) {
|
||||
try {
|
||||
if (existingCodes.contains(program.code)) {
|
||||
continue;
|
||||
final existingProgram = existingByCode[program.code];
|
||||
final int programId;
|
||||
if (existingProgram == null) {
|
||||
programId = await _programService.addProgram(program);
|
||||
} else {
|
||||
// 全量覆盖:保留 id,其余字段取 Excel
|
||||
programId = existingProgram.id!;
|
||||
await _programService.updateProgram(
|
||||
existingProgram.copyWith(
|
||||
name: program.name,
|
||||
temperature: program.temperature,
|
||||
airflowTime: program.airflowTime,
|
||||
status: program.status,
|
||||
createdAt: program.createdAt,
|
||||
),
|
||||
);
|
||||
// 清空旧步骤
|
||||
final oldSteps =
|
||||
await _programService.getStepsByProgramId(programId);
|
||||
if (oldSteps.isNotEmpty) {
|
||||
await _programService.deleteSteps(
|
||||
oldSteps.map((s) => s.id!).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final programId = await _programService.addProgram(program);
|
||||
|
||||
// 写入新步骤(按 step_no 排序后重新编号为 1..N)
|
||||
final rawSteps = stepsByCode[program.code] ?? const <_RawStep>[];
|
||||
// 按 step_no 排序后写入
|
||||
rawSteps.sort((a, b) => a.stepNo.compareTo(b.stepNo));
|
||||
for (var i = 0; i < rawSteps.length; i++) {
|
||||
final raw = rawSteps[i];
|
||||
@@ -71,14 +97,14 @@ class ExcelImportService {
|
||||
await _programService.addStep(step);
|
||||
}
|
||||
|
||||
importedCount++;
|
||||
processedCount++;
|
||||
} catch (_) {
|
||||
// 单条失败不影响其他程序
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return importedCount;
|
||||
return processedCount;
|
||||
}
|
||||
|
||||
Sheet? _findSheet(Excel excel, String name) {
|
||||
|
||||
Reference in New Issue
Block a user