Browse Source

元数据迁移工具收工

master
hejl 2 months ago
parent
commit
e0f3566515
  1. BIN
      documents/PB UFT模块迁移方案.docx
  2. 141
      win_text_editor/lib/modules/data_compare/controllers/data_compare_controller.dart
  3. 48
      win_text_editor/lib/modules/data_compare/widgets/data_compare_data_source.dart
  4. 2
      win_text_editor/lib/modules/data_compare/widgets/data_compare_grid.dart
  5. 67
      win_text_editor/lib/modules/data_compare/widgets/data_compare_view.dart
  6. 105
      win_text_editor/lib/modules/demo/widgets/demo_view.dart

BIN
documents/PB UFT模块迁移方案.docx

Binary file not shown.

141
win_text_editor/lib/modules/data_compare/controllers/data_compare_controller.dart

@ -1,7 +1,9 @@
import 'package:flutter/material.dart'; import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:win_text_editor/framework/controllers/logger.dart'; import 'package:win_text_editor/framework/controllers/logger.dart';
import 'package:win_text_editor/shared/base/base_content_controller.dart'; import 'package:win_text_editor/shared/base/base_content_controller.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart'; import 'package:path/path.dart' as path;
class DataCompareController extends BaseContentController { class DataCompareController extends BaseContentController {
List<String> leftColumns = []; List<String> leftColumns = [];
@ -65,7 +67,7 @@ class DataCompareController extends BaseContentController {
// 1. // 1.
if (leftData.isEmpty && rightData.isEmpty) return; if (leftData.isEmpty && rightData.isEmpty) return;
// 2. //
if (leftData.isEmpty || rightData.isEmpty) { if (leftData.isEmpty || rightData.isEmpty) {
final source = leftData.isEmpty ? rightData : leftData; final source = leftData.isEmpty ? rightData : leftData;
comparedData = comparedData =
@ -78,6 +80,7 @@ class DataCompareController extends BaseContentController {
// 3. // 3.
final rightMap = {for (var r in rightData) r['key']: r}; final rightMap = {for (var r in rightData) r['key']: r};
//
for (var leftRow in leftData) { for (var leftRow in leftData) {
final rightRow = rightMap[leftRow['key']]; final rightRow = rightMap[leftRow['key']];
@ -108,14 +111,134 @@ class DataCompareController extends BaseContentController {
}); });
} }
void exportLeftTable(String matchType) { void exportLeftTable(String matchType) async {
// Implement export logic based on matchType try {
// 'full_match', 'key_match', or 'no_match' final filteredData =
comparedData.where((row) {
if (!row['is_left']) return false;
return row['match_status'] == matchType;
}).toList();
if (filteredData.isEmpty) {
Logger().info('没有匹配类型的左表数据可导出');
return;
}
// CSV内容
final csvContent = _generateCsvContent(filteredData, leftColumns);
final fileName = 'left_table_${_getMatchTypeName(matchType)}.csv';
await _saveCsvFile(fileName, csvContent);
Logger().info('左表(${_getMatchTypeName(matchType)})导出成功');
} catch (e) {
Logger().error('左表导出失败: $e');
rethrow;
}
}
void exportRightTable(String matchType) async {
try {
final List<Map<String, dynamic>> filteredData = [];
//
if (matchType == 'full_match') {
filteredData.addAll(
comparedData.where((row) {
return row['match_status'] == "full_match";
}).toList(),
);
} else if (matchType == 'key_match') {
//right_data中取出右表数据
filteredData.addAll(
comparedData
.where((row) => row['match_status'] == "key_match")
.map((row) {
// right_data存在且是Map类型
if (row['right_data'] is Map<String, dynamic>) {
return row['right_data'] as Map<String, dynamic>;
} else {
return <String, dynamic>{}; // Map作为保护
}
})
.where((row) => row.isNotEmpty) //
.toList(),
);
} else {
filteredData.addAll(
comparedData.where((row) {
if (row['is_left']) return false;
return row['match_status'] == matchType;
}).toList(),
);
}
if (filteredData.isEmpty) {
Logger().info('没有匹配类型的右表数据可导出');
return;
}
// CSV内容
final csvContent = _generateCsvContent(filteredData, rightColumns);
final fileName = 'right_table_${_getMatchTypeName(matchType)}.csv';
await _saveCsvFile(fileName, csvContent);
Logger().info('右表(${_getMatchTypeName(matchType)})导出成功');
} catch (e) {
Logger().error('右表导出失败: $e');
rethrow;
}
}
// CSV内容
String _generateCsvContent(List<Map<String, dynamic>> data, List<String> columns) {
final buffer = StringBuffer();
// ()
buffer.write('主键'); // "序号"
for (var column in columns) {
buffer.write(',$column');
}
buffer.writeln();
//
for (var row in data) {
buffer.write('${row['key']}'); // serial
for (var column in columns) {
buffer.write(',${row[column] ?? ''}');
}
buffer.writeln();
}
return buffer.toString();
}
// CSV文件
Future<void> _saveCsvFile(String fileName, String content) async {
final filePath = await FilePicker.platform.saveFile(
dialogTitle: '保存对比结果',
fileName: fileName,
type: FileType.custom,
allowedExtensions: ['csv'],
);
if (filePath != null) {
final file = File(filePath);
await file.writeAsString(content);
}
} }
void exportRightTable(String matchType) { //
// Implement export logic based on matchType String _getMatchTypeName(String matchType) {
// 'full_match', 'key_match', or 'no_match' switch (matchType) {
case 'full_match':
return '整行匹配';
case 'key_match':
return '主键匹配';
case 'no_match':
return '不匹配';
default:
return '未知类型';
}
} }
@override @override

