diff --git a/win_text_editor/lib/modules/data_compare/controllers/data_compare_controller.dart b/win_text_editor/lib/modules/data_compare/controllers/data_compare_controller.dart index 177cb26..7012fe8 100644 --- a/win_text_editor/lib/modules/data_compare/controllers/data_compare_controller.dart +++ b/win_text_editor/lib/modules/data_compare/controllers/data_compare_controller.dart @@ -8,11 +8,9 @@ class DataCompareController extends BaseContentController { List rightColumns = []; List> leftData = []; List> rightData = []; - late DataCompareDataSource dataSource; - DataCompareController() { - dataSource = DataCompareDataSource(this); - } + // 比对结果状态 + List> comparedData = []; Future importCsv(bool isLeftTable, String csvContent) async { try { @@ -31,19 +29,17 @@ class DataCompareController extends BaseContentController { if (lines[i].trim().isEmpty) continue; final values = lines[i].split(delimiter); - if (values.length < 1) continue; + if (values.isEmpty) continue; // 创建行数据:序号、主键和各列值 final row = { - 'serial': i.toString(), // 序号 - 'key': values[0], // 主键(第一列) + 'serial': i.toString(), + 'key': values[0], + ...Map.fromIterables( + headers, + List.generate(headers.length, (j) => j + 1 < values.length ? values[j + 1] : ''), + ), }; - - // 填充各列数据 - for (int j = 0; j < headers.length; j++) { - row[headers[j]] = (j + 1 < values.length) ? values[j + 1] : ''; - } - data.add(row); } @@ -55,18 +51,61 @@ class DataCompareController extends BaseContentController { rightData = data; } - compareData(); + _compareData(); notifyListeners(); } catch (e) { - Logger().error('${isLeftTable ? '左表' : '右表'}数据导入失败: $e'); + Logger().error('${isLeftTable ? '左表' : '右表'}导入失败: $e'); rethrow; } } - void compareData() { - // Implement comparison logic here - // This would update the comparison status for each row - dataSource.updateData(leftData, rightData); + void _compareData() { + comparedData = []; + + // 1. 处理空数据情况 + if (leftData.isEmpty && rightData.isEmpty) return; + + // 2. 处理单边数据 + if (leftData.isEmpty || rightData.isEmpty) { + final source = leftData.isEmpty ? rightData : leftData; + comparedData = + source + .map((row) => {...row, 'match_status': 'no_match', 'is_left': leftData.isNotEmpty}) + .toList(); + return; + } + + // 3. 双边数据比对 + final rightMap = {for (var r in rightData) r['key']: r}; + + for (var leftRow in leftData) { + final rightRow = rightMap[leftRow['key']]; + + if (rightRow != null) { + bool isFullMatch = leftColumns.every((col) => leftRow[col] == rightRow[col]); + + comparedData.add({ + ...leftRow, + 'match_status': isFullMatch ? 'full_match' : 'key_match', + 'is_left': true, + 'right_data': rightRow, + }); + rightMap.remove(leftRow['key']); + } else { + comparedData.add({...leftRow, 'match_status': 'no_match', 'is_left': true}); + } + } + + // 添加仅右表有的数据 + comparedData.addAll( + rightMap.values.map((row) => ({...row, 'match_status': 'no_match', 'is_left': false})), + ); + + // 4. 排序:匹配的在前 + comparedData.sort((a, b) { + const order = {'full_match': 0, 'key_match': 1, 'no_match': 2}; + return order[a['match_status']]!.compareTo(order[b['match_status']]!); + }); } void exportLeftTable(String matchType) { @@ -89,39 +128,3 @@ class DataCompareController extends BaseContentController { // TODO: implement onOpenFolder } } - -class DataCompareDataSource extends DataGridSource { - final DataCompareController controller; - List _rows = []; - - DataCompareDataSource(this.controller) { - _rows = []; - } - - void updateData(List> leftData, List> rightData) { - _rows = []; - // Implement logic to combine left and right data into rows - // and determine comparison status - notifyListeners(); - } - - @override - List get rows => _rows; - - @override - DataGridRowAdapter? buildRow(DataGridRow row) { - return DataGridRowAdapter( - cells: - row.getCells().map((dataGridCell) { - return Container( - alignment: Alignment.center, - padding: EdgeInsets.all(8), - child: - dataGridCell.value.runtimeType == Icon - ? dataGridCell.value - : Text(dataGridCell.value.toString()), - ); - }).toList(), - ); - } -} diff --git a/win_text_editor/lib/modules/data_compare/widgets/data_compare_data_source.dart b/win_text_editor/lib/modules/data_compare/widgets/data_compare_data_source.dart new file mode 100644 index 0000000..a4efe27 --- /dev/null +++ b/win_text_editor/lib/modules/data_compare/widgets/data_compare_data_source.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_datagrid/datagrid.dart'; +import 'package:win_text_editor/modules/data_compare/controllers/data_compare_controller.dart'; + +class DataCompareDataSource extends DataGridSource { + final DataCompareController controller; + + DataCompareDataSource(this.controller) { + controller.addListener(_handleDataChange); + } + + void _handleDataChange() { + notifyListeners(); + } + + @override + void dispose() { + controller.removeListener(_handleDataChange); + super.dispose(); + } + + @override + List get rows { + return controller.comparedData.map((row) { + final isLeft = row['is_left'] as bool; + final rightData = row['right_data'] as Map?; + final status = row['match_status'] as String; + + return DataGridRow( + cells: [ + // 左表列 + DataGridCell(columnName: 'left_serial', value: isLeft ? row['serial'] : ''), + DataGridCell(columnName: 'left_key', value: isLeft ? row['key'] : ''), + ...controller.leftColumns.map( + (col) => DataGridCell(columnName: 'left_$col', value: isLeft ? row[col] : ''), + ), + + // 对比状态 + DataGridCell(columnName: 'comparison', value: _getStatusIcon(status)), + + // 右表列 + DataGridCell( + columnName: 'right_serial', + value: rightData?['serial'] ?? (!isLeft ? row['serial'] : ''), + ), + DataGridCell( + columnName: 'right_key', + value: rightData?['key'] ?? (!isLeft ? row['key'] : ''), + ), + ...controller.rightColumns.map( + (col) => DataGridCell( + columnName: 'right_$col', + value: rightData?[col] ?? (!isLeft ? row[col] : ''), + ), + ), + ], + ); + }).toList(); + } + + Icon _getStatusIcon(String status) { + switch (status) { + case 'full_match': + return const Icon(Icons.double_arrow, color: Colors.green); + case 'key_match': + return const Icon(Icons.arrow_forward_ios, color: Colors.blue); + default: + return const Icon(Icons.close, color: Colors.red); + } + } + + @override + DataGridRowAdapter? buildRow(DataGridRow row) { + return DataGridRowAdapter( + cells: + row.getCells().map((cell) { + return Container( + alignment: Alignment.center, + padding: const EdgeInsets.all(8), + child: cell.value.runtimeType == Icon ? cell.value : Text(cell.value.toString()), + ); + }).toList(), + ); + } +} diff --git a/win_text_editor/lib/modules/data_compare/widgets/data_compare_grid.dart b/win_text_editor/lib/modules/data_compare/widgets/data_compare_grid.dart index 6b4d00c..3de272a 100644 --- a/win_text_editor/lib/modules/data_compare/widgets/data_compare_grid.dart +++ b/win_text_editor/lib/modules/data_compare/widgets/data_compare_grid.dart @@ -5,7 +5,9 @@ import 'package:win_text_editor/modules/data_compare/controllers/data_compare_co class DataCompareGrid extends StatelessWidget { final DataCompareController controller; - const DataCompareGrid({super.key, required this.controller}); + final DataGridSource dataSource; + + const DataCompareGrid({super.key, required this.dataSource, required this.controller}); @override Widget build(BuildContext context) { @@ -14,7 +16,7 @@ class DataCompareGrid extends StatelessWidget { borderRadius: BorderRadius.circular(2.0), // 这里可以根据需求调整圆角的大小 ), child: SfDataGrid( - source: controller.dataSource, + source: dataSource, columns: _buildColumns(), stackedHeaderRows: _buildStackedHeaders(), columnWidthMode: ColumnWidthMode.fill, diff --git a/win_text_editor/lib/modules/data_compare/widgets/data_compare_view.dart b/win_text_editor/lib/modules/data_compare/widgets/data_compare_view.dart index 5debfb1..26ec4e6 100644 --- a/win_text_editor/lib/modules/data_compare/widgets/data_compare_view.dart +++ b/win_text_editor/lib/modules/data_compare/widgets/data_compare_view.dart @@ -2,12 +2,12 @@ import 'dart:io'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:provider/provider.dart'; import 'package:syncfusion_flutter_datagrid/datagrid.dart'; import 'package:win_text_editor/framework/controllers/logger.dart'; import 'package:win_text_editor/framework/controllers/tab_items_controller.dart'; import 'package:win_text_editor/modules/data_compare/controllers/data_compare_controller.dart'; +import 'package:win_text_editor/modules/data_compare/widgets/data_compare_data_source.dart'; import 'data_compare_grid.dart'; // 新增导入 class DataCompareView extends StatefulWidget { @@ -23,6 +23,8 @@ class _DataCompareViewState extends State { late final DataGridController _dataGridController; bool _isControllerFromTabManager = false; + late final DataCompareDataSource _dataSource; + get tabManager => Provider.of(context, listen: false); @override @@ -39,6 +41,9 @@ class _DataCompareViewState extends State { _isControllerFromTabManager = false; tabManager.registerController(widget.tabId, _controller); } + + // 初始化DataSource + _dataSource = DataCompareDataSource(_controller); } @override @@ -208,7 +213,7 @@ class _DataCompareViewState extends State { ], ), ), - Expanded(child: DataCompareGrid(controller: controller)), + Expanded(child: DataCompareGrid(dataSource: _dataSource, controller: controller)), ], ), );