diff --git a/win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart b/win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart index 49cab26..3a9982b 100644 --- a/win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart +++ b/win_text_editor/lib/modules/template_parser/controllers/template_parser_controller.dart @@ -37,32 +37,9 @@ class TemplateParserController extends BaseContentController { void selectTreeNode(TemplateNode node) { _selectedNode = node; - _templateItems = _generateTemplateItems(node); notifyListeners(); } - List _generateTemplateItems(TemplateNode node) { - // 实际项目中应从XML解析真实数据 - return [ - if (node.attributes != null) - ...node.attributes!.entries.map( - (e) => TemplateItem( - id: e.key.hashCode, - content: '${node.name}.${e.key}', - xPath: '${node.name}@${e.key}', - value: e.value, - ), - ), - if (node.text != null && node.text!.trim().isNotEmpty) - TemplateItem( - id: node.text.hashCode, - content: node.text!, - xPath: '${node.name}/text()', - value: node.text!, - ), - ]; - } - Future _loadTemplateData() async { try { _errorMessage = null; @@ -85,36 +62,62 @@ class TemplateParserController extends BaseContentController { Future _parseXmlContent(String xmlContent) async { final document = xml.XmlDocument.parse(xmlContent); + Logger().debug('开始解析XML,根元素: ${document.rootElement.name}'); _treeNodes = _buildTreeNodes(document.rootElement, depth: 0); + Logger().debug('解析完成,共生成 ${_treeNodes.length} 个顶级节点'); + notifyListeners(); } - List _buildTreeNodes(xml.XmlElement element, {required int depth}) { + List _buildTreeNodes( + xml.XmlElement element, { + required int depth, + int repreatCount = 1, + }) { final node = TemplateNode( name: element.name.local, children: [], - attributes: - element.attributes.isNotEmpty - ? {for (var attr in element.attributes) attr.name.local: attr.value} - : null, - text: element.text.trim().isNotEmpty ? element.text : null, depth: depth, - isExpanded: depth < 1, // 默认展开第一层 - ); - - // 处理子元素 - node.children.addAll( - element.children.whereType().map( - (e) => _buildTreeNodes(e, depth: depth + 1).first, - ), + isExpanded: depth < 5, // 默认展开前两层 + isRepeated: repreatCount > 1, + repreatCount: repreatCount, ); - // 添加属性节点 + // 添加当前元素的所有属性节点 if (element.attributes.isNotEmpty) { node.children.addAll( - element.attributes.map((attr) => TemplateNode.attribute(attr.name.local, attr.value)), + element.attributes.map( + (attr) => TemplateNode( + name: '@${attr.name.local}', + children: [], + depth: depth + 1, + isAttribute: true, + ), + ), ); } + // 处理子元素节点(忽略文本节点) + final childElements = element.children.whereType(); + final groupedChildren = >{}; + + // 按元素名分组 + for (var child in childElements) { + groupedChildren.putIfAbsent(child.name.local, () => []).add(child); + } + + // 为每个唯一子元素创建节点 + groupedChildren.forEach((name, elements) { + if (elements.length == 1) { + // 单一节点直接添加(包含其所有属性) + node.children.addAll(_buildTreeNodes(elements.first, depth: depth + 1)); + } else { + // 多个相同节点需要合并 + node.children.addAll( + _buildTreeNodes(elements.first, depth: depth + 1, repreatCount: elements.length), + ); + } + }); + return [node]; } diff --git a/win_text_editor/lib/modules/template_parser/models/template_node.dart b/win_text_editor/lib/modules/template_parser/models/template_node.dart index 1b84e45..836af75 100644 --- a/win_text_editor/lib/modules/template_parser/models/template_node.dart +++ b/win_text_editor/lib/modules/template_parser/models/template_node.dart @@ -2,57 +2,33 @@ import 'package:flutter/material.dart'; import 'package:win_text_editor/shared/components/tree_view.dart'; class TemplateNode implements TreeNode { - @override - final String id; // 使用路径或唯一标识作为ID - @override final String name; - @override final List children; - final Map? attributes; - final String? text; - - @override - final int depth; // 节点深度 - @override - bool isExpanded; // 是否展开 - bool isRepeated; // 是否重复节点 - - @override - bool get isDirectory => children.isNotEmpty; // 有子节点即为目录 - - @override - IconData? get iconData { - if (name.startsWith('@')) return Icons.code; // 属性节点 - return isDirectory ? Icons.folder : Icons.insert_drive_file; // 元素节点 - } + final int depth; + bool isExpanded; + bool isRepeated; + bool isAttribute; + int repreatCount; TemplateNode({ required this.name, required this.children, - this.attributes, - this.text, - this.depth = 0, + required this.depth, this.isExpanded = false, this.isRepeated = false, - String? id, - }) : id = id ?? '${depth}_${name}'; // 默认ID生成逻辑 + this.isAttribute = false, + this.repreatCount = 1, + }); + + // 实现TreeNode接口 + @override + String get id => '$name-$depth'; - // 转换为属性节点 - TemplateNode.attribute(String name, String value) - : this(name: '@$name', children: const [], attributes: {name: value}, depth: 1); + @override + bool get isDirectory => children.isNotEmpty; - // 克隆方法用于生成子节点 - TemplateNode copyWith({int? depth, bool? isExpanded}) { - return TemplateNode( - name: name, - children: children, - attributes: attributes, - text: text, - depth: depth ?? this.depth, - isExpanded: isExpanded ?? this.isExpanded, - id: id, - ); - } + @override + IconData? get iconData => isAttribute ? Icons.code : Icons.label_outline; } class TemplateItem { diff --git a/win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart b/win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart index df2afd3..7f2374a 100644 --- a/win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart +++ b/win_text_editor/lib/modules/template_parser/widgets/template_parser_view.dart @@ -111,32 +111,17 @@ class _TemplateParserViewState extends State { } List _processXmlNodes(List nodes) { - final uniqueNodes = {}; - final result = []; - - for (final node in nodes) { - // 生成结构签名(节点名+属性名组合,忽略值) - final signature = '${node.name}:${node.attributes?.keys.join(',') ?? ''}'; - - if (!uniqueNodes.containsKey(signature)) { - uniqueNodes[signature] = node; - result.add(node); - } else { - // 标记为重复节点(可选) - uniqueNodes[signature]!.isRepeated = true; - } - } - return result.cast(); + return nodes.cast(); } Widget _buildCustomNode(TreeNode node) { final templateNode = node as TemplateNode; - final isAttribute = node.depth > 0 && node.name.startsWith('@'); + final isAttribute = node.isAttribute; return Consumer( builder: (context, controller, _) { return Padding( - padding: EdgeInsets.only(left: 12.0 * node.depth), // 动态缩进 + padding: EdgeInsets.only(left: 12.0 * node.depth), child: ListTile( dense: true, leading: @@ -152,7 +137,10 @@ class _TemplateParserViewState extends State { ), trailing: templateNode.isRepeated - ? const Text("(repeated)", style: TextStyle(color: Colors.grey)) + ? Text( + "(${templateNode.repreatCount.toString()})", + style: const TextStyle(color: Colors.grey), + ) : null, onTap: () => controller.selectTreeNode(templateNode), ),