Browse Source

模板解析OK

master
hejl 2 months ago
parent
commit
4ffb5f39f9
  1. 252
      win_text_editor/lib/modules/template_parser/widgets/template_grid_view.dart

252
win_text_editor/lib/modules/template_parser/widgets/template_grid_view.dart

@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart'; import 'package:syncfusion_flutter_datagrid/datagrid.dart';
import 'package:win_text_editor/framework/controllers/logger.dart';
import 'package:win_text_editor/modules/template_parser/controllers/template_parser_controller.dart'; import 'package:win_text_editor/modules/template_parser/controllers/template_parser_controller.dart';
import 'package:win_text_editor/modules/template_parser/models/template_node.dart'; import 'package:win_text_editor/modules/template_parser/models/template_node.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
@ -18,9 +19,9 @@ class TemplateGridView extends StatelessWidget {
return GestureDetector( return GestureDetector(
onSecondaryTapDown: (details) { onSecondaryTapDown: (details) {
_showContextMenu(context, details.globalPosition, controller); _showContextMenu(context, details.globalPosition, controller);
}, },
child: _buildGridView(controller.templateItems, controller), child: _buildGridView(controller),
); );
}, },
); );
} }
@ -30,11 +31,9 @@ class TemplateGridView extends StatelessWidget {
Offset position, Offset position,
TemplateParserController controller, TemplateParserController controller,
) async { ) async {
//
final renderBox = context.findRenderObject() as RenderBox; final renderBox = context.findRenderObject() as RenderBox;
final localPosition = renderBox.globalToLocal(position); final localPosition = renderBox.globalToLocal(position);
//
final result = await showMenu<String>( final result = await showMenu<String>(
context: context, context: context,
position: RelativeRect.fromLTRB( position: RelativeRect.fromLTRB(
@ -46,7 +45,6 @@ class TemplateGridView extends StatelessWidget {
items: [const PopupMenuItem<String>(value: 'export', child: Text('导出(csv)'))], items: [const PopupMenuItem<String>(value: 'export', child: Text('导出(csv)'))],
); );
//
if (result == 'export' && context.mounted) { if (result == 'export' && context.mounted) {
try { try {
await _exportToCsv(controller); await _exportToCsv(controller);
@ -56,29 +54,30 @@ class TemplateGridView extends StatelessWidget {
context, context,
).showSnackBar(SnackBar(content: Text('导出失败: ${e.toString()}'))); ).showSnackBar(SnackBar(content: Text('导出失败: ${e.toString()}')));
} }
} }
} }
} }
Future<void> _exportToCsv(TemplateParserController controller) async { Future<void> _exportToCsv(TemplateParserController controller) async {
String csvData = '序号\t';
final selectedNodes = controller.getSelectedNodes(); final selectedNodes = controller.getSelectedNodes();
for (var node in selectedNodes) { if (selectedNodes.isEmpty) return;
csvData += '${node.name}\t';
} //
String csvData = '序号\t';
csvData += selectedNodes.map((node) => node.name).join('\t');
csvData += '\n'; csvData += '\n';
final filteredItems = //
controller.selectedNode != null final rows = _getGroupedData(controller);
? controller.templateItems.where((item) => item.matchesPath(controller.selectedNode!.path)).toList()
: controller.templateItems;
for (var item in filteredItems) { //
csvData += '${filteredItems.indexOf(item) + 1}\t'; for (var i = 0; i < rows.length; i++) {
for (var node in selectedNodes) { csvData += '${i + 1}\t';
// csvData += selectedNodes
csvData += '\t'; .map((node) {
} return rows[i][node.path] ?? '';
})
.join('\t');
csvData += '\n'; csvData += '\n';
} }
@ -90,50 +89,49 @@ class TemplateGridView extends StatelessWidget {
); );
if (filePath != null) { if (filePath != null) {
final file = File(filePath); await File(filePath).writeAsString(csvData);
await file.writeAsString(csvData);
} }
} }
Widget _buildGridView(List<TemplateItem> items, TemplateParserController controller) { Widget _buildGridView(TemplateParserController controller) {
final filteredItems =
controller.selectedNode != null
? items.where((item) => item.matchesPath(controller.selectedNode!.path)).toList()
: items;
final selectedNodes = controller.getSelectedNodes(); final selectedNodes = controller.getSelectedNodes();
final dataSource = _TemplateItemDataSource( if (selectedNodes.isEmpty) {
items: filteredItems, return const Center(child: Text('请在左侧树中选择要显示的节点(勾选复选框)'));
selectedNodes: selectedNodes, }
);
List<GridColumn> columns = [ //
GridColumn( final allItems = controller.templateItems;
columnName: 'index',
width: 60, // -
label: Container( final rows = _buildDataRows(selectedNodes, allItems);
padding: const EdgeInsets.all(8.0),
color: Colors.grey[200], final dataSource = _TemplateItemDataSource(rows: rows, selectedNodes: selectedNodes);
alignment: Alignment.center,
child: const Text('序号'),
),
),
];
for (var node in selectedNodes) { //
columns.add( final columns = <GridColumn>[
GridColumn( GridColumn(
columnName: node.id.toString(), columnName: 'index',
width: 60,
label: Container(
padding: const EdgeInsets.all(8.0),
color: Colors.grey[200],
alignment: Alignment.center,
child: const Text('序号'),
),
),
...selectedNodes.map((node) {
return GridColumn(
columnName: node.path,
label: Container( label: Container(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
alignment: Alignment.center, alignment: Alignment.center,
color: Colors.grey[200], color: Colors.grey[200],
child: Text(node.name), child: Text(node.isAttribute ? node.name.substring(1) : node.name),
), ),
), );
); }).toList(),
} ];
return SfDataGrid( return SfDataGrid(
source: dataSource, source: dataSource,
@ -143,28 +141,148 @@ class TemplateGridView extends StatelessWidget {
columnWidthMode: ColumnWidthMode.fill, columnWidthMode: ColumnWidthMode.fill,
); );
} }
List<Map<String, dynamic>> _buildDataRows(
List<TemplateNode> selectedNodes,
List<TemplateItem> allItems,
) {
// 1.
final nodeValueGroups = <String, List<MapEntry<int, String>>>{};
for (final node in selectedNodes) {
//
final valuesWithIndex =
allItems
.asMap()
.entries
.where((entry) => entry.value.xPath == node.path)
.map((entry) => MapEntry(entry.key, entry.value.value))
.toList();
nodeValueGroups[node.path] = valuesWithIndex;
}
// 2.
final maxRows = nodeValueGroups.values.fold(
0,
(max, group) => group.length > max ? group.length : max,
);
// 3.
final rows = <Map<String, dynamic>>[];
for (var rowIndex = 0; rowIndex < maxRows; rowIndex++) {
final row = <String, dynamic>{'_index': rowIndex + 1};
//
for (final node in selectedNodes) {
final values = nodeValueGroups[node.path]!;
row[node.path] = rowIndex < values.length ? values[rowIndex].value : '';
}
rows.add(row);
}
return rows;
}
List<String> _getInstancesForParent(String parentPath, List<TemplateItem> items) {
//
return items
.where((item) => item.xPath.startsWith(parentPath))
.map((item) {
//
if (parentPath.contains('[')) {
return item.xPath.substring(0, item.xPath.indexOf('/', parentPath.length));
}
return parentPath;
})
.toSet()
.toList();
}
//
List<Map<String, String>> _getGroupedData(TemplateParserController controller) {
final selectedNodes = controller.getSelectedNodes();
if (selectedNodes.isEmpty) return [];
//
final allItems =
controller.templateItems.where((item) {
return selectedNodes.any((node) => item.xPath == node.path);
}).toList();
//
final parentPaths =
selectedNodes.map((node) {
return node.path.substring(0, node.path.lastIndexOf('/'));
}).toSet();
final groupedData = <Map<String, String>>[];
for (final parentPath in parentPaths) {
//
final items =
allItems.where((item) {
return item.xPath.startsWith(parentPath) ||
item.xPath == parentPath ||
(item.xPath.contains('@') &&
item.xPath.substring(0, item.xPath.lastIndexOf('@')) == parentPath);
}).toList();
//
final instanceGroups = <String, Map<String, String>>{};
for (final item in items) {
//
final instanceId = _getInstanceId(item.xPath, parentPath);
if (!instanceGroups.containsKey(instanceId)) {
instanceGroups[instanceId] = {'_parent': parentPath};
}
instanceGroups[instanceId]![item.xPath] = item.value;
}
groupedData.addAll(instanceGroups.values);
}
return groupedData;
}
String _getInstanceId(String fullPath, String parentPath) {
// 使ID
return fullPath.substring(parentPath.length);
}
} }
class _TemplateItemDataSource extends DataGridSource { class _TemplateItemDataSource extends DataGridSource {
final List<TemplateItem> items; final List<Map<String, dynamic>> _rows;
final List<TemplateNode> selectedNodes; final List<TemplateNode> selectedNodes;
_TemplateItemDataSource({required this.items, required this.selectedNodes}); _TemplateItemDataSource({required List<Map<String, dynamic>> rows, required this.selectedNodes})
: _rows = rows;
@override @override
List<DataGridRow> get rows => List<DataGridRow> get rows {
items.map((item) { // print("[DEBUG] 原始可加载记录数:${_rows.length}");
List<DataGridCell> cells = [ return _rows.asMap().entries.map((entry) {
DataGridCell<int>(columnName: 'index', value: items.indexOf(item) + 1), final index = entry.key;
]; final rowData = entry.value;
for (var node in selectedNodes) {
//
cells.add(DataGridCell<String>(columnName: node.id.toString(), value: ''));
}
return DataGridRow(cells: cells); return DataGridRow(
}).toList(); cells: [
DataGridCell<int>(columnName: 'index', value: index + 1),
...selectedNodes.map((node) {
return DataGridCell<String>(
columnName: node.path,
value: rowData[node.path]?.toString() ?? '',
);
}).toList(),
],
);
}).toList();
}
@override @override
DataGridRowAdapter? buildRow(DataGridRow row) { DataGridRowAdapter? buildRow(DataGridRow row) {

Loading…
Cancel
Save