Browse Source

就差过滤效果了

master
hejl 2 months ago
parent
commit
d05b796cda
  1. 181
      win_text_editor/lib/modules/template_parser/controllers/filter_controller.dart
  2. 53
      win_text_editor/lib/modules/template_parser/controllers/grid_view_controller.dart
  3. 9
      win_text_editor/lib/modules/template_parser/controllers/template_notifier.dart
  4. 249
      win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart
  5. 68
      win_text_editor/lib/modules/template_parser/controllers/tree_view_controller.dart
  6. 11
      win_text_editor/lib/modules/template_parser/models/template_node.dart
  7. 140
      win_text_editor/lib/modules/template_parser/widgets/template_filter_panel.dart
  8. 81
      win_text_editor/lib/modules/template_parser/widgets/template_grid_view.dart
  9. 25
      win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart
  10. 19
      win_text_editor/lib/modules/template_parser/widgets/template_tree_view.dart

181
win_text_editor/lib/modules/template_parser/controllers/filter_controller.dart

@ -0,0 +1,181 @@ @@ -0,0 +1,181 @@
// filter_controller.dart
import 'package:flutter/foundation.dart';
import 'package:win_text_editor/modules/template_parser/models/template_node.dart';
import 'template_notifier.dart';
class FilterController extends TemplateNotifier {
String? _selectedFilterField;
String? _selectedFilterOperator;
String _filterValue = '';
bool _isFilterValid = false;
bool _isFilterApplied = false;
List<String> _availableFields = [];
// Notifier
final ValueNotifier<bool> _filterActionNotifier = ValueNotifier(false);
String? get selectedFilterField => _selectedFilterField;
String? get selectedFilterOperator => _selectedFilterOperator;
String get filterValue => _filterValue;
bool get isFilterValid => _isFilterValid;
bool get isFilterApplied => _isFilterApplied;
List<String> get availableFields => _availableFields;
ValueNotifier<bool> get filterActionNotifier => _filterActionNotifier;
set selectedFilterOperator(String? value) {
_selectedFilterOperator = value;
_updateFilterValidity();
//
safeNotify();
}
set filterValue(String value) {
_filterValue = value;
_updateFilterValidity();
//
safeNotify();
}
set selectedFilterField(String? value) {
//
if (value != null && !_availableFields.contains(value)) {
_selectedFilterField = null;
} else {
_selectedFilterField = value;
}
_updateFilterValidity();
safeNotify();
}
void updateAvailableFields(List<String> newFields) {
_availableFields = newFields;
//
if (_selectedFilterField != null && !newFields.contains(_selectedFilterField)) {
_selectedFilterField = null;
}
_updateFilterValidity();
safeNotify();
}
void _updateFilterValidity() {
_isFilterValid = _checkFilterValidity();
}
bool _checkFilterValidity() {
if (_selectedFilterField == null || _selectedFilterOperator == null || _filterValue.isEmpty) {
return false;
}
final operator = _selectedFilterOperator!;
final value = _filterValue;
if (operator == 'between' || operator == 'in') {
final parts = value.split(',').map((e) => e.trim()).where((e) => e.isNotEmpty).toList();
if (parts.length < 2) {
return false;
}
if (operator == 'between') {
for (var part in parts) {
if (double.tryParse(part) == null) {
return false;
}
}
}
} else if (operator == '>' || operator == '<') {
return double.tryParse(value) != null;
}
return true;
}
//
List<TemplateItem> doFilter(List<TemplateItem> templateItems) {
if (selectedFilterField == null || selectedFilterOperator == null) {
return templateItems;
}
List<TemplateItem> filteredItems = [];
// 1.
final primaryMatches = templateItems.where((item) {
final nodeName = item.xPath.split('/').last;
return nodeName == selectedFilterField && _matchesCondition(item.value);
});
// 2. IDID
final matchedIds = primaryMatches.map((item) => item.id).toSet();
// 3.
filteredItems.cast();
filteredItems.addAll(
filteredItems.where((item) {
return matchedIds.contains(item.id);
}).toList(),
);
_isFilterApplied = true;
return filteredItems;
}
//
bool _matchesCondition(String value) {
if (selectedFilterOperator == null || filterValue.isEmpty) {
return false;
}
switch (selectedFilterOperator!) {
case '==':
return value == filterValue;
case '>':
final numValue = double.tryParse(value);
final filterNum = double.tryParse(filterValue);
return numValue != null && filterNum != null && numValue > filterNum;
case '<':
final numValue = double.tryParse(value);
final filterNum = double.tryParse(filterValue);
return numValue != null && filterNum != null && numValue < filterNum;
case 'between':
final parts =
filterValue.split(',').map((e) => e.trim()).where((e) => e.isNotEmpty).toList();
if (parts.length < 2) return false;
final lower = double.tryParse(parts[0]);
final upper = double.tryParse(parts[1]);
final numValue = double.tryParse(value);
return numValue != null &&
lower != null &&
upper != null &&
numValue >= lower &&
numValue <= upper;
case 'in':
final filterValues =
filterValue.split(',').map((e) => e.trim()).where((e) => e.isNotEmpty).toList();
return filterValues.contains(value);
default:
return false;
}
}
//
void applyFilter() {
_isFilterApplied = true;
_filterActionNotifier.value = !_filterActionNotifier.value;
}
//
void clearFilter() {
_isFilterApplied = false;
_filterActionNotifier.value = !_filterActionNotifier.value;
}
}

53
win_text_editor/lib/modules/template_parser/controllers/grid_view_controller.dart

@ -0,0 +1,53 @@ @@ -0,0 +1,53 @@
// grid_view_controller.dart
import 'package:win_text_editor/modules/template_parser/models/template_node.dart';
import 'template_notifier.dart';
class GridViewController extends TemplateNotifier {
List<TemplateItem> _templateItems = [];
List<TemplateItem> _filteredItems = [];
bool _isFilterApplied = false;
List<TemplateItem> get displayedItems => _isFilterApplied ? _filteredItems : _templateItems;
bool get isFilterApplied => _isFilterApplied;
List<TemplateItem> get templateItems => _templateItems;
//
List<TemplateNode>? _currentTreeNodes;
void updateTreeNodesRef(List<TemplateNode> nodes) {
_currentTreeNodes = nodes;
safeNotify();
}
List<TemplateNode> getSelectedNodes() {
if (_currentTreeNodes == null) return [];
List<TemplateNode> selectedNodes = [];
void traverse(TemplateNode node) {
if (node.isChecked) selectedNodes.add(node);
for (var child in node.children) {
traverse(child);
}
}
for (var node in _currentTreeNodes!) {
traverse(node);
}
return selectedNodes;
}
void updateTemplateItems(List<TemplateItem> items) {
_templateItems = items;
safeNotify();
}
void applyFilter(List<TemplateItem> filteredItems) {
_filteredItems = filteredItems;
_isFilterApplied = true;
safeNotify();
}
void clearFilter() {
_isFilterApplied = false;
safeNotify();
}
}

9
win_text_editor/lib/modules/template_parser/controllers/template_notifier.dart

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
// template_notifier.dart
import 'package:flutter/foundation.dart';
abstract class TemplateNotifier extends ChangeNotifier {
@protected
void safeNotify() {
if (hasListeners) notifyListeners();
}
}

249
win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart

@ -4,26 +4,58 @@ import 'package:win_text_editor/modules/template_parser/models/template_node.dar @@ -4,26 +4,58 @@ import 'package:win_text_editor/modules/template_parser/models/template_node.dar
import 'package:win_text_editor/shared/base/base_content_controller.dart';
import 'package:xml/xml.dart' as xml;
import 'dart:io';
import 'tree_view_controller.dart';
import 'filter_controller.dart';
import 'grid_view_controller.dart';
class TemplateParserController extends BaseContentController {
final TreeViewController treeController;
final FilterController filterController;
final GridViewController gridController;
String _filePath = '';
List<TemplateNode> _treeNodes = [];
List<TemplateItem> _templateItems = [];
String? _errorMessage;
TemplateNode? _selectedNode;
String? _currentParentPath; //
// Getters
String get filePath => _filePath;
List<TemplateNode> get treeNodes => _treeNodes;
List<TemplateItem> get templateItems => _templateItems;
String? get errorMessage => _errorMessage;
TemplateNode? get selectedNode => _selectedNode;
//-------------------
TemplateParserController()
: treeController = TreeViewController(),
filterController = FilterController(),
gridController = GridViewController() {
_setupCrossControllerCommunication();
}
//
void _setupCrossControllerCommunication() {
// 1
treeController.addListener(() {
filterController.updateAvailableFields(treeController.selectedNodeNames);
// Grid的节点引用
gridController.updateTreeNodesRef(treeController.treeNodes);
});
// 2
filterController.filterActionNotifier.addListener(() {
if (filterController.isFilterApplied) {
final List<TemplateItem> filteredItems = filterController.doFilter(
gridController.templateItems,
);
gridController.applyFilter(filteredItems);
} else {
gridController.clearFilter();
}
});
}
//---------------------
//widget调用入口
Future<void> pickFile() async {
final result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ['xml'],
allowedExtensions: ['xml', '*'],
);
if (result != null) {
_filePath = result.files.single.path!;
@ -31,119 +63,35 @@ class TemplateParserController extends BaseContentController { @@ -31,119 +63,35 @@ class TemplateParserController extends BaseContentController {
}
}
//
Future<void> setFilePath(String path) async {
_filePath = path;
await _loadTemplateData();
}
void selectTreeNode(TemplateNode node) {
_selectedNode = node;
notifyListeners();
}
//
void toggleNodeCheck(TemplateNode node) {
final parentPath = node.path.substring(0, node.path.lastIndexOf('/'));
if (_currentParentPath != null && _currentParentPath != parentPath) {
//
clearAllChecked();
}
node.isChecked = !node.isChecked;
_currentParentPath = parentPath;
notifyListeners();
}
//
void clearAllChecked() {
void traverse(TemplateNode node) {
node.isChecked = false;
for (var child in node.children) {
traverse(child);
}
}
for (var node in _treeNodes) {
traverse(node);
}
}
//xml文件
Future<void> _loadTemplateData() async {
try {
_errorMessage = null;
_treeNodes = [];
_templateItems = [];
_selectedNode = null;
_currentParentPath = null; //
if (_filePath.isEmpty) return;
final file = File(_filePath);
final content = await file.readAsString();
await _parseXmlContent(content);
final document = xml.XmlDocument.parse(content);
//
//
treeController.updateTreeNodes(
_buildTreeNodes(document.rootElement, document.rootElement.localName, depth: 0),
);
//
gridController.updateTemplateItems(_parseAllNodeValues(document));
} catch (e) {
_errorMessage = 'Failed to load XML: ${e.toString()}';
Logger().error('XML加载错误$_errorMessage');
} finally {
notifyListeners();
}
}
Future<void> _parseXmlContent(String xmlContent) async {
final document = xml.XmlDocument.parse(xmlContent);
Logger().debug('开始解析XML,根元素: ${document.rootElement.name}');
_treeNodes = _buildTreeNodes(document.rootElement, document.rootElement.localName, depth: 0);
// templateItems
_templateItems = _parseAllNodeValues(document);
notifyListeners();
}
List<TemplateItem> _parseAllNodeValues(xml.XmlDocument document) {
final items = <TemplateItem>[];
int id = 0;
//
void traverse(xml.XmlElement element, String currentPath) {
// 1.
for (final attr in element.attributes) {
items.add(
TemplateItem(
id: id++,
content: attr.value,
xPath: '$currentPath/@${attr.name.local}',
value: attr.value,
nodeType: NodeType.attribute,
),
);
}
// 2.
final textNodes = element.children.whereType<xml.XmlText>().where(
(t) => t.text.trim().isNotEmpty,
);
if (textNodes.isNotEmpty) {
items.add(
TemplateItem(
id: id++,
content: textNodes.first.text,
xPath: currentPath,
value: textNodes.first.text,
nodeType: NodeType.text,
),
);
}
// 3.
for (final child in element.childElements) {
traverse(child, '$currentPath/${child.name.local}');
}
}
traverse(document.rootElement, document.rootElement.localName);
return items;
}
//-----------------------------
//
List<TemplateNode> _buildTreeNodes(
xml.XmlElement element,
String path, {
@ -201,40 +149,77 @@ class TemplateParserController extends BaseContentController { @@ -201,40 +149,77 @@ class TemplateParserController extends BaseContentController {
return [node];
}
@override
void onOpenFile(String filePath) {
setFilePath(filePath);
}
//
List<TemplateItem> _parseAllNodeValues(xml.XmlDocument document) {
final items = <TemplateItem>[];
int id = 0;
@override
void dispose() {
_treeNodes.clear();
_templateItems.clear();
super.dispose();
}
void traverse(xml.XmlElement element, String currentPath, int index) {
//
for (final attr in element.attributes) {
items.add(
TemplateItem(
id: id++,
rowId: "$currentPath/@$index",
content: attr.value,
xPath: '$currentPath/@${attr.name.local}',
value: attr.value,
nodeType: NodeType.attribute,
),
);
}
@override
void onOpenFolder(String folderPath) {
// TODO: implement onOpenFolder
}
//
final textNodes = element.children.whereType<xml.XmlText>();
if (textNodes.isNotEmpty) {
items.add(
TemplateItem(
id: id++,
rowId: "$currentPath/@$index",
content: textNodes.first.text,
xPath: currentPath,
value: textNodes.first.text,
nodeType: NodeType.text,
),
);
}
//
List<TemplateNode> getSelectedNodes() {
List<TemplateNode> selectedNodes = [];
// 3.
final childElements = element.children.whereType<xml.XmlElement>();
final groupedChildren = <String, List<xml.XmlElement>>{};
void traverse(TemplateNode node) {
if (node.isChecked) {
selectedNodes.add(node);
//
for (var child in childElements) {
groupedChildren.putIfAbsent(child.name.local, () => []).add(child);
}
for (var child in node.children) {
traverse(child);
//
int i = 0;
for (final child in element.childElements) {
traverse(child, '$currentPath/${child.name.local}', i++);
}
}
for (var node in _treeNodes) {
traverse(node);
}
traverse(document.rootElement, document.rootElement.localName, 0);
return items;
}
//-------------
@override
void onOpenFile(String filePath) {
setFilePath(filePath);
}
return selectedNodes;
@override
void onOpenFolder(String folderPath) {
//
}
@override
void dispose() {
treeController.dispose();
filterController.dispose();
gridController.dispose();
super.dispose();
}
}

68
win_text_editor/lib/modules/template_parser/controllers/tree_view_controller.dart

@ -0,0 +1,68 @@ @@ -0,0 +1,68 @@
// tree_view_controller.dart
import 'package:win_text_editor/modules/template_parser/models/template_node.dart';
import 'template_notifier.dart';
class TreeViewController extends TemplateNotifier {
//
List<TemplateNode> _treeNodes = [];
TemplateNode? _selectedNode;
String? _currentParentPath;
List<TemplateNode> get treeNodes => _treeNodes;
TemplateNode? get selectedNode => _selectedNode;
//
void updateTreeNodes(List<TemplateNode> nodes) {
_treeNodes = nodes;
safeNotify();
}
void selectTreeNode(TemplateNode node) {
_selectedNode = node;
safeNotify();
}
// ,
void toggleNodeCheck(TemplateNode node) {
final parentPath = node.path.substring(0, node.path.lastIndexOf('/'));
if (_currentParentPath != null && _currentParentPath != parentPath) {
clearAllChecked();
}
node.isChecked = !node.isChecked;
_currentParentPath = parentPath;
safeNotify();
}
void clearAllChecked() {
void traverse(TemplateNode node) {
node.isChecked = false;
for (var child in node.children) {
traverse(child);
}
}
for (var node in _treeNodes) {
traverse(node);
}
}
List<String> get selectedNodeNames {
List<String> selectedNodeNames = [];
void traverse(TemplateNode node) {
if (node.isChecked) {
selectedNodeNames.add(node.name);
}
for (var child in node.children) {
traverse(child);
}
}
for (var node in _treeNodes) {
traverse(node);
}
return selectedNodeNames;
}
}

11
win_text_editor/lib/modules/template_parser/models/template_node.dart

@ -2,11 +2,16 @@ import 'package:flutter/material.dart'; @@ -2,11 +2,16 @@ import 'package:flutter/material.dart';
import 'package:win_text_editor/shared/components/tree_view.dart';
class TemplateNode implements TreeNode {
@override
final String name;
@override
final List<TemplateNode> children;
@override
final int depth;
final String path;
@override
bool isExpanded;
final String path;
bool isRepeated;
bool isAttribute;
int repreatCount;
@ -15,8 +20,8 @@ class TemplateNode implements TreeNode { @@ -15,8 +20,8 @@ class TemplateNode implements TreeNode {
TemplateNode({
required this.name,
required this.children,
required this.path,
required this.depth,
required this.path,
this.isExpanded = false,
this.isRepeated = false,
this.isAttribute = false,
@ -38,6 +43,7 @@ enum NodeType { element, attribute, text } @@ -38,6 +43,7 @@ enum NodeType { element, attribute, text }
class TemplateItem {
final int id;
final String rowId;
final String content;
final String xPath;
final String value;
@ -45,6 +51,7 @@ class TemplateItem { @@ -45,6 +51,7 @@ class TemplateItem {
TemplateItem({
required this.id,
required this.rowId,
required this.content,
required this.xPath,
required this.value,

140
win_text_editor/lib/modules/template_parser/widgets/template_filter_panel.dart

@ -0,0 +1,140 @@ @@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:win_text_editor/modules/template_parser/controllers/filter_controller.dart';
class TemplateFilterPanel extends StatefulWidget {
final FilterController controller;
const TemplateFilterPanel({super.key, required this.controller});
@override
State<TemplateFilterPanel> createState() => _TemplateFilterPanelState();
}
class _TemplateFilterPanelState extends State<TemplateFilterPanel> {
late TextEditingController _textController;
@override
void initState() {
super.initState();
_textController = TextEditingController(text: widget.controller.filterValue);
}
@override
void didUpdateWidget(covariant TemplateFilterPanel oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.controller.filterValue != _textController.text) {
_textController.text = widget.controller.filterValue;
}
}
@override
void dispose() {
_textController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Consumer<FilterController>(
builder: (context, controller, _) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
children: [
Expanded(
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: '过滤字段',
border: OutlineInputBorder(),
),
items:
controller.availableFields.map((String value) {
return DropdownMenuItem<String>(value: value, child: Text(value));
}).toList(),
onChanged: (value) {
controller.selectedFilterField = value;
},
value: controller.selectedFilterField,
),
),
const SizedBox(width: 16),
SizedBox(
width: 120,
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: '匹配方式',
border: OutlineInputBorder(),
),
items: const [
DropdownMenuItem(value: '==', child: Text('==')),
DropdownMenuItem(value: '>', child: Text('>')),
DropdownMenuItem(value: '<', child: Text('<')),
DropdownMenuItem(value: 'between', child: Text('between')),
DropdownMenuItem(value: 'in', child: Text('in')),
],
onChanged: (value) {
controller.selectedFilterOperator = value;
},
value: controller.selectedFilterOperator,
),
),
],
),
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: SizedBox(
height: 190,
child: TextField(
maxLines: null,
expands: true,
decoration: const InputDecoration(
labelText: '过滤条件值',
border: OutlineInputBorder(),
alignLabelWithHint: true,
),
controller: _textController,
onChanged: (value) {
widget.controller.filterValue = value;
},
),
),
),
const SizedBox(height: 16),
//
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(
onPressed:
controller.selectedFilterField != null &&
controller.selectedFilterOperator != null &&
controller.isFilterValid
? () => controller.applyFilter()
: null,
child: const Text('过滤'),
),
const SizedBox(width: 16),
ElevatedButton(
onPressed: () {
controller.applyFilter();
},
child: const Text('刷新'),
),
],
),
),
],
);
},
);
}
}

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

@ -1,20 +1,19 @@ @@ -1,20 +1,19 @@
import 'package:flutter/material.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/modules/template_parser/controllers/template_parser_controller.dart';
import 'package:win_text_editor/modules/template_parser/controllers/grid_view_controller.dart';
import 'package:win_text_editor/modules/template_parser/models/template_node.dart';
import 'package:file_picker/file_picker.dart';
import 'dart:io';
class TemplateGridView extends StatelessWidget {
final TemplateParserController controller;
final GridViewController controller;
const TemplateGridView({super.key, required this.controller});
@override
Widget build(BuildContext context) {
return Consumer<TemplateParserController>(
return Consumer<GridViewController>(
builder: (context, controller, _) {
return GestureDetector(
onSecondaryTapDown: (details) {
@ -29,7 +28,7 @@ class TemplateGridView extends StatelessWidget { @@ -29,7 +28,7 @@ class TemplateGridView extends StatelessWidget {
Future<void> _showContextMenu(
BuildContext context,
Offset position,
TemplateParserController controller,
GridViewController controller,
) async {
final renderBox = context.findRenderObject() as RenderBox;
final localPosition = renderBox.globalToLocal(position);
@ -58,7 +57,7 @@ class TemplateGridView extends StatelessWidget { @@ -58,7 +57,7 @@ class TemplateGridView extends StatelessWidget {
}
}
Future<void> _exportToCsv(TemplateParserController controller) async {
Future<void> _exportToCsv(GridViewController controller) async {
final selectedNodes = controller.getSelectedNodes();
if (selectedNodes.isEmpty) return;
@ -93,7 +92,7 @@ class TemplateGridView extends StatelessWidget { @@ -93,7 +92,7 @@ class TemplateGridView extends StatelessWidget {
}
}
Widget _buildGridView(TemplateParserController controller) {
Widget _buildGridView(GridViewController controller) {
final selectedNodes = controller.getSelectedNodes();
if (selectedNodes.isEmpty) {
@ -101,7 +100,7 @@ class TemplateGridView extends StatelessWidget { @@ -101,7 +100,7 @@ class TemplateGridView extends StatelessWidget {
}
//
final allItems = controller.templateItems;
final allItems = controller.displayedItems;
// -
final rows = _buildDataRows(selectedNodes, allItems);
@ -146,63 +145,29 @@ class TemplateGridView extends StatelessWidget { @@ -146,63 +145,29 @@ class TemplateGridView extends StatelessWidget {
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;
final instanceMap = <String, Map<String, dynamic>>{};
// 1.
for (final item in allItems) {
// 2.
if (selectedNodes.any((n) => n.path == item.xPath)) {
final instanceId = item.rowId; // 使
instanceMap.putIfAbsent(instanceId, () => {'_index': instanceMap.length + 1});
instanceMap[instanceId]![item.xPath] = item.value;
}
}
// 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};
//
// 3.
return instanceMap.values.map((row) {
for (final node in selectedNodes) {
final values = nodeValueGroups[node.path]!;
row[node.path] = rowIndex < values.length ? values[rowIndex].value : '';
row.putIfAbsent(node.path, () => row[node.path] ?? '');
}
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();
return row;
}).toList();
}
//
List<Map<String, String>> _getGroupedData(TemplateParserController controller) {
List<Map<String, String>> _getGroupedData(GridViewController controller) {
final selectedNodes = controller.getSelectedNodes();
if (selectedNodes.isEmpty) return [];

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

@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.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/widgets/template_filter_panel.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';
@ -32,8 +33,13 @@ class _TemplateParserViewState extends State<TemplateParserView> { @@ -32,8 +33,13 @@ class _TemplateParserViewState extends State<TemplateParserView> {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: _controller,
return MultiProvider(
providers: [
ChangeNotifierProvider.value(value: _controller),
ChangeNotifierProvider.value(value: _controller.treeController),
ChangeNotifierProvider.value(value: _controller.filterController),
ChangeNotifierProvider.value(value: _controller.gridController),
],
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
@ -79,10 +85,21 @@ class _TemplateParserViewState extends State<TemplateParserView> { @@ -79,10 +85,21 @@ class _TemplateParserViewState extends State<TemplateParserView> {
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.3,
child: Card(child: TemplateTreeView(controller: controller)),
child: Column(
children: [
const Expanded(flex: 2, child: Card(child: TemplateTreeView())),
const SizedBox(height: 8),
SizedBox(
height: 340,
child: Card(
child: TemplateFilterPanel(controller: controller.filterController),
),
),
],
),
),
const SizedBox(width: 8),
Expanded(child: Card(child: TemplateGridView(controller: controller))),
Expanded(child: Card(child: TemplateGridView(controller: controller.gridController))),
],
);
},

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

@ -1,24 +1,22 @@ @@ -1,24 +1,22 @@
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/controllers/tree_view_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});
const TemplateTreeView({super.key});
@override
Widget build(BuildContext context) {
return Consumer<TemplateParserController>(
return Consumer<TreeViewController>(
builder: (context, controller, _) {
if (controller.treeNodes.isEmpty) {
return const Center(child: Text('No XML data available'));
}
return TreeView(
nodes: _processXmlNodes(controller.treeNodes),
nodes: controller.treeNodes,
config: const TreeViewConfig(
showIcons: true,
singleSelect: true,
@ -26,8 +24,7 @@ class TemplateTreeView extends StatelessWidget { @@ -26,8 +24,7 @@ class TemplateTreeView extends StatelessWidget {
icons: {'element': Icons.label_outline, 'attribute': Icons.code},
),
onNodeTap: (node) {
final templateNode = node as TemplateNode;
controller.selectTreeNode(templateNode);
controller.selectTreeNode;
},
nodeBuilder: (context, node, isSelected, onTap) {
return _buildTreeNode(node, isSelected, onTap, controller);
@ -37,15 +34,11 @@ class TemplateTreeView extends StatelessWidget { @@ -37,15 +34,11 @@ class TemplateTreeView extends StatelessWidget {
);
}
List<TreeNode> _processXmlNodes(List<TemplateNode> nodes) {
return nodes.cast<TreeNode>();
}
Widget _buildTreeNode(
TreeNode node,
bool isSelected,
VoidCallback onTap,
TemplateParserController controller,
TreeViewController controller,
) {
final templateNode = node as TemplateNode;
final isAttribute = node.isAttribute;

Loading…
Cancel
Save