From 289f22ec2c52316c6da227126a042a213a45a352 Mon Sep 17 00:00:00 2001 From: hejl Date: Thu, 29 May 2025 10:37:47 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E8=A7=A3=E6=9E=90OK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/config/uft_macro_list.yaml | 4 +- .../framework/controllers/file_provider.dart | 24 +++---- .../lib/framework/models/file_node.dart | 8 +-- .../lib/framework/services/file_service.dart | 60 +++++++++-------- .../lib/shared/components/file_explorer.dart | 65 +++++++------------ .../lib/shared/components/tree_view.dart | 48 +++++++------- 6 files changed, 92 insertions(+), 117 deletions(-) diff --git a/win_text_editor/assets/config/uft_macro_list.yaml b/win_text_editor/assets/config/uft_macro_list.yaml index 9f5219e..9fa7708 100644 --- a/win_text_editor/assets/config/uft_macro_list.yaml +++ b/win_text_editor/assets/config/uft_macro_list.yaml @@ -35,13 +35,13 @@ templates: [获取记录][{{tableName}}({{keyName}})][ {{#keyFields}} - {{value}} = @{{value}} {{^isLast}}, {{/isLast}} + {{value}} = @{{value}} {{/keyFields}} ] [修改记录][{{tableName}}][ {{#fields}} - {{value}} = @{{value}} {{^isLast}}, {{/isLast}} + {{value}} = @{{value}} {{/fields}} ] } \ No newline at end of file diff --git a/win_text_editor/lib/framework/controllers/file_provider.dart b/win_text_editor/lib/framework/controllers/file_provider.dart index e87167a..1830dab 100644 --- a/win_text_editor/lib/framework/controllers/file_provider.dart +++ b/win_text_editor/lib/framework/controllers/file_provider.dart @@ -60,14 +60,15 @@ class FileProvider with ChangeNotifier { try { final directory = Directory(path); + final displayName = await FileService.getModuleDisplayName(directory.path); final rootNode = FileNode( - name: directory.path.split(Platform.pathSeparator).last, + name: displayName ?? directory.path.split(Platform.pathSeparator).last, path: directory.path, isDirectory: true, - isRoot: true, // 添加根节点标识 + isRoot: true, children: await FileService.buildFileTree(directory.path), ); - _fileNodes = [rootNode]; // 将根节点作为唯一顶层节点 + _fileNodes = [rootNode]; } catch (e) { Logger().error('Error loading directory: $e'); _fileNodes = []; @@ -84,9 +85,10 @@ class FileProvider with ChangeNotifier { notifyListeners(); try { + final displayName = await FileService.getModuleDisplayName(_currentRootPath!); _fileNodes = [ FileNode( - name: _currentRootPath!.split(Platform.pathSeparator).last, + name: displayName ?? _currentRootPath!.split(Platform.pathSeparator).last, path: _currentRootPath!, isDirectory: true, isRoot: true, @@ -108,9 +110,10 @@ class FileProvider with ChangeNotifier { notifyListeners(); try { + final displayName = await FileService.getModuleDisplayName(path); _fileNodes = [ FileNode( - name: path.split(Platform.pathSeparator).last, + name: displayName ?? path.split(Platform.pathSeparator).last, path: path, isDirectory: true, isRoot: true, @@ -161,16 +164,9 @@ class FileProvider with ChangeNotifier { notifyListeners(); try { - final contents = await FileService.listDirectory( - dirNode.path, - parentDepth: dirNode.depth, // 确保传递父节点深度 - ); + final contents = await FileService.listDirectory(dirNode.path, parentDepth: dirNode.depth); - final updatedNode = dirNode.copyWith( - children: contents, - isExpanded: true, - // 不需要设置 depth,因为 copyWith 会自动保留原值 - ); + final updatedNode = dirNode.copyWith(children: contents, isExpanded: true); _replaceNodeInTree(dirNode, updatedNode); } catch (e) { diff --git a/win_text_editor/lib/framework/models/file_node.dart b/win_text_editor/lib/framework/models/file_node.dart index 960b174..9aa50df 100644 --- a/win_text_editor/lib/framework/models/file_node.dart +++ b/win_text_editor/lib/framework/models/file_node.dart @@ -97,19 +97,19 @@ class FileNode implements TreeNode { String? name, String? path, bool? isDirectory, + bool? isExpanded, bool? isRoot, - int? depth, // 添加depth参数 List? children, - bool? isExpanded, + int? depth, }) { return FileNode( name: name ?? this.name, path: path ?? this.path, isDirectory: isDirectory ?? this.isDirectory, + isExpanded: isExpanded ?? this.isExpanded, isRoot: isRoot ?? this.isRoot, - depth: depth ?? this.depth, // 保留原有depth或使用新值 children: children ?? this.children, - isExpanded: isExpanded ?? this.isExpanded, + depth: depth ?? this.depth, ); } diff --git a/win_text_editor/lib/framework/services/file_service.dart b/win_text_editor/lib/framework/services/file_service.dart index 76359e3..0e8a06b 100644 --- a/win_text_editor/lib/framework/services/file_service.dart +++ b/win_text_editor/lib/framework/services/file_service.dart @@ -1,42 +1,46 @@ import 'dart:io'; +import 'package:win_text_editor/framework/controllers/logger.dart'; import 'package:win_text_editor/framework/models/file_node.dart'; +import 'package:xml/xml.dart'; class FileService { /// 延时加载目录内容(不递归) static Future> listDirectory(String path, {int parentDepth = 0}) async { - final directory = Directory(path); + final dir = Directory(path); + final List entities = await dir.list().toList(); final List nodes = []; - if (await directory.exists()) { - final entities = directory.listSync(); - - // 遍历目录实体并创建FileNode for (final entity in entities) { - nodes.add( - FileNode( - name: entity.path.split(Platform.pathSeparator).last, - path: entity.path, - isDirectory: entity is Directory, - depth: parentDepth + 1, - children: [], - ), - ); - } + final isDirectory = await FileSystemEntity.isDirectory(entity.path); + final displayName = isDirectory + ? await getModuleDisplayName(entity.path) + : null; + + nodes.add(FileNode( + name: displayName ?? entity.path.split(Platform.pathSeparator).last, + path: entity.path, + isDirectory: isDirectory, + depth: parentDepth + 1, + )); + } - // 排序:文件夹在前按字母排序,文件在后按字母排序 - nodes.sort((a, b) { - if (a.isDirectory && !b.isDirectory) { - return -1; // a是文件夹,b是文件,a排在前面 - } else if (!a.isDirectory && b.isDirectory) { - return 1; // a是文件,b是文件夹,b排在前面 - } else { - // 都是文件夹或都是文件时,按名称字母排序 - return a.name.compareTo(b.name); - } - }); - } + return nodes; +} - return nodes; + + static Future getModuleDisplayName(String dirPath) async { + try { + final moduleFile = File('$dirPath${Platform.pathSeparator}module.xml'); + if (await moduleFile.exists()) { + final content = await moduleFile.readAsString(); + final xmlDoc = XmlDocument.parse(content); + final infoNode = xmlDoc.findAllElements('info').firstOrNull; + return infoNode?.getAttribute('cname'); + } + } catch (e) { + Logger().debug('Error reading module.xml: $e'); + } + return null; } /// 递归构建完整文件树(原方法保留备用) diff --git a/win_text_editor/lib/shared/components/file_explorer.dart b/win_text_editor/lib/shared/components/file_explorer.dart index 1d68a56..08ee963 100644 --- a/win_text_editor/lib/shared/components/file_explorer.dart +++ b/win_text_editor/lib/shared/components/file_explorer.dart @@ -1,9 +1,7 @@ import 'dart:math'; -import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -import 'package:win_text_editor/framework/services/file_path_manager.dart'; import '../../framework/models/file_node.dart'; import '../../framework/controllers/file_provider.dart'; @@ -20,32 +18,18 @@ class FileExplorer extends StatefulWidget { } class _FileExplorerState extends State { - // 移除所有 ScrollController + final ScrollController _scrollController = ScrollController(); // 添加ScrollController - Future _promptForDirectory(BuildContext context) async { - final fileProvider = Provider.of(context, listen: false); - final String? selectedDirectory = await FilePicker.platform.getDirectoryPath(); - - if (selectedDirectory != null) { - await FilePathManager.saveLastOpenedFolder(selectedDirectory); - await fileProvider.setRootPath(selectedDirectory); - } - } - - Widget _buildEmptyPrompt(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('没有文件可显示'), - TextButton(onPressed: () => _promptForDirectory(context), child: const Text('打开目录')), - ], - ); + @override + void dispose() { + _scrollController.dispose(); // 记得销毁controller + super.dispose(); } // 动态计算总宽度(根据层级深度调整) double calculateTotalWidth(BuildContext context, FileProvider fileProvider) { final maxDepth = _getMaxDepth(fileProvider.fileNodes); - return maxDepth * 200 + MediaQuery.of(context).size.width * 0.5; + return maxDepth * 60 + MediaQuery.of(context).size.width * 0.2; } int _getMaxDepth(List nodes) { @@ -62,29 +46,24 @@ class _FileExplorerState extends State { Widget build(BuildContext context) { final fileProvider = Provider.of(context); - return Container( - color: Colors.white, - child: Column( - children: [ - Expanded( - child: ClipRect( - child: - fileProvider.isLoading - ? const Center(child: CircularProgressIndicator()) - : fileProvider.fileNodes.isEmpty - ? Center(child: _buildEmptyPrompt(context)) - : SizedBox( - width: calculateTotalWidth(context, fileProvider), - child: TreeView( - nodes: fileProvider.fileNodes, - config: const TreeViewConfig(showIcons: true, lazyLoad: true), - onNodeTap: (node) => _handleNodeTap(context, node as FileNode), - onNodeDoubleTap: (node) => _handleNodeDoubleTap(node as FileNode), - ), - ), + return Scrollbar( + controller: _scrollController, // 指定controller + thumbVisibility: true, + child: SingleChildScrollView( + controller: _scrollController, // 使用同一个controller + scrollDirection: Axis.horizontal, + child: Container( + color: Colors.white, + child: SizedBox( + width: calculateTotalWidth(context, fileProvider), + child: TreeView( + nodes: fileProvider.fileNodes, + config: const TreeViewConfig(showIcons: true, lazyLoad: true), + onNodeTap: (node) => _handleNodeTap(context, node as FileNode), + onNodeDoubleTap: (node) => _handleNodeDoubleTap(node as FileNode), ), ), - ], + ), ), ); } diff --git a/win_text_editor/lib/shared/components/tree_view.dart b/win_text_editor/lib/shared/components/tree_view.dart index e907532..00eb3f2 100644 --- a/win_text_editor/lib/shared/components/tree_view.dart +++ b/win_text_editor/lib/shared/components/tree_view.dart @@ -89,37 +89,33 @@ class _TreeViewState extends State { @override Widget build(BuildContext context) { - return Scrollbar( + return ListView.builder( controller: _effectiveController, - thumbVisibility: true, - notificationPredicate: (_) => true, // 确保接收所有滚动通知 - child: ListView.builder( - controller: _effectiveController, - physics: const ClampingScrollPhysics(), - itemCount: _countVisibleNodes(widget.nodes), - itemBuilder: (context, index) { - final node = _getVisibleNode(widget.nodes, index); - final isSelected = _selectedIds.contains(node.id); - - // 使用自定义构建器或默认构建器 - return widget.nodeBuilder != null - ? widget.nodeBuilder!(context, node, isSelected, () => _handleNodeTap(node)) - : TreeNodeWidget( - node: node, - config: widget.config, - isSelected: isSelected, - isChecked: _checkedIds.contains(node.id), - onTap: () => _handleNodeTap(node), - onDoubleTap: () => widget.onNodeDoubleTap?.call(node), - onCheckChanged: (value) => _handleNodeCheckChanged(node, value), - ); - }, - ), + physics: const ClampingScrollPhysics(), + itemCount: _countVisibleNodes(widget.nodes), + itemBuilder: (context, index) { + final node = _getVisibleNode(widget.nodes, index); + final isSelected = _selectedIds.contains(node.id); + + // 使用自定义构建器或默认构建器 + return widget.nodeBuilder != null + ? widget.nodeBuilder!(context, node, isSelected, () => _handleNodeTap(node)) + : TreeNodeWidget( + node: node, + config: widget.config, + isSelected: isSelected, + isChecked: _checkedIds.contains(node.id), + onTap: () => _handleNodeTap(node), + onDoubleTap: () => widget.onNodeDoubleTap?.call(node), + onCheckChanged: (value) => _handleNodeCheckChanged(node, value), + ); + }, ); } void _handleNodeTap(TreeNode node) { - if (widget.config.singleSelect && !node.isDirectory) { // 只处理叶子节点的单选逻辑 + if (widget.config.singleSelect && !node.isDirectory) { + // 只处理叶子节点的单选逻辑 setState(() { _selectedIds.clear(); _selectedIds.add(node.id);