48
win_text_editor/lib/modules/data_compare/widgets/data_compare_data_source.dart

@ -26,35 +26,47 @@ class DataCompareDataSource extends DataGridSource {
final rightData = row['right_data'] as Map<String, dynamic>?; final rightData = row['right_data'] as Map<String, dynamic>?;
final status = row['match_status'] as String; final status = row['match_status'] as String;
return DataGridRow( return DataGridRow(cells: [
cells: [ // -
// DataGridCell<String>(
DataGridCell<String>(columnName: 'left_serial', value: isLeft ? row['serial'] : ''), columnName: 'left_serial',
DataGridCell<String>(columnName: 'left_key', value: isLeft ? row['key'] : ''), value: (isLeft || status != 'no_match') ? row['serial'] : ''
...controller.leftColumns.map(
(col) => DataGridCell<String>(columnName: 'left_$col', value: isLeft ? row[col] : ''),
), ),
DataGridCell<String>(
columnName: 'left_key',
value: (isLeft || status != 'no_match') ? row['key'] : ''
),
...controller.leftColumns.map((col) => DataGridCell<String>(
columnName: 'left_$col',
value: (isLeft || status != 'no_match') ? row[col] : ''
)),
// //
DataGridCell<Icon>(columnName: 'comparison', value: _getStatusIcon(status)), DataGridCell<Icon>(
columnName: 'comparison',
value: _getStatusIcon(status)
),
// // -
DataGridCell<String>( DataGridCell<String>(
columnName: 'right_serial', columnName: 'right_serial',
value: rightData?['serial'] ?? (!isLeft ? row['serial'] : ''), value: (!isLeft || status != 'no_match')
? (rightData?['serial'] ?? row['serial'])
: ''
), ),
DataGridCell<String>( DataGridCell<String>(
columnName: 'right_key', columnName: 'right_key',
value: rightData?['key'] ?? (!isLeft ? row['key'] : ''), value: (!isLeft || status != 'no_match')
? (rightData?['key'] ?? row['key'])
: ''
), ),
...controller.rightColumns.map( ...controller.rightColumns.map((col) => DataGridCell<String>(
(col) => DataGridCell<String>(
columnName: 'right_$col', columnName: 'right_$col',
value: rightData?[col] ?? (!isLeft ? row[col] : ''), value: (!isLeft || status != 'no_match')
), ? (rightData?[col] ?? row[col])
), : ''
], )),
); ]);
}).toList(); }).toList();
} }

2
win_text_editor/lib/modules/data_compare/widgets/data_compare_grid.dart

@ -62,8 +62,6 @@ class DataCompareGrid extends StatelessWidget {
], ],
child: _buildGroupHeader('左表', color: Colors.green[50]), child: _buildGroupHeader('左表', color: Colors.green[50]),
), ),
//
// StackedHeaderCell(columnNames: ["comparison"], child: Container()),
StackedHeaderCell( StackedHeaderCell(
columnNames: [ columnNames: [
'right_serial', 'right_serial',

67
win_text_editor/lib/modules/data_compare/widgets/data_compare_view.dart

@ -93,40 +93,45 @@ class _DataCompareViewState extends State<DataCompareView> {
// //
Widget _buildExportButton(bool isLeftTable) { Widget _buildExportButton(bool isLeftTable) {
return ElevatedButton( return Builder(
onPressed: () { builder: (btnContext) {
_showExportMenu(isLeftTable); return ElevatedButton(
onPressed: () {
final RenderBox renderBox = btnContext.findRenderObject() as RenderBox;
final buttonSize = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
_showExportMenu(
isLeftTable,
RelativeRect.fromLTRB(
offset.dx,
offset.dy + buttonSize.height,
offset.dx + buttonSize.width,
offset.dy + buttonSize.height,
),
);
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: isLeftTable ? Colors.green[50] : Colors.blue[50], backgroundColor: isLeftTable ? Colors.green[50] : Colors.blue[50],
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
), ),
child: Row( child: Row(
children: [ children: [
const Icon(Icons.output, size: 18), const Icon(Icons.output, size: 18),
const SizedBox(width: 4), const SizedBox(width: 4),
Text('导出${isLeftTable ? '左表' : '右表'}'), Text('导出${isLeftTable ? '左表' : '右表'}'),
], ],
), ),
);
},
); );
} }
// //
void _showExportMenu(bool isLeftTable) { void _showExportMenu(bool isLeftTable, RelativeRect position) {
final RenderBox button = context.findRenderObject() as RenderBox;
final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
final RelativeRect position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(Offset.zero, ancestor: overlay), //
button.localToGlobal(button.size.bottomRight(Offset.zero), ancestor: overlay),
),
Offset.zero & overlay.size,
);
showMenu<String>( showMenu<String>(
context: context, context: context,
position: position, position: position,
items: [ items: [
const PopupMenuItem( const PopupMenuItem(
value: 'full_match', value: 'full_match',
@ -151,7 +156,11 @@ class _DataCompareViewState extends State<DataCompareView> {
const PopupMenuItem( const PopupMenuItem(
value: 'no_match', value: 'no_match',
child: Row( child: Row(
children: [Icon(Icons.close, color: Colors.red), SizedBox(width: 8), Text('不匹配')], children: [
Icon(Icons.close, color: Colors.red),
SizedBox(width: 8),
Text('不匹配')
],
), ),
), ),
], ],

105
win_text_editor/lib/modules/demo/widgets/demo_view.dart

@ -44,10 +44,109 @@ class _DemoViewState extends State<DemoView> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ChangeNotifierProvider.value( return ChangeNotifierProvider.value(
value: _controller, value: _controller,
child: const Padding( child: const Padding(padding: EdgeInsets.all(8.0), child: ButtonPositionExample()),
padding: EdgeInsets.all(8.0), );
child: Column(children: [Expanded(flex: 1, child: Text("Demo,就是一个空窗体,用于新增菜单时快速初始化(拷贝)。"))]), }
}
class ButtonPositionExample extends StatefulWidget {
const ButtonPositionExample({Key? key}) : super(key: key);
@override
_ButtonPositionExampleState createState() => _ButtonPositionExampleState();
}
class _ButtonPositionExampleState extends State<ButtonPositionExample> {
// GlobalKey
final GlobalKey _buttonKey = GlobalKey();
//
String _buttonPosition = '未获取';
//
String _clickPosition = '未点击';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('按钮位置和点击坐标示例')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
//
Text('按钮位置: $_buttonPosition'),
const SizedBox(height: 10),
//
Text('点击位置: $_clickPosition'),
const SizedBox(height: 30),
//
ElevatedButton(
key: _buttonKey,
onPressed: () {
_getButtonPosition();
},
child: const Text('获取按钮位置'),
),
const SizedBox(height: 20),
//
GestureDetector(
onTapDown: (TapDownDetails details) {
_getClickPosition(details);
},
child: Container(
padding: const EdgeInsets.all(16.0),
color: Colors.blue,
child: const Text('点击此处获取点击坐标', style: TextStyle(color: Colors.white)),
),
),
const SizedBox(height: 20),
//
Builder(
builder: (BuildContext context) {
return ElevatedButton(
onPressed: () {
// 使context.findRenderObject()
final RenderBox renderBox = context.findRenderObject() as RenderBox;
final position = renderBox.localToGlobal(Offset.zero);
setState(() {
_buttonPosition =
'按钮位置: (${position.dx.toStringAsFixed(2)}, ${position.dy.toStringAsFixed(2)})';
});
},
child: const Text('使用Builder获取按钮位置'),
);
},
),
],
),
), ),
); );
} }
//
void _getButtonPosition() {
final RenderBox renderBox = _buttonKey.currentContext?.findRenderObject() as RenderBox;
final position = renderBox.localToGlobal(Offset.zero);
setState(() {
_buttonPosition =
'按钮位置: (${position.dx.toStringAsFixed(2)}, ${position.dy.toStringAsFixed(2)})';
});
}
//
void _getClickPosition(TapDownDetails details) {
final RenderBox renderBox = context.findRenderObject() as RenderBox;
final position = renderBox.globalToLocal(details.globalPosition);
setState(() {
_clickPosition =
'点击位置: (${position.dx.toStringAsFixed(2)}, ${position.dy.toStringAsFixed(2)})';
});
}
} }

Loading…
Cancel
Save