Browse Source

拆分正常

master
hejl 2 months ago
parent
commit
93e20973b5
  1. 93
      win_text_editor/lib/modules/template_parser/widgets/template_grid_view.dart
  2. 216
      win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart
  3. 83
      win_text_editor/lib/modules/template_parser/widgets/template_tree_view.dart

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

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.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';
class TemplateGridView extends StatelessWidget {
final TemplateParserController controller;
const TemplateGridView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Consumer<TemplateParserController>(
builder: (context, controller, _) {
return _buildGridView(controller.templateItems, controller);
},
);
}
Widget _buildGridView(List<TemplateItem> items, TemplateParserController controller) {
final filteredItems =
controller.selectedNode != null
? items.where((item) => item.matchesPath(controller.selectedNode!.path)).toList()
: items;
final dataSource = _TemplateItemDataSource(
items: filteredItems,
selectedNode: controller.selectedNode,
);
return SfDataGrid(
source: dataSource,
columns: [
GridColumn(
columnName: 'index',
width: 60,
label: Container(
padding: const EdgeInsets.all(8.0),
color: Colors.grey[200],
alignment: Alignment.center,
child: const Text('序号'),
),
),
GridColumn(
columnName: 'content',
label: Container(
padding: const EdgeInsets.all(8.0),
alignment: Alignment.center,
color: Colors.grey[200],
child: const Text('内容'),
),
),
],
gridLinesVisibility: GridLinesVisibility.both,
headerGridLinesVisibility: GridLinesVisibility.both,
columnWidthMode: ColumnWidthMode.fill,
);
}
}
class _TemplateItemDataSource extends DataGridSource {
final List<TemplateItem> items;
final TemplateNode? selectedNode;
_TemplateItemDataSource({required this.items, required this.selectedNode});
@override
List<DataGridRow> get rows =>
items.map((item) {
return DataGridRow(
cells: [
DataGridCell<int>(columnName: 'index', value: items.indexOf(item) + 1),
DataGridCell<String>(columnName: 'content', value: item.value),
],
);
}).toList();
@override
DataGridRowAdapter? buildRow(DataGridRow row) {
return DataGridRowAdapter(
cells:
row.getCells().map<Widget>((dataGridCell) {
return Container(
padding: const EdgeInsets.all(8.0),
alignment:
dataGridCell.columnName == 'index' ? Alignment.center : Alignment.centerLeft,
child: Text(dataGridCell.value.toString()),
);
}).toList(),
);
}
}

216
win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart

@ -1,13 +1,9 @@ @@ -1,13 +1,9 @@
import 'dart:io';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.dart';
import 'package:win_text_editor/framework/controllers/tab_items_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/shared/components/tree_view.dart';
import 'package:win_text_editor/modules/template_parser/widgets/template_grid_view.dart';
import 'package:win_text_editor/modules/template_parser/widgets/template_tree_view.dart';
class TemplateParserView extends StatefulWidget {
final String tabId;
@ -83,217 +79,13 @@ class _TemplateParserViewState extends State<TemplateParserView> { @@ -83,217 +79,13 @@ class _TemplateParserViewState extends State<TemplateParserView> {
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.3,
child: Card(child: _buildTreeView(controller.treeNodes)),
child: Card(child: TemplateTreeView(controller: controller)),
),
const SizedBox(width: 8),
Expanded(
child: Card(
child: GestureDetector(
onSecondaryTapDown: (details) {
_showContextMenu(context, details.globalPosition);
},
child: _buildGridView(controller.templateItems),
),
),
),
],
);
},
);
}
Future<void> _showContextMenu(BuildContext context, Offset position) async {
//
final renderBox = context.findRenderObject() as RenderBox;
final localPosition = renderBox.globalToLocal(position);
//
final result = await showMenu<String>(
context: context,
position: RelativeRect.fromLTRB(
position.dx,
position.dy,
position.dx + renderBox.size.width - localPosition.dx,
position.dy + renderBox.size.height - localPosition.dy,
),
items: [const PopupMenuItem<String>(value: 'export', child: Text('导出(csv)'))],
);
//
if (result == 'export' && context.mounted) {
try {
await _exportToCsv();
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('导出失败: ${e.toString()}')));
}
}
}
}
Future<void> _exportToCsv() async {
String csvData = '序号\t内容\n';
final items = _controller.templateItems;
for (var i = 0; i < items.length; i++) {
final item = items[i];
csvData += '${i + 1}\t${item.value}\n';
}
final filePath = await FilePicker.platform.saveFile(
dialogTitle: '保存导出结果',
fileName: 'template_results.csv',
type: FileType.custom,
allowedExtensions: ['csv'],
);
if (filePath != null) {
final file = File(filePath);
await file.writeAsString(csvData);
}
}
Widget _buildTreeView(List<TemplateNode> nodes) {
if (nodes.isEmpty) {
return const Center(child: Text('No XML data available'));
}
return Consumer<TemplateParserController>(
builder: (context, controller, _) {
return TreeView(
nodes: _processXmlNodes(nodes),
config: const TreeViewConfig(
showIcons: true,
singleSelect: true,
selectedColor: Colors.lightBlueAccent,
icons: {'element': Icons.label_outline, 'attribute': Icons.code},
),
onNodeTap: (node) {
final templateNode = node as TemplateNode;
controller.selectTreeNode(templateNode);
},
nodeBuilder: (context, node, isSelected, onTap) {
final templateNode = node as TemplateNode;
final isAttribute = node.isAttribute;
// 使 selectedNode
final isActuallySelected = controller.selectedNode?.id == templateNode.id;
return Container(
color:
isActuallySelected ? Colors.lightBlueAccent.withOpacity(0.2) : Colors.transparent,
child: Padding(
padding: EdgeInsets.only(left: 12.0 * node.depth),
child: ListTile(
dense: true,
leading:
isAttribute
? const Icon(Icons.code, size: 16, color: Colors.grey)
: const Icon(Icons.label_outline, size: 18, color: Colors.blue),
title: Text(
isAttribute ? templateNode.name.substring(1) : templateNode.name,
style: TextStyle(
color: isAttribute ? Colors.grey[600] : Colors.black,
fontWeight: isAttribute ? FontWeight.normal : FontWeight.w500,
),
),
trailing:
templateNode.isRepeated
? Text(
"(${templateNode.repreatCount.toString()})",
style: const TextStyle(color: Colors.grey),
)
: null,
onTap: onTap,
),
),
);
},
);
},
);
}
List<TreeNode> _processXmlNodes(List<TemplateNode> nodes) {
return nodes.cast<TreeNode>();
}
Widget _buildGridView(List<TemplateItem> items) {
return Consumer<TemplateParserController>(
builder: (context, controller, _) {
// path过滤数据
final filteredItems =
controller.selectedNode != null
? items.where((item) => item.matchesPath(controller.selectedNode!.path)).toList()
: items;
// DataGridSource
final dataSource = _TemplateItemDataSource(
items: filteredItems,
selectedNode: controller.selectedNode,
);
return SfDataGrid(
source: dataSource,
columns: [
GridColumn(
columnName: 'index',
width: 60,
label: Container(
padding: const EdgeInsets.all(8.0),
color: Colors.grey[200],
alignment: Alignment.center,
child: const Text('序号'),
),
),
GridColumn(
columnName: 'content',
label: Container(
padding: const EdgeInsets.all(8.0),
alignment: Alignment.center,
color: Colors.grey[200],
child: const Text('内容'),
),
),
Expanded(child: Card(child: TemplateGridView(controller: controller))),
],
gridLinesVisibility: GridLinesVisibility.both,
headerGridLinesVisibility: GridLinesVisibility.both,
columnWidthMode: ColumnWidthMode.fill,
);
},
);
}
}
class _TemplateItemDataSource extends DataGridSource {
final List<TemplateItem> items;
final TemplateNode? selectedNode;
_TemplateItemDataSource({required this.items, required this.selectedNode});
@override
List<DataGridRow> get rows =>
items.map((item) {
return DataGridRow(
cells: [
DataGridCell<int>(columnName: 'index', value: items.indexOf(item) + 1),
DataGridCell<String>(columnName: 'content', value: item.value),
],
);
}).toList();
@override
DataGridRowAdapter? buildRow(DataGridRow row) {
return DataGridRowAdapter(
cells:
row.getCells().map<Widget>((dataGridCell) {
return Container(
padding: const EdgeInsets.all(8.0),
alignment:
dataGridCell.columnName == 'index' ? Alignment.center : Alignment.centerLeft,
child: Text(dataGridCell.value.toString()),
);
}).toList(),
);
}
}

83
win_text_editor/lib/modules/template_parser/widgets/template_tree_view.dart

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.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/shared/components/tree_view.dart';
class TemplateTreeView extends StatelessWidget {
final TemplateParserController controller;
const TemplateTreeView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Consumer<TemplateParserController>(
builder: (context, controller, _) {
if (controller.treeNodes.isEmpty) {
return const Center(child: Text('No XML data available'));
}
return TreeView(
nodes: _processXmlNodes(controller.treeNodes),
config: const TreeViewConfig(
showIcons: true,
singleSelect: true,
selectedColor: Colors.lightBlueAccent,
icons: {'element': Icons.label_outline, 'attribute': Icons.code},
),
onNodeTap: (node) {
final templateNode = node as TemplateNode;
controller.selectTreeNode(templateNode);
},
nodeBuilder: (context, node, isSelected, onTap) {
return _buildTreeNode(node, isSelected, onTap, controller);
},
);
},
);
}
List<TreeNode> _processXmlNodes(List<TemplateNode> nodes) {
return nodes.cast<TreeNode>();
}
Widget _buildTreeNode(
TreeNode node,
bool isSelected,
VoidCallback onTap,
TemplateParserController controller,
) {
final templateNode = node as TemplateNode;
final isAttribute = node.isAttribute;
final isActuallySelected = controller.selectedNode?.id == templateNode.id;
return Container(
color: isActuallySelected ? Colors.lightBlueAccent.withOpacity(0.2) : Colors.transparent,
child: Padding(
padding: EdgeInsets.only(left: 12.0 * node.depth),
child: ListTile(
dense: true,
leading:
isAttribute
? const Icon(Icons.code, size: 16, color: Colors.grey)
: const Icon(Icons.label_outline, size: 18, color: Colors.blue),
title: Text(
isAttribute ? templateNode.name.substring(1) : templateNode.name,
style: TextStyle(
color: isAttribute ? Colors.grey[600] : Colors.black,
fontWeight: isAttribute ? FontWeight.normal : FontWeight.w500,
),
),
trailing:
templateNode.isRepeated
? Text(
"(${templateNode.repreatCount.toString()})",
style: const TextStyle(color: Colors.grey),
)
: null,
onTap: onTap,
),
),
);
}
}
Loading…
Cancel
Save