From 18493fde87db5e0ee74488461bb4a141caea763a Mon Sep 17 00:00:00 2001 From: hejl Date: Tue, 27 May 2025 17:24:09 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E9=80=89=E6=8B=A9=E5=B7=AE?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E8=81=94=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/controllers/file_provider.dart | 2 + .../controllers/tab_items_controller.dart | 33 ++- .../framework/services/file_path_manager.dart | 21 ++ .../framework/widgets/file_explorer_pane.dart | 16 ++ win_text_editor/lib/main.dart | 4 +- win_text_editor/lib/menus/app_menu.dart | 10 +- win_text_editor/lib/menus/menu_actions.dart | 16 +- win_text_editor/lib/menus/menu_constants.dart | 5 +- .../controllers/memory_table_controller.dart | 229 ++++++++++++++++ .../services/memory_table_service.dart | 123 +++++++++ .../widgets/memory_table_left_side.dart | 256 ++++++++++++++++++ .../widgets/memory_table_right_side.dart | 106 ++++++++ .../widgets/memory_table_view.dart | 69 +++++ .../lib/modules/module_router.dart | 10 +- .../controllers/tree_view_controller.dart | 68 ----- .../controllers/uft_file_controller.dart | 145 ---------- .../modules/uft_file/widgets/tree_view.dart | 89 ------ .../uft_file/widgets/uft_file_view.dart | 112 -------- .../lib/shared/components/file_explorer.dart | 2 + win_text_editor/pubspec.lock | 184 +++++++++---- win_text_editor/pubspec.yaml | 1 + 21 files changed, 1000 insertions(+), 501 deletions(-) create mode 100644 win_text_editor/lib/framework/services/file_path_manager.dart create mode 100644 win_text_editor/lib/modules/memory_table/controllers/memory_table_controller.dart create mode 100644 win_text_editor/lib/modules/memory_table/services/memory_table_service.dart create mode 100644 win_text_editor/lib/modules/memory_table/widgets/memory_table_left_side.dart create mode 100644 win_text_editor/lib/modules/memory_table/widgets/memory_table_right_side.dart create mode 100644 win_text_editor/lib/modules/memory_table/widgets/memory_table_view.dart delete mode 100644 win_text_editor/lib/modules/uft_file/controllers/tree_view_controller.dart delete mode 100644 win_text_editor/lib/modules/uft_file/controllers/uft_file_controller.dart delete mode 100644 win_text_editor/lib/modules/uft_file/widgets/tree_view.dart delete mode 100644 win_text_editor/lib/modules/uft_file/widgets/uft_file_view.dart diff --git a/win_text_editor/lib/framework/controllers/file_provider.dart b/win_text_editor/lib/framework/controllers/file_provider.dart index 7764b53..e87167a 100644 --- a/win_text_editor/lib/framework/controllers/file_provider.dart +++ b/win_text_editor/lib/framework/controllers/file_provider.dart @@ -18,6 +18,8 @@ class FileProvider with ChangeNotifier { // 移除构造函数的_initFileTree调用 FileProvider(); + String? get rootPath => _currentRootPath; + // 新增方法:手动设置根路径 Future setRootPath(String path) async { _currentRootPath = path; diff --git a/win_text_editor/lib/framework/controllers/tab_items_controller.dart b/win_text_editor/lib/framework/controllers/tab_items_controller.dart index 909c139..712a602 100644 --- a/win_text_editor/lib/framework/controllers/tab_items_controller.dart +++ b/win_text_editor/lib/framework/controllers/tab_items_controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_js/quickjs/ffi.dart'; import 'package:win_text_editor/framework/models/tab_model.dart'; import 'package:win_text_editor/modules/module_router.dart'; import 'package:win_text_editor/shared/base/base_content_controller.dart'; @@ -58,7 +59,6 @@ class TabItemsController with ChangeNotifier { BaseContentController? get activeContentController { if (_activeTabId == null) { - Logger().error("没有活动的选项卡ID", source: 'TabItemsController'); return null; } final controller = _contentControllers[_activeTabId]; @@ -111,7 +111,15 @@ class TabItemsController with ChangeNotifier { void handleFileDoubleTap(String filePath) { if (activeContentController == null) { - Logger().error("没有活动的内容控制器", source: 'TabItemsController'); + final fileExtension = _getFileExtension(filePath); + switch (fileExtension) { + case 'uftstructure': + openOrActivateTab("内存表", RouterKey.memoryTable, Icons.drive_file_move); + break; + default: + Logger().error("没有活动的内容控制器", source: 'TabItemsController'); + } + return; } activeContentController?.onOpenFile(filePath); @@ -120,4 +128,25 @@ class TabItemsController with ChangeNotifier { bool hasController(String tabId) { return _contentControllers.containsKey(tabId); } + + // 新增方法 + Future openOrActivateTab(String title, String type, IconData icon) async { + try { + final existingTab = _tabs.firstWhereOrNull((tab) => tab.type == type); + + if (existingTab != null) { + setActiveTab(existingTab.id); + } else { + final tabId = DateTime.now().millisecondsSinceEpoch.toString(); + await addTab(tabId, title: title, type: type, icon: icon, content: ""); + } + } catch (e) { + Logger().error("打开或激活标签页失败: $e", source: 'TabItemsController'); + } + } + + static String _getFileExtension(String filePath) { + final parts = filePath.split('.'); + return parts.length > 1 ? parts.last.toLowerCase() : ''; + } } diff --git a/win_text_editor/lib/framework/services/file_path_manager.dart b/win_text_editor/lib/framework/services/file_path_manager.dart new file mode 100644 index 0000000..e0ff3a6 --- /dev/null +++ b/win_text_editor/lib/framework/services/file_path_manager.dart @@ -0,0 +1,21 @@ +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:win_text_editor/framework/controllers/logger.dart'; + +class FilePathManager { + static const String _lastOpenedFolderKey = 'last_opened_folder'; + + // 保存上次打开的文件夹路径 + static Future saveLastOpenedFolder(String folderPath) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setString(_lastOpenedFolderKey, folderPath); + Logger().info("保存最新打开的文件夹地址:$folderPath"); + } + + // 读取上次打开的文件夹路径 + static Future getLastOpenedFolder() async { + final prefs = await SharedPreferences.getInstance(); + final folderPath = prefs.getString(_lastOpenedFolderKey); + Logger().info("加载最后保存的文件夹地址:$folderPath"); + return folderPath; + } +} diff --git a/win_text_editor/lib/framework/widgets/file_explorer_pane.dart b/win_text_editor/lib/framework/widgets/file_explorer_pane.dart index 1e5f15e..73af2a7 100644 --- a/win_text_editor/lib/framework/widgets/file_explorer_pane.dart +++ b/win_text_editor/lib/framework/widgets/file_explorer_pane.dart @@ -2,6 +2,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:win_text_editor/framework/controllers/file_provider.dart'; +import 'package:win_text_editor/framework/services/file_path_manager.dart'; import 'package:win_text_editor/shared/components/file_explorer.dart'; class FileExplorerPane extends StatelessWidget { @@ -12,6 +13,9 @@ class FileExplorerPane extends StatelessWidget { @override Widget build(BuildContext context) { + final fileProvider = Provider.of(context, listen: false); + _loadLastOpenedFolder(fileProvider); + return Consumer( builder: (context, fileProvider, child) { return Material( @@ -67,7 +71,19 @@ class FileExplorerPane extends StatelessWidget { 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); } } + + Future _loadLastOpenedFolder(FileProvider fileProvider) async { + if (fileProvider.rootPath != null && fileProvider.rootPath!.isNotEmpty) { + return; + } + + final String? lastOpenedFolder = await FilePathManager.getLastOpenedFolder(); + if (lastOpenedFolder != null) { + await fileProvider.setRootPath(lastOpenedFolder); + } + } } diff --git a/win_text_editor/lib/main.dart b/win_text_editor/lib/main.dart index 4aa506d..8cc0bea 100644 --- a/win_text_editor/lib/main.dart +++ b/win_text_editor/lib/main.dart @@ -12,7 +12,7 @@ void main() async { WindowOptions windowOptions = const WindowOptions( size: Size(1600, 1000), center: true, - title: '文件操作工具箱', + title: '编程辅助工具', ); windowManager.waitUntilReadyToShow(windowOptions, () async { await windowManager.show(); @@ -36,7 +36,7 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( - title: '升级工具', + title: '编程辅助工具', debugShowCheckedModeBanner: false, theme: ThemeData( useMaterial3: true, // 启用 Material 3 设计规范 diff --git a/win_text_editor/lib/menus/app_menu.dart b/win_text_editor/lib/menus/app_menu.dart index 150ab2a..38f64af 100644 --- a/win_text_editor/lib/menus/app_menu.dart +++ b/win_text_editor/lib/menus/app_menu.dart @@ -14,8 +14,8 @@ class AppMenu extends StatelessWidget { child: Row( children: [ _buildMenuButton(context, '文件', _buildFileMenuItems()), - _buildMenuButton(context, '工具', _buildToolsMenuItems()), - _buildMenuButton(context, 'AIGC', _buildAigcMenuItems()), + _buildMenuButton(context, '文本工具', _buildToolsMenuItems()), + _buildMenuButton(context, 'UFT工具', _buildUftToolsMenuItems()), _buildMenuButton(context, '编辑', _buildEditMenuItems()), _buildMenuButton(context, '窗口', _buildWindowMenuItems()), _buildMenuButton(context, '帮助', _buildHelpMenuItems()), @@ -50,11 +50,11 @@ class AppMenu extends StatelessWidget { ]; } - List> _buildAigcMenuItems() { + List> _buildUftToolsMenuItems() { return [ const PopupMenuItem( - value: MenuConstants.uftFile, - child: ListTile(leading: Icon(Icons.drive_file_move), title: Text('UFT文件')), + value: MenuConstants.memoryTable, + child: ListTile(leading: Icon(Icons.list), title: Text('内存表')), ), ]; } diff --git a/win_text_editor/lib/menus/menu_actions.dart b/win_text_editor/lib/menus/menu_actions.dart index 83019f5..17129fc 100644 --- a/win_text_editor/lib/menus/menu_actions.dart +++ b/win_text_editor/lib/menus/menu_actions.dart @@ -4,7 +4,6 @@ import 'package:provider/provider.dart'; import 'package:win_text_editor/framework/controllers/tab_items_controller.dart'; import 'package:win_text_editor/menus/menu_constants.dart'; import 'package:win_text_editor/framework/controllers/file_provider.dart'; -import 'package:collection/collection.dart'; import 'dart:io'; import 'package:win_text_editor/modules/module_router.dart'; @@ -16,7 +15,7 @@ class MenuActions { MenuConstants.templateParser: _openTemplateParser, MenuConstants.dataFormat: _dataFormat, MenuConstants.dataCompare: _dataCompare, - MenuConstants.uftFile: _openUftFile, + MenuConstants.memoryTable: _memoryTable, MenuConstants.demo: _demo, MenuConstants.exit: _exitApplication, }; @@ -48,8 +47,8 @@ class MenuActions { await _openOrActivateTab(context, "XML解析", RouterKey.templateParser, Icons.auto_awesome_mosaic); } - static Future _openUftFile(BuildContext context) async { - await _openOrActivateTab(context, "UFT文件", RouterKey.uftFile, Icons.drive_file_move); + static Future _memoryTable(BuildContext context) async { + await _openOrActivateTab(context, "内存表", RouterKey.memoryTable, Icons.drive_file_move); } static Future _dataFormat(BuildContext context) async { @@ -72,14 +71,7 @@ class MenuActions { ) async { try { final tabManager = Provider.of(context, listen: false); - final existingTab = tabManager.tabs.firstWhereOrNull((tab) => tab.type == type); - - if (existingTab != null) { - tabManager.setActiveTab(existingTab.id); - } else { - final tabId = DateTime.now().millisecondsSinceEpoch.toString(); - await tabManager.addTab(tabId, title: title, type: type, icon: icon, content: ""); - } + await tabManager.openOrActivateTab(title, type, icon); } catch (e) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('打开标签页失败: $e'))); } diff --git a/win_text_editor/lib/menus/menu_constants.dart b/win_text_editor/lib/menus/menu_constants.dart index fa97cd3..47625c9 100644 --- a/win_text_editor/lib/menus/menu_constants.dart +++ b/win_text_editor/lib/menus/menu_constants.dart @@ -21,7 +21,10 @@ class MenuConstants { // AIGC菜单项 static const String aigc = 'aigc'; - static const String uftFile = 'uftFile'; + + //Uft菜单 + static const String memoryTable = 'memory_table'; + static const String uftTools = 'uft_tools'; // 编辑菜单项 static const String undo = 'undo'; diff --git a/win_text_editor/lib/modules/memory_table/controllers/memory_table_controller.dart b/win_text_editor/lib/modules/memory_table/controllers/memory_table_controller.dart new file mode 100644 index 0000000..be38b06 --- /dev/null +++ b/win_text_editor/lib/modules/memory_table/controllers/memory_table_controller.dart @@ -0,0 +1,229 @@ +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_datagrid/datagrid.dart'; +import 'package:win_text_editor/framework/controllers/logger.dart'; +import 'package:win_text_editor/modules/memory_table/services/memory_table_service.dart'; +import 'package:win_text_editor/shared/base/base_content_controller.dart'; + +// 字段数据模型 +class Field { + Field(this.id, this.name, this.chineseName, this.type, this.remark, [this.isSelected = false]); + + final String id; // 序号 + final String name; // 名称 + final String chineseName; // 中文名 + final String type; // 类型 + final String remark; // 备注 + bool isSelected; +} + +// 索引数据模型 +class Index { + Index(this.indexName, this.isPrimary, this.indexFields, this.rule, [this.isSelected = false]); + + final String indexName; // 索引名称 + final String isPrimary; // 是否主键 + final String indexFields; // 索引字段 + final String rule; // 规则 + bool isSelected; +} + +// 字段数据源 +class FieldsDataSource extends DataGridSource { + FieldsDataSource(this.fields); + + List fields; + + void toggleAllSelection(bool? value) { + for (var field in fields) { + field.isSelected = value ?? false; + } + notifyListeners(); + } + + void toggleRowSelection(int index, bool? value) { + fields[index].isSelected = value ?? false; + notifyListeners(); + } + + void updateData(List newFields) { + fields = newFields; + notifyListeners(); // 关键:通知DataGrid更新 + } + + @override + List get rows => + fields + .map( + (field) => DataGridRow( + cells: [ + DataGridCell(columnName: 'select', value: field.isSelected), + DataGridCell(columnName: 'id', value: field.id), + DataGridCell(columnName: 'name', value: field.name), + DataGridCell(columnName: 'chineseName', value: field.chineseName), + DataGridCell(columnName: 'type', value: field.type), + DataGridCell(columnName: 'remark', value: field.remark), + ], + ), + ) + .toList(); + + @override + DataGridRowAdapter? buildRow(DataGridRow row) { + final int rowIndex = effectiveRows.indexOf(row); + return DataGridRowAdapter( + cells: + row.getCells().map((dataGridCell) { + if (dataGridCell.columnName == 'select') { + return Center( + child: Checkbox( + value: fields[rowIndex].isSelected, + onChanged: (value) { + toggleRowSelection(rowIndex, value); + }, + ), + ); + } + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text(dataGridCell.value.toString(), overflow: TextOverflow.ellipsis), + ); + }).toList(), + ); + } +} + +// 索引数据源 +class IndexesDataSource extends DataGridSource { + IndexesDataSource(this.indexes); + + List indexes; + + void toggleAllSelection(bool? value) { + for (var index in indexes) { + index.isSelected = value ?? false; + } + notifyListeners(); + } + + void toggleRowSelection(int index, bool? value) { + indexes[index].isSelected = value ?? false; + notifyListeners(); + } + + void updateData(List newIndexes) { + indexes = newIndexes; + notifyListeners(); // 关键:通知DataGrid更新 + } + + @override + List get rows => + indexes + .map( + (index) => DataGridRow( + cells: [ + DataGridCell(columnName: 'select', value: index.isSelected), + DataGridCell(columnName: 'indexName', value: index.indexName), + DataGridCell(columnName: 'isPrimary', value: index.isPrimary), + DataGridCell(columnName: 'indexFields', value: index.indexFields), + DataGridCell(columnName: 'rule', value: index.rule), + ], + ), + ) + .toList(); + + @override + DataGridRowAdapter? buildRow(DataGridRow row) { + final int rowIndex = effectiveRows.indexOf(row); + return DataGridRowAdapter( + cells: + row.getCells().map((dataGridCell) { + if (dataGridCell.columnName == 'select') { + return Center( + child: Checkbox( + value: indexes[rowIndex].isSelected, + onChanged: (value) { + toggleRowSelection(rowIndex, value); + }, + ), + ); + } + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text(dataGridCell.value.toString(), overflow: TextOverflow.ellipsis), + ); + }).toList(), + ); + } + + bool _getSelectedValue(DataGridRow row) { + final int rowIndex = rows.indexOf(row); + if (rowIndex == -1) { + return false; + } + return indexes[rowIndex].isSelected; + } +} + +class MemoryTableController extends BaseContentController { + String? _errorMessage; + String tableName = ""; + String objectId = ""; + String chineseName = ""; + + late DataGridSource fieldsSource; + late DataGridSource indexesSource; + late MemoryTableService _service; + + MemoryTableController() : _service = MemoryTableService(Logger()) { + // 初始化空数据 + fieldsSource = FieldsDataSource([ + Field('1', '', '', '', ''), // 序号1的空字段 + Field('2', '', '', '', ''), // 序号2的空字段 + Field('3', '', '', '', ''), // 序号3的空字段 + ]); + + indexesSource = IndexesDataSource([ + Index('', '', '', ''), // 空索引1 + Index('', '', '', ''), // 空索引2 + ]); + } + + String? get errorMessage => _errorMessage; + + @override + Future onOpenFile(String filePath) async { + try { + final tableData = await _service.parseStructureFile(filePath); + + // Update controller state + tableName = tableData.tableName; + chineseName = tableData.chineseName; + objectId = tableData.objectId; + + // Update data sources + (fieldsSource as FieldsDataSource).updateData(tableData.fields); + (indexesSource as IndexesDataSource).updateData(tableData.indexes); + + // Clear any previous error + _errorMessage = null; + + // Notify UI to update + notifyListeners(); + } catch (e) { + _errorMessage = e.toString(); + notifyListeners(); + } + } + + @override + void onOpenFolder(String folderPath) { + // 不支持打开文件夹 + } + + @override + void dispose() { + super.dispose(); + } +} diff --git a/win_text_editor/lib/modules/memory_table/services/memory_table_service.dart b/win_text_editor/lib/modules/memory_table/services/memory_table_service.dart new file mode 100644 index 0000000..3b8eedc --- /dev/null +++ b/win_text_editor/lib/modules/memory_table/services/memory_table_service.dart @@ -0,0 +1,123 @@ +// memory_table_service.dart +import 'dart:io'; + +import 'package:win_text_editor/modules/memory_table/controllers/memory_table_controller.dart'; +import 'package:xml/xml.dart' as xml; +import 'package:path/path.dart' as path; +import 'package:win_text_editor/framework/controllers/logger.dart'; + +class MemoryTableService { + final Logger _logger; + + MemoryTableService(this._logger); + + Future parseStructureFile(String filePath) async { + try { + // 1. Check file extension + if (!filePath.toLowerCase().endsWith('.uftstructure')) { + throw const FormatException("文件扩展名必须是.uftstructure"); + } + + // 2. Read and parse file content + final file = File(filePath); + final content = await file.readAsString(); + + final document = xml.XmlDocument.parse(content); + final structureNode = document.findAllElements('structure:Structure').firstOrNull; + + if (structureNode == null) { + throw const FormatException("文件格式错误:缺少structure:Structure节点"); + } + + // 3. Get basic info + final fileNameWithoutExt = path.basenameWithoutExtension(filePath); + final chineseName = structureNode.getAttribute('chineseName') ?? ''; + final objectId = structureNode.getAttribute('objectId') ?? ''; + + // 4. Process properties (fields) + final properties = document.findAllElements('properties'); + final fields = []; + int index = 1; + + for (final property in properties) { + final id = property.getAttribute('id') ?? ''; + fields.add( + Field( + (index++).toString(), // 序号 + id, // 名称 + '', // 中文名 + '', // 类型 + '', // 备注 + ), + ); + } + + // 5. Process indexes + final indexes = document.findAllElements('indexs'); + final indexList = []; + + for (final indexNode in indexes) { + final name = indexNode.getAttribute('name') ?? ''; + final containerType = indexNode.getAttribute('containerType'); + final isPrimary = containerType == null ? '是' : '否'; + final rule = containerType ?? ''; + + // Get all index fields + final items = indexNode.findAllElements('items'); + final fieldsList = + items + .map((item) => item.getAttribute('attrname') ?? '') + .where((f) => f.isNotEmpty) + .toList(); + final indexFields = fieldsList.join(','); + + indexList.add( + Index( + name, // 索引名称 + isPrimary, // 是否主键 + indexFields, // 索引字段 + rule, // 规则 + ), + ); + } + + return TableData( + tableName: fileNameWithoutExt, + chineseName: chineseName, + objectId: objectId, + fields: fields.isNotEmpty ? fields : _getDefaultFields(), + indexes: indexList.isNotEmpty ? indexList : _getDefaultIndexes(), + ); + } on xml.XmlParserException catch (e) { + _logger.error("XML解析错误: ${e.message}"); + rethrow; + } catch (e) { + _logger.error("处理文件时发生错误: $e"); + rethrow; + } + } + + List _getDefaultFields() { + return [Field('1', '', '', '', ''), Field('2', '', '', '', ''), Field('3', '', '', '', '')]; + } + + List _getDefaultIndexes() { + return [Index('', '', '', ''), Index('', '', '', '')]; + } +} + +class TableData { + final String tableName; + final String chineseName; + final String objectId; + final List fields; + final List indexes; + + TableData({ + required this.tableName, + required this.chineseName, + required this.objectId, + required this.fields, + required this.indexes, + }); +} diff --git a/win_text_editor/lib/modules/memory_table/widgets/memory_table_left_side.dart b/win_text_editor/lib/modules/memory_table/widgets/memory_table_left_side.dart new file mode 100644 index 0000000..24ef40e --- /dev/null +++ b/win_text_editor/lib/modules/memory_table/widgets/memory_table_left_side.dart @@ -0,0 +1,256 @@ +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_datagrid/datagrid.dart'; +import 'package:win_text_editor/modules/memory_table/controllers/memory_table_controller.dart'; + +class MemoryTableLeftSide extends StatelessWidget { + final MemoryTableController controller; + const MemoryTableLeftSide({super.key, required this.controller}); + + Widget _buildTextFieldRow(String label, String value) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text('$label:'), + SizedBox( + width: 200, + child: TextField( + controller: TextEditingController(text: value), + readOnly: true, + decoration: const InputDecoration(isDense: true, contentPadding: EdgeInsets.all(8)), + ), + ), + ], + ); + } + + Container _buildGridHeader(String text) { + return Container( + alignment: Alignment.center, + color: Colors.grey[200], + child: Text(text, style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16)), + ); + } + + Widget _buildFieldsDataGrid(MemoryTableController controller) { + final fieldsSource = controller.fieldsSource as FieldsDataSource; + + return Card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SizedBox( + width: constraints.maxWidth > 800 ? constraints.maxWidth : 800, + child: SfDataGrid( + source: fieldsSource, + gridLinesVisibility: GridLinesVisibility.both, + headerGridLinesVisibility: GridLinesVisibility.both, + columnWidthMode: ColumnWidthMode.fitByCellValue, + selectionMode: SelectionMode.none, + columns: [ + GridColumn( + columnName: 'select', + label: _buildCheckboxHeader( + context, + fieldsSource, + controller.fieldsSource.rows.length, + ), + width: 60, + ), + GridColumn( + columnName: 'id', + label: _buildGridHeader('序号'), + minimumWidth: 80, + ), + GridColumn( + columnName: 'name', + label: _buildGridHeader('名称'), + minimumWidth: 120, + ), + GridColumn( + columnName: 'chineseName', + label: _buildGridHeader('中文名'), + minimumWidth: 120, + ), + GridColumn( + columnName: 'type', + label: _buildGridHeader('类型'), + minimumWidth: 120, + ), + GridColumn( + columnName: 'remark', + label: _buildGridHeader('备注'), + minimumWidth: 200, + ), + ], + onCellTap: (details) { + if (details.column.columnName == 'select') { + final rowIndex = details.rowColumnIndex.rowIndex - 1; + if (rowIndex >= 0 && rowIndex < fieldsSource.fields.length) { + fieldsSource.toggleRowSelection( + rowIndex, + !fieldsSource.fields[rowIndex].isSelected, + ); + } + } + }, + ), + ), + ); + }, + ), + ), + ], + ), + ); + } + + Widget _buildCheckboxHeader(BuildContext context, FieldsDataSource dataSource, int rowCount) { + final allSelected = rowCount > 0 && dataSource.fields.every((item) => item.isSelected); + + return Container( + alignment: Alignment.center, + color: Colors.grey[200], + child: Checkbox( + value: allSelected, + tristate: true, + onChanged: (value) { + dataSource.toggleAllSelection(value ?? false); + }, + ), + ); + } + + Widget _buildIndexesDataGrid(MemoryTableController controller) { + final indexesSource = controller.indexesSource as IndexesDataSource; + + return Card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SizedBox( + width: constraints.maxWidth > 800 ? constraints.maxWidth : 800, + child: SfDataGrid( + source: indexesSource, + gridLinesVisibility: GridLinesVisibility.both, + headerGridLinesVisibility: GridLinesVisibility.both, + columnWidthMode: ColumnWidthMode.fitByCellValue, + columns: [ + GridColumn( + columnName: 'select', + label: _buildCheckboxHeaderForIndexes( + context, + indexesSource, + controller.indexesSource.rows.length, + ), + width: 60, + ), + GridColumn( + columnName: 'indexName', + label: _buildGridHeader('索引名称'), + minimumWidth: 120, + ), + GridColumn( + columnName: 'isPrimary', + label: _buildGridHeader('是否主键'), + minimumWidth: 100, + ), + GridColumn( + columnName: 'indexFields', + label: _buildGridHeader('索引字段'), + minimumWidth: 150, + ), + GridColumn( + columnName: 'rule', + label: _buildGridHeader('规则'), + minimumWidth: 200, + ), + ], + onCellTap: (details) { + if (details.column.columnName == 'select') { + final rowIndex = details.rowColumnIndex.rowIndex - 1; + if (rowIndex >= 0 && rowIndex < indexesSource.indexes.length) { + indexesSource.toggleRowSelection( + rowIndex, + !indexesSource.indexes[rowIndex].isSelected, + ); + } + } + }, + ), + ), + ); + }, + ), + ), + ], + ), + ); + } + + Widget _buildCheckboxHeaderForIndexes( + BuildContext context, + IndexesDataSource dataSource, + int rowCount, + ) { + final allSelected = rowCount > 0 && dataSource.indexes.every((item) => item.isSelected); + + return Container( + alignment: Alignment.center, + color: Colors.grey[200], + child: Checkbox( + value: allSelected, + tristate: true, + onChanged: (value) { + dataSource.toggleAllSelection(value ?? false); + }, + ), + ); + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: double.infinity, + child: Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Wrap( + spacing: 16, + runSpacing: 8, + children: [ + _buildTextFieldRow('名称', controller.tableName), + _buildTextFieldRow('中文名', controller.chineseName), + _buildTextFieldRow('对象编号', controller.objectId), + ], + ), + ), + ), + ), + const SizedBox(height: 8), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text('字段列表', style: TextStyle(fontWeight: FontWeight.bold)), + ), + Expanded(flex: 6, child: _buildFieldsDataGrid(controller)), + const Padding( + padding: EdgeInsets.all(8.0), + child: Text('索引列表', style: TextStyle(fontWeight: FontWeight.bold)), + ), + Expanded(flex: 4, child: _buildIndexesDataGrid(controller)), + ], + ); + } +} diff --git a/win_text_editor/lib/modules/memory_table/widgets/memory_table_right_side.dart b/win_text_editor/lib/modules/memory_table/widgets/memory_table_right_side.dart new file mode 100644 index 0000000..c66388f --- /dev/null +++ b/win_text_editor/lib/modules/memory_table/widgets/memory_table_right_side.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class MemoryTableRightSide extends StatelessWidget { + final TextEditingController codeController; + const MemoryTableRightSide({super.key, required this.codeController}); + + Widget _buildCheckboxSection() { + return SizedBox( + width: double.infinity, + child: Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + spacing: 16, + runSpacing: 8, + children: [ + _buildCheckbox('获取记录'), + _buildCheckbox('获取记录数'), + _buildCheckbox('插入记录'), + _buildCheckbox('修改记录'), + _buildCheckbox('删除记录'), + _buildCheckbox('遍历记录'), + ], + ), + ], + ), + ), + ), + ); + } + + Widget _buildCheckbox(String label) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Checkbox( + value: false, // Replace with your actual value + onChanged: (bool? value) { + // Handle checkbox change + }, + ), + Text(label), + ], + ); + } + + Widget _buildCodeEditor() { + return Card( + child: Padding( + padding: const EdgeInsets.all(8), + // 保留内部的Expanded + child: Container( + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(4), + ), + child: TextField( + controller: codeController, + maxLines: null, + expands: true, + decoration: const InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.all(8), + ), + style: const TextStyle(fontFamily: 'monospace'), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + _buildCheckboxSection(), + Padding( + padding: const EdgeInsets.only(left: 8.0, right: 8.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('生成代码:', style: TextStyle(fontWeight: FontWeight.bold)), + IconButton( + icon: const Icon(Icons.content_copy, size: 20), + tooltip: '复制代码', + onPressed: () { + if (codeController.text.isNotEmpty) { + Clipboard.setData(ClipboardData(text: codeController.text)); + ScaffoldMessenger.of( + context, + ).showSnackBar(const SnackBar(content: Text('已复制到剪贴板'))); + } + }, + ), + ], + ), + ), + Flexible(child: _buildCodeEditor()), + ], + ); + } +} diff --git a/win_text_editor/lib/modules/memory_table/widgets/memory_table_view.dart b/win_text_editor/lib/modules/memory_table/widgets/memory_table_view.dart new file mode 100644 index 0000000..7e9bd91 --- /dev/null +++ b/win_text_editor/lib/modules/memory_table/widgets/memory_table_view.dart @@ -0,0 +1,69 @@ +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/memory_table/controllers/memory_table_controller.dart'; +import 'memory_table_left_side.dart'; +import 'memory_table_right_side.dart'; + +class MemoryTableView extends StatefulWidget { + final String tabId; + const MemoryTableView({super.key, required this.tabId}); + + @override + State createState() => _MemoryTableViewState(); +} + +class _MemoryTableViewState extends State { + late final MemoryTableController _controller; + final TextEditingController _codeController = TextEditingController(); + bool _isControllerFromTabManager = false; + + get tabManager => Provider.of(context, listen: false); + + @override + void initState() { + super.initState(); + + final controllerFromManager = tabManager.getController(widget.tabId); + if (controllerFromManager != null) { + _controller = controllerFromManager; + _isControllerFromTabManager = true; + } else { + _controller = MemoryTableController(); + _isControllerFromTabManager = false; + tabManager.registerController(widget.tabId, _controller); + } + } + + @override + void dispose() { + if (!_isControllerFromTabManager) { + _controller.dispose(); + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: _controller, + child: Consumer( + builder: (context, controller, child) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // 左侧部分 (50%) + Expanded(flex: 5, child: MemoryTableLeftSide(controller: controller)), + const SizedBox(width: 8), + // 右侧部分 (50%) + Expanded(flex: 5, child: MemoryTableRightSide(codeController: _codeController)), + ], + ), + ); + }, + ), + ); + } +} diff --git a/win_text_editor/lib/modules/module_router.dart b/win_text_editor/lib/modules/module_router.dart index bb0f7a7..07a40dd 100644 --- a/win_text_editor/lib/modules/module_router.dart +++ b/win_text_editor/lib/modules/module_router.dart @@ -7,8 +7,8 @@ import 'package:win_text_editor/modules/data_format/controllers/data_format_cont import 'package:win_text_editor/modules/data_format/widgets/data_format_view.dart'; import 'package:win_text_editor/modules/demo/controllers/demo_controller.dart'; import 'package:win_text_editor/modules/demo/widgets/demo_view.dart'; -import 'package:win_text_editor/modules/uft_file/controllers/uft_file_controller.dart'; -import 'package:win_text_editor/modules/uft_file/widgets/uft_file_view.dart'; +import 'package:win_text_editor/modules/memory_table/controllers/memory_table_controller.dart'; +import 'package:win_text_editor/modules/memory_table/widgets/memory_table_view.dart'; import 'package:win_text_editor/shared/base/base_content_controller.dart'; import 'package:win_text_editor/modules/content_search/controllers/content_search_controller.dart'; import 'package:win_text_editor/modules/template_parser/controllers/template_parser_controller.dart'; @@ -21,7 +21,7 @@ class RouterKey { static const String dataFormat = 'data_format'; static const String textEditor = 'text_editor'; static const String dataCompare = 'data_compare'; - static const String uftFile = 'uft_file'; + static const String memoryTable = 'memory_table'; static const String demo = 'demo'; } @@ -32,7 +32,7 @@ class ModuleRouter { RouterKey.templateParser: (tab) => TemplateParserController(), RouterKey.dataFormat: (tab) => DataFormatController(), RouterKey.dataCompare: (tab) => DataCompareController(), - RouterKey.uftFile: (tab) => UftFileController(), + RouterKey.memoryTable: (tab) => MemoryTableController(), RouterKey.demo: (tab) => DemoController(), }; @@ -42,7 +42,7 @@ class ModuleRouter { RouterKey.templateParser: (tab, controller) => TemplateParserView(tabId: tab.id), RouterKey.dataFormat: (tab, controller) => DataFormatView(tabId: tab.id), RouterKey.dataCompare: (tab, controller) => DataCompareView(tabId: tab.id), - RouterKey.uftFile: (tab, controller) => UftFileView(tabId: tab.id), + RouterKey.memoryTable: (tab, controller) => MemoryTableView(tabId: tab.id), RouterKey.demo: (tab, controller) => DemoView(tabId: tab.id), }; diff --git a/win_text_editor/lib/modules/uft_file/controllers/tree_view_controller.dart b/win_text_editor/lib/modules/uft_file/controllers/tree_view_controller.dart deleted file mode 100644 index 0bb7e4c..0000000 --- a/win_text_editor/lib/modules/uft_file/controllers/tree_view_controller.dart +++ /dev/null @@ -1,68 +0,0 @@ -// tree_view_controller.dart - -import 'package:win_text_editor/shared/models/template_node.dart'; -import '../../../shared/base/safe_notifier.dart'; - -class TreeViewController extends SafeNotifier { - //根节点 - List _treeNodes = []; - TemplateNode? _selectedNode; - String? _currentParentPath; - - List get treeNodes => _treeNodes; - TemplateNode? get selectedNode => _selectedNode; - - // 加载树视图,当文件路径改变时调用 - void updateTreeNodes(List 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 get selectedNodeNames { - List 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; - } -} diff --git a/win_text_editor/lib/modules/uft_file/controllers/uft_file_controller.dart b/win_text_editor/lib/modules/uft_file/controllers/uft_file_controller.dart deleted file mode 100644 index c87cf77..0000000 --- a/win_text_editor/lib/modules/uft_file/controllers/uft_file_controller.dart +++ /dev/null @@ -1,145 +0,0 @@ -import 'package:file_picker/file_picker.dart'; -import 'package:win_text_editor/framework/controllers/logger.dart'; -import 'package:win_text_editor/shared/models/template_node.dart'; -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'; - -class UftFileController extends BaseContentController { - final TreeViewController treeController; - - String _filePath = ''; - String? _errorMessage; - - String get filePath => _filePath; - String? get errorMessage => _errorMessage; - - //---------------初始化方法---- - - UftFileController() : treeController = TreeViewController() { - _setupCrossControllerCommunication(); - } - - //设置跨控制器状态协同 - void _setupCrossControllerCommunication() {} - - //----------------业务入口方法----- - //widget调用入口:打开文件 - Future pickFile() async { - final result = await FilePicker.platform.pickFiles( - type: FileType.custom, - allowedExtensions: ['xml', '*'], - ); - if (result != null) { - _filePath = result.files.single.path!; - notifyListeners(); // 通知 Consumer 刷新 - await _loadTemplateData(); - } - } - - //执行框架回调入口:双击左侧资源管理文件 - Future setFilePath(String path) async { - _filePath = path; - notifyListeners(); // 通知 Consumer 刷新 - await _loadTemplateData(); - } - - //加载xml文件 - Future _loadTemplateData() async { - try { - _errorMessage = null; - final file = File(_filePath); - final content = await file.readAsString(); - final document = xml.XmlDocument.parse(content); - - // 更新各控制器 - //树视图展示文件结构 - treeController.updateTreeNodes( - _buildTreeNodes(document.rootElement, document.rootElement.localName, depth: 0), - ); - //列表展示选中节点的内容 - } catch (e) { - _errorMessage = 'Failed to load XML: ${e.toString()}'; - Logger().error('XML加载错误$_errorMessage'); - } - } - - //--------------------私有方法--------- - // 构建树节点 - List _buildTreeNodes( - xml.XmlElement element, - String path, { - required int depth, - int repreatCount = 1, - }) { - final node = TemplateNode( - path: path, - name: element.qualifiedName, - children: [], - depth: depth, - isExpanded: depth < 5, // 默认展开前两层 - isRepeated: repreatCount > 1, - repreatCount: repreatCount, - ); - - // 添加当前元素的所有属性节点 - if (element.attributes.isNotEmpty) { - node.children.addAll( - element.attributes.map( - (attr) => TemplateNode( - path: '$path/@${attr.name.local}', - name: '@${attr.qualifiedName}', - 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) { - String path0 = '$path/${elements.first.name.local}'; - if (elements.length == 1) { - // 单一节点直接添加(包含其所有属性) - node.children.addAll(_buildTreeNodes(elements.first, path0, depth: depth + 1)); - } else { - // 多个相同节点需要合并 - node.children.addAll( - _buildTreeNodes(elements.first, path0, depth: depth + 1, repreatCount: elements.length), - ); - } - }); - - return [node]; - } - - //解析全量数据 - - //-----------框架回调-- - @override - void onOpenFile(String filePath) { - setFilePath(filePath); - } - - @override - void onOpenFolder(String folderPath) { - // 不支持打开文件夹 - } - - @override - void dispose() { - treeController.dispose(); - super.dispose(); - } -} diff --git a/win_text_editor/lib/modules/uft_file/widgets/tree_view.dart b/win_text_editor/lib/modules/uft_file/widgets/tree_view.dart deleted file mode 100644 index 9ab60e5..0000000 --- a/win_text_editor/lib/modules/uft_file/widgets/tree_view.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:win_text_editor/modules/uft_file/controllers/tree_view_controller.dart'; -import 'package:win_text_editor/shared/models/template_node.dart'; -import 'package:win_text_editor/shared/components/tree_view.dart'; - -class FileTreeView extends StatelessWidget { - const FileTreeView({super.key}); - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, controller, _) { - if (controller.treeNodes.isEmpty) { - return const Center(child: Text('No XML data available')); - } - - return TreeView( - nodes: controller.treeNodes, - config: const TreeViewConfig( - showIcons: true, - singleSelect: true, - selectedColor: Colors.lightBlueAccent, - icons: {'element': Icons.label_outline, 'attribute': Icons.code}, - ), - onNodeTap: (node) { - controller.selectTreeNode; - }, - nodeBuilder: (context, node, isSelected, onTap) { - return _buildTreeNode(node, isSelected, onTap, controller); - }, - ); - }, - ); - } - - Widget _buildTreeNode( - TreeNode node, - bool isSelected, - VoidCallback onTap, - TreeViewController 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: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (templateNode.children.isEmpty) // 仅在叶子节点显示复选框 - Checkbox( - value: templateNode.isChecked, - onChanged: (value) { - if (value != null) { - controller.toggleNodeCheck(templateNode); - } - }, - ), - 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, - ), - ), - ); - } -} diff --git a/win_text_editor/lib/modules/uft_file/widgets/uft_file_view.dart b/win_text_editor/lib/modules/uft_file/widgets/uft_file_view.dart deleted file mode 100644 index 09fa046..0000000 --- a/win_text_editor/lib/modules/uft_file/widgets/uft_file_view.dart +++ /dev/null @@ -1,112 +0,0 @@ -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/uft_file/controllers/uft_file_controller.dart'; -import 'package:win_text_editor/modules/uft_file/widgets/tree_view.dart'; - -class UftFileView extends StatefulWidget { - final String tabId; - const UftFileView({super.key, required this.tabId}); - - @override - State createState() => _UftFileViewState(); -} - -class _UftFileViewState extends State { - late final UftFileController _controller; - - bool _isControllerFromTabManager = false; - - get tabManager => Provider.of(context, listen: false); - - @override - void initState() { - super.initState(); - - final controllerFromManager = tabManager.getController(widget.tabId); - if (controllerFromManager != null) { - _controller = controllerFromManager; - _isControllerFromTabManager = true; - } else { - _controller = UftFileController(); - _isControllerFromTabManager = false; - tabManager.registerController(widget.tabId, _controller); - } - } - - @override - void dispose() { - if (!_isControllerFromTabManager) { - _controller.dispose(); - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return MultiProvider( - providers: [ - ChangeNotifierProvider.value(value: _controller), - ChangeNotifierProvider.value(value: _controller.treeController), - ], - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - children: [ - _buildFilePathInput(), - const SizedBox(height: 8), - Expanded(child: _buildMainContent()), - ], - ), - ), - ); - } - - Widget _buildFilePathInput() { - return Consumer( - builder: (context, controller, _) { - return TextField( - decoration: InputDecoration( - labelText: 'UFT File', - hintText: 'UFT File (包括服务、原子层代码文件)', - suffixIcon: IconButton( - icon: const Icon(Icons.folder_open), - onPressed: controller.pickFile, - ), - border: const OutlineInputBorder(), - errorText: controller.errorMessage, - ), - controller: TextEditingController(text: controller.filePath), - readOnly: true, - ); - }, - ); - } - - Widget _buildMainContent() { - return Consumer( - builder: (context, controller, _) { - if (controller.errorMessage != null) { - return Center(child: Text(controller.errorMessage!)); - } - - return Row( - children: [ - SizedBox( - width: MediaQuery.of(context).size.width * 0.3, - child: const Column( - children: [ - Expanded(flex: 2, child: Card(child: FileTreeView())), - SizedBox(height: 8), - Expanded(flex: 4, child: Card(child: Text("UFT File Content1"))), - ], - ), - ), - const SizedBox(width: 8), - const Expanded(child: Card(child: Text("UFT File Content2"))), - ], - ); - }, - ); - } -} diff --git a/win_text_editor/lib/shared/components/file_explorer.dart b/win_text_editor/lib/shared/components/file_explorer.dart index 237511a..1d68a56 100644 --- a/win_text_editor/lib/shared/components/file_explorer.dart +++ b/win_text_editor/lib/shared/components/file_explorer.dart @@ -3,6 +3,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'; @@ -26,6 +27,7 @@ class _FileExplorerState extends State { final String? selectedDirectory = await FilePicker.platform.getDirectoryPath(); if (selectedDirectory != null) { + await FilePathManager.saveLastOpenedFolder(selectedDirectory); await fileProvider.setRootPath(selectedDirectory); } } diff --git a/win_text_editor/pubspec.lock b/win_text_editor/pubspec.lock index 1c15196..d92b2c7 100644 --- a/win_text_editor/pubspec.lock +++ b/win_text_editor/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: async sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.12.0" bitsdojo_window: @@ -14,7 +14,7 @@ packages: description: name: bitsdojo_window sha256: "88ef7765dafe52d97d7a3684960fb5d003e3151e662c18645c1641c22b873195" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.1.6" bitsdojo_window_linux: @@ -22,7 +22,7 @@ packages: description: name: bitsdojo_window_linux sha256: "9519c0614f98be733e0b1b7cb15b827007886f6fe36a4fb62cf3d35b9dd578ab" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.1.4" bitsdojo_window_macos: @@ -30,7 +30,7 @@ packages: description: name: bitsdojo_window_macos sha256: f7c5be82e74568c68c5b8449e2c5d8fd12ec195ecd70745a7b9c0f802bb0268f - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.1.4" bitsdojo_window_platform_interface: @@ -38,7 +38,7 @@ packages: description: name: bitsdojo_window_platform_interface sha256: "65daa015a0c6dba749bdd35a0f092e7a8ba8b0766aa0480eb3ef808086f6e27c" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.1.2" bitsdojo_window_windows: @@ -46,7 +46,7 @@ packages: description: name: bitsdojo_window_windows sha256: fa982cf61ede53f483e50b257344a1c250af231a3cdc93a7064dd6dc0d720b68 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.1.6" boolean_selector: @@ -54,7 +54,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.1.2" characters: @@ -62,7 +62,7 @@ packages: description: name: characters sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.4.0" clock: @@ -70,7 +70,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.1.2" collection: @@ -78,7 +78,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.19.1" cross_file: @@ -86,7 +86,7 @@ packages: description: name: cross_file sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.3.4+2" csv: @@ -94,7 +94,7 @@ packages: description: name: csv sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "6.0.0" expandable: @@ -102,7 +102,7 @@ packages: description: name: expandable sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "5.0.1" fake_async: @@ -110,7 +110,7 @@ packages: description: name: fake_async sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.3.2" ffi: @@ -118,15 +118,23 @@ packages: description: name: ffi sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" file_picker: dependency: "direct main" description: name: file_picker sha256: "77f8e81d22d2a07d0dee2c62e1dda71dc1da73bf43bb2d45af09727406167964" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "10.1.9" flutter: @@ -139,7 +147,7 @@ packages: description: name: flutter_js sha256: "0d22d73a474b5b80c3ab5508e7c3eab6fb20beea9dec45bbd21088cfd27a5e61" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.8.3" flutter_lints: @@ -147,7 +155,7 @@ packages: description: name: flutter_lints sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.0.3" flutter_plugin_android_lifecycle: @@ -155,7 +163,7 @@ packages: description: name: flutter_plugin_android_lifecycle sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.0.28" flutter_syntax_view: @@ -163,7 +171,7 @@ packages: description: name: flutter_syntax_view sha256: c5017bbedfdcf538daba765e16541fcb26434071655ca00cea7cbc205a70246a - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "4.1.7" flutter_test: @@ -181,7 +189,7 @@ packages: description: name: http sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.4.0" http_parser: @@ -189,7 +197,7 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "4.1.2" leak_tracker: @@ -197,7 +205,7 @@ packages: description: name: leak_tracker sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "10.0.8" leak_tracker_flutter_testing: @@ -205,7 +213,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "3.0.9" leak_tracker_testing: @@ -213,7 +221,7 @@ packages: description: name: leak_tracker_testing sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "3.0.1" lints: @@ -221,7 +229,7 @@ packages: description: name: lints sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.1.1" matcher: @@ -229,7 +237,7 @@ packages: description: name: matcher sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.12.17" material_color_utilities: @@ -237,7 +245,7 @@ packages: description: name: material_color_utilities sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.11.1" meta: @@ -245,7 +253,7 @@ packages: description: name: meta sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.16.0" mustache_template: @@ -253,7 +261,7 @@ packages: description: name: mustache_template sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.0.0" nested: @@ -261,7 +269,7 @@ packages: description: name: nested sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.0.0" path: @@ -269,7 +277,7 @@ packages: description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.9.1" path_provider: @@ -277,7 +285,7 @@ packages: description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.1.5" path_provider_android: @@ -285,7 +293,7 @@ packages: description: name: path_provider_android sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.2.17" path_provider_foundation: @@ -293,7 +301,7 @@ packages: description: name: path_provider_foundation sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.4.1" path_provider_linux: @@ -301,7 +309,7 @@ packages: description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.2.1" path_provider_platform_interface: @@ -309,7 +317,7 @@ packages: description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.1.2" path_provider_windows: @@ -317,7 +325,7 @@ packages: description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.3.0" petitparser: @@ -325,7 +333,7 @@ packages: description: name: petitparser sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "6.1.0" platform: @@ -333,7 +341,7 @@ packages: description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "3.1.6" plugin_platform_interface: @@ -341,7 +349,7 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.1.8" provider: @@ -349,7 +357,7 @@ packages: description: name: provider sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "6.1.5" screen_retriever: @@ -357,9 +365,65 @@ packages: description: name: screen_retriever sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.1.9" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter @@ -370,7 +434,7 @@ packages: description: name: source_span sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.10.1" stack_trace: @@ -378,7 +442,7 @@ packages: description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.12.1" stream_channel: @@ -386,7 +450,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.1.4" string_scanner: @@ -394,7 +458,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.4.1" sync_http: @@ -402,7 +466,7 @@ packages: description: name: sync_http sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.3.1" syncfusion_flutter_core: @@ -410,7 +474,7 @@ packages: description: name: syncfusion_flutter_core sha256: a2427697bfad5b611db78ea4c4daef82d3350b83c729a8dc37959662a31547f9 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "23.2.7" syncfusion_flutter_datagrid: @@ -418,7 +482,7 @@ packages: description: name: syncfusion_flutter_datagrid sha256: "9f621f6344d2ed7ea3a8d0ff5c145c174f1e227d6d8851290591ceb718e44600" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "23.2.7" term_glyph: @@ -426,7 +490,7 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.2.2" test_api: @@ -434,7 +498,7 @@ packages: description: name: test_api sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.7.4" typed_data: @@ -442,7 +506,7 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.4.0" vector_math: @@ -450,7 +514,7 @@ packages: description: name: vector_math sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "2.1.4" vm_service: @@ -458,7 +522,7 @@ packages: description: name: vm_service sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "14.3.1" web: @@ -466,7 +530,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.1.1" win32: @@ -474,7 +538,7 @@ packages: description: name: win32 sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "5.13.0" window_manager: @@ -482,7 +546,7 @@ packages: description: name: window_manager sha256: "8699323b30da4cdbe2aa2e7c9de567a6abd8a97d9a5c850a3c86dcd0b34bbfbf" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "0.3.9" xdg_directories: @@ -490,7 +554,7 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "1.1.0" xml: @@ -498,7 +562,7 @@ packages: description: name: xml sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + url: "https://pub.dev" source: hosted version: "6.5.0" sdks: diff --git a/win_text_editor/pubspec.yaml b/win_text_editor/pubspec.yaml index 768fc1f..c75ce03 100644 --- a/win_text_editor/pubspec.yaml +++ b/win_text_editor/pubspec.yaml @@ -23,6 +23,7 @@ dependencies: xml: ^6.5.0 csv: ^6.0.0 mustache_template: ^2.0.0 + shared_preferences: ^2.2.1 dev_dependencies: flutter_test: