From 086f9ae021384e039d4dff3148518f875200be76 Mon Sep 17 00:00:00 2001 From: hejl Date: Fri, 30 May 2025 11:09:47 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=88=97=E8=A1=A8=E5=B1=95?= =?UTF-8?q?=E7=A4=BAOK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../services/memory_table_service.dart | 68 +---------- .../controllers/component_source.dart | 51 +++++++++ .../controllers/uft_component_controller.dart | 57 +++++----- .../uft_component/models/uft_component.dart | 28 +++-- .../services/uft_component_service.dart | 78 ++++++++++++- .../uft_component/widgets/component_grid.dart | 106 ++++++++++++++++++ .../widgets/uft_component_left_side.dart | 43 +++---- .../uft_std_fields/field_data_service.dart | 18 ++- 8 files changed, 308 insertions(+), 141 deletions(-) create mode 100644 win_text_editor/lib/modules/uft_component/controllers/component_source.dart create mode 100644 win_text_editor/lib/modules/uft_component/widgets/component_grid.dart 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 index 9e91ee0..5ef3979 100644 --- 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 @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:win_text_editor/modules/memory_table/models/memory_table.dart'; import 'package:win_text_editor/shared/data/std_fields_cache.dart'; import 'package:win_text_editor/shared/models/std_filed.dart'; +import 'package:win_text_editor/shared/uft_std_fields/field_data_service.dart'; import 'package:xml/xml.dart' as xml; import 'package:path/path.dart' as path; import 'package:win_text_editor/framework/controllers/logger.dart'; @@ -23,9 +24,9 @@ class MemoryTableService { // 2. 查找 metadata 目录和 stdfield.stfield 文件 if (await StdFieldsCache.getLength() == 0) { _logger.info("加载标准字段缓存"); - final metadataFile = await _findMetadataFile(filePath); + final metadataFile = await FieldDataService.findMetadataFile(filePath); if (metadataFile != null) { - await _processStdFieldFile(metadataFile); + await FieldDataService.processStdFieldFile(metadataFile); } } @@ -97,8 +98,8 @@ class MemoryTableService { tableName: fileNameWithoutExt, chineseName: chineseName, objectId: objectId, - fields: fields.isNotEmpty ? fields : _getDefaultFields(), - indexes: indexList.isNotEmpty ? indexList : _getDefaultIndexes(), + fields: fields.isNotEmpty ? fields : FieldDataService.getDefaultFields(), + indexes: indexList.isNotEmpty ? indexList : FieldDataService.getDefaultIndexes(), ); } on xml.XmlParserException catch (e) { _logger.error("XML解析错误: ${e.message}"); @@ -108,65 +109,6 @@ class MemoryTableService { rethrow; } } - - // 查找 metadata 目录和 stdfield.stfield 文件 - Future _findMetadataFile(String filePath) async { - Directory currentDir = File(filePath).parent; - const targetDirName = 'metadata'; - const targetFileName = 'stdfield.stdfield'; - - // 向上查找 metadata 目录 - while (true) { - final metadataDir = Directory('${currentDir.path}/$targetDirName'); - if (await metadataDir.exists()) { - final stdFieldFile = File('${metadataDir.path}/$targetFileName'); - if (await stdFieldFile.exists()) { - return stdFieldFile; - } else { - _logger.error("没找到标准字段文件 $targetFileName"); - return null; - } - } - - // 到达根目录时停止 - if (currentDir.path == currentDir.parent.path) { - _logger.error("没有找到元数据目录 $targetDirName"); - return null; - } - - currentDir = currentDir.parent; - } - } - - // 处理标准字段文件并缓存 - Future _processStdFieldFile(File stdFieldFile) async { - try { - final content = await stdFieldFile.readAsString(); - final document = xml.XmlDocument.parse(content); - final items = document.findAllElements('items'); - - for (final item in items) { - final name = item.getAttribute('name') ?? ''; - final chineseName = item.getAttribute('chineseName') ?? ''; - final dataType = item.getAttribute('dataType') ?? ''; - - if (name.isNotEmpty) { - final stdField = StdField(name: name, chineseName: chineseName, dateType: dataType); - await StdFieldsCache.setData(name, stdField); - } - } - } catch (e) { - _logger.error("处理标准字段文件时出错: $e"); - } - } - - List _getDefaultFields() { - return [Field('1', '', '', ''), Field('2', '', '', ''), Field('3', '', '', '')]; - } - - List _getDefaultIndexes() { - return [Index('', false, '', ''), Index('', false, '', '')]; - } } class TableData { diff --git a/win_text_editor/lib/modules/uft_component/controllers/component_source.dart b/win_text_editor/lib/modules/uft_component/controllers/component_source.dart new file mode 100644 index 0000000..7e06f81 --- /dev/null +++ b/win_text_editor/lib/modules/uft_component/controllers/component_source.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_datagrid/datagrid.dart'; +import 'package:win_text_editor/modules/uft_component/models/uft_component.dart'; +import 'package:win_text_editor/shared/base/base_data_source.dart'; + +class ComponentSource extends SelectableDataSource { + ComponentSource( + List uftComponents, { + required Null Function(dynamic index, dynamic isSelected) onSelectionChanged, + }) : super(uftComponents, onSelectionChanged: onSelectionChanged); + + @override + List get rows => + items + .map( + (component) => DataGridRow( + cells: [ + DataGridCell(columnName: 'select', value: component.isSelected), + DataGridCell(columnName: 'id', value: component.id), + DataGridCell(columnName: 'name', value: component.name), + DataGridCell(columnName: 'chineseName', value: component.chineseName), + ], + ), + ) + .toList(); + + get data => items; + + @override + DataGridRowAdapter buildRow(DataGridRow row) { + final rowIndex = effectiveRows.indexOf(row); + return DataGridRowAdapter( + cells: + row.getCells().map((cell) { + if (cell.columnName == 'select') { + return Center( + child: Checkbox( + value: items[rowIndex].isSelected, + onChanged: (value) => toggleRowSelection(rowIndex, value), + ), + ); + } + return Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text(cell.value.toString(), overflow: TextOverflow.ellipsis), + ); + }).toList(), + ); + } +} diff --git a/win_text_editor/lib/modules/uft_component/controllers/uft_component_controller.dart b/win_text_editor/lib/modules/uft_component/controllers/uft_component_controller.dart index 7338ec3..09c9b0b 100644 --- a/win_text_editor/lib/modules/uft_component/controllers/uft_component_controller.dart +++ b/win_text_editor/lib/modules/uft_component/controllers/uft_component_controller.dart @@ -1,30 +1,41 @@ import 'package:syncfusion_flutter_datagrid/datagrid.dart'; import 'package:win_text_editor/framework/controllers/logger.dart'; import 'package:win_text_editor/framework/services/macro_template_service.dart'; +import 'package:win_text_editor/modules/uft_component/controllers/component_source.dart'; +import 'package:win_text_editor/modules/uft_component/models/uft_component.dart'; +import 'package:win_text_editor/modules/uft_component/services/uft_component_service.dart'; import 'package:win_text_editor/shared/models/std_filed.dart'; import 'package:win_text_editor/shared/uft_std_fields/field_data_source.dart'; import 'package:win_text_editor/modules/memory_table/models/memory_table.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 UftComponentController extends BaseContentController { String? _errorMessage; - String tableName = ""; - String objectId = ""; + String componentName = ""; String chineseName = ""; late DataGridSource fieldsSource; - final MemoryTableService _service; + late DataGridSource componentsSource; + final UftComponentService _service; final MacroTemplateService templateService = MacroTemplateService(); - // 新增:维护MemoryTable对象 - late MemoryTable _memoryTable; + // 新增:维护当前选中组件对象 + late UftComponent _currentUftComponent; - UftComponentController() : _service = MemoryTableService(Logger()) { + UftComponentController() : _service = UftComponentService(Logger()) { // 初始化空数据 final initialFields = [Field('1', '', '', '', false), Field('2', '', '', '', false)]; - final initialIndexes = [Index('', false, '', '', false)]; + final initalComponents = [ + UftComponent(id: 1, name: '', chineseName: '', fields: initialFields), + ]; + + componentsSource = ComponentSource( + initalComponents, + onSelectionChanged: (index, isSelected) { + updateComponentSelection(index, isSelected); + }, + ); fieldsSource = FieldsDataSource( initialFields, @@ -33,14 +44,14 @@ class UftComponentController extends BaseContentController { }, ); - // 初始化MemoryTable - _memoryTable = MemoryTable(tableName: '', columns: initialFields, indexes: initialIndexes); + // 初始化uftComponent + _currentUftComponent = UftComponent(id: 0, name: '', chineseName: '', fields: initialFields); } String? get errorMessage => _errorMessage; - // 新增:获取当前MemoryTable - MemoryTable get memoryTable => _memoryTable; + // 新增:获取当前UftComponent + UftComponent get currentUftComponent => _currentUftComponent; void initTemplateService() { if (!templateService.inited) { @@ -50,29 +61,17 @@ class UftComponentController extends BaseContentController { String? genCodeString(List macroList) { initTemplateService(); - return templateService.renderTemplate(macroList, _memoryTable.toMap()); + return templateService.renderTemplate(macroList, _currentUftComponent.toMap()); } @override Future onOpenFile(String filePath) async { Logger().info("Opening file: $filePath"); try { - final tableData = await _service.parseStructureFile(filePath); - - // Update controller state - tableName = tableData.tableName; - chineseName = tableData.chineseName; - objectId = tableData.objectId; + final components = await _service.parseComponentFile(filePath); // Update data sources - (fieldsSource as FieldsDataSource).updateData(tableData.fields); - - // 更新MemoryTable对象 - _memoryTable = MemoryTable( - tableName: tableName, - columns: tableData.fields, - indexes: tableData.indexes, - ); + (componentsSource as ComponentSource).updateData(components); // Clear any previous error _errorMessage = null; @@ -94,7 +93,7 @@ class UftComponentController extends BaseContentController { fieldsSource.notifyListeners(); // 同步更新MemoryTable - _memoryTable.columns[index].isSelected = isSelected; + _currentUftComponent.fields[index].isSelected = isSelected; notifyListeners(); } } @@ -110,4 +109,6 @@ class UftComponentController extends BaseContentController { void dispose() { super.dispose(); } + + void updateComponentSelection(index, isSelected) {} } diff --git a/win_text_editor/lib/modules/uft_component/models/uft_component.dart b/win_text_editor/lib/modules/uft_component/models/uft_component.dart index 2e80f97..1f6e0d0 100644 --- a/win_text_editor/lib/modules/uft_component/models/uft_component.dart +++ b/win_text_editor/lib/modules/uft_component/models/uft_component.dart @@ -1,25 +1,37 @@ import 'package:win_text_editor/shared/models/std_filed.dart'; -class UftComponent { - final String componentName; - final List columns; +class UftComponent implements SelectableItem { + final int id; + final String name; + final String chineseName; + final List fields; + @override + bool isSelected; - UftComponent({required this.componentName, required this.columns}); + UftComponent({ + required this.id, + required this.name, + required this.chineseName, + required this.fields, + this.isSelected = false, + }); - List get selectFields => columns.where((field) => field.isSelected).toList(); + List get selectFields => fields.where((field) => field.isSelected).toList(); Map toMap() { return { - 'componentName': componentName, + 'id': id, + 'name': name, + 'chineseName': chineseName, 'fields': - columns + fields .map( (field) => { 'id': field.id, 'name': field.name, 'chineseName': field.chineseName, 'type': field.type, - 'isLast': columns.indexOf(field) == columns.length - 1, + 'isLast': fields.indexOf(field) == fields.length - 1, }, ) .toList(), diff --git a/win_text_editor/lib/modules/uft_component/services/uft_component_service.dart b/win_text_editor/lib/modules/uft_component/services/uft_component_service.dart index 220891b..e415bdb 100644 --- a/win_text_editor/lib/modules/uft_component/services/uft_component_service.dart +++ b/win_text_editor/lib/modules/uft_component/services/uft_component_service.dart @@ -1,19 +1,85 @@ // memory_table_service.dart +import 'dart:io'; + +import 'package:win_text_editor/modules/uft_component/models/uft_component.dart'; +import 'package:win_text_editor/shared/data/std_fields_cache.dart'; import 'package:win_text_editor/shared/models/std_filed.dart'; import 'package:win_text_editor/framework/controllers/logger.dart'; +import 'package:win_text_editor/shared/uft_std_fields/field_data_service.dart'; +import 'package:xml/xml.dart' as xml; class UftComponentService { final Logger _logger; UftComponentService(this._logger); -} -class ComponentData { - final String name; - final String chineseName; - final List fields; + Future> parseComponentFile(String filePath) async { + try { + // 1. Check file extension + if (!filePath.toLowerCase().endsWith('component.xml')) { + throw const FormatException("文件名必须是component.xml"); + } + + // 2. 查找 metadata 目录和 stdfield.stfield 文件 + if (await StdFieldsCache.getLength() == 0) { + _logger.info("加载标准字段缓存"); + final metadataFile = await FieldDataService.findMetadataFile(filePath); + if (metadataFile != null) { + await FieldDataService.processStdFieldFile(metadataFile); + } + } + + // 3. Read and parse structure file content + final file = File(filePath); + final content = await file.readAsString(); + + final document = xml.XmlDocument.parse(content); + final componentNodes = document.findAllElements('items'); + + if (componentNodes.isEmpty) { + throw const FormatException("文件格式错误:缺少items节点"); + } + + // 4. 解析组件列表 + final components = []; + int id = 0; + for (var node in componentNodes) { + if (node.findElements("items").isEmpty) continue; + id++; + final name = node.getAttribute('name') ?? ''; + final chineseName = node.getAttribute('chineseName') ?? ''; + + // 5. Process properties (fields) + final childNodes = node.children; + final fields = []; + int index = 1; + + for (final childNode in childNodes) { + final name = childNode.getAttribute('name') ?? ''; + // 尝试从缓存获取标准字段信息 + final stdField = StdFieldsCache.getData(name); + fields.add( + Field( + (index++).toString(), // 序号 + name, // 名称 + stdField?.chineseName ?? '', // 中文名 + stdField?.dateType ?? '', // 类型 + ), + ); + } + + components.add(UftComponent(id: id, name: name, chineseName: chineseName, fields: fields)); + } - ComponentData({required this.name, required this.chineseName, required this.fields}); + return components; + } on xml.XmlParserException catch (e) { + _logger.error("XML解析错误: ${e.message}"); + rethrow; + } catch (e) { + _logger.error("处理文件时发生错误: $e"); + rethrow; + } + } } diff --git a/win_text_editor/lib/modules/uft_component/widgets/component_grid.dart b/win_text_editor/lib/modules/uft_component/widgets/component_grid.dart new file mode 100644 index 0000000..14af7be --- /dev/null +++ b/win_text_editor/lib/modules/uft_component/widgets/component_grid.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:syncfusion_flutter_datagrid/datagrid.dart'; +import 'package:win_text_editor/modules/uft_component/controllers/component_source.dart'; +import 'package:win_text_editor/shared/base/base_data_source.dart'; +import 'package:win_text_editor/shared/models/std_filed.dart'; + +class ComponentGrid extends StatelessWidget { + final ComponentSource componentsSource; + final Function(int index, bool isSelected)? onSelectionChanged; + + const ComponentGrid({super.key, required this.componentsSource, this.onSelectionChanged}); + + 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 _buildCheckboxHeader( + BuildContext context, + SelectableDataSource dataSource, + ) { + final allSelected = + dataSource.items.isNotEmpty && dataSource.items.every((item) => item.isSelected); + final someSelected = + dataSource.items.isNotEmpty && + dataSource.items.any((item) => item.isSelected) && + !allSelected; + + return Container( + alignment: Alignment.center, + color: Colors.grey[200], + child: Checkbox( + value: allSelected, + tristate: someSelected, + onChanged: (value) => dataSource.toggleAllSelection(value ?? false), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Card( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) { + return SizedBox( + width: constraints.maxWidth, + child: SfDataGrid( + source: componentsSource, + gridLinesVisibility: GridLinesVisibility.both, + headerGridLinesVisibility: GridLinesVisibility.both, + columnWidthMode: ColumnWidthMode.fitByCellValue, + selectionMode: SelectionMode.none, + columns: [ + GridColumn( + columnName: 'select', + label: ValueListenableBuilder( + valueListenable: componentsSource.selectionNotifier, + builder: + (context, _, __) => _buildCheckboxHeader(context, componentsSource), + ), + width: 60, + ), + GridColumn(columnName: 'id', label: _buildGridHeader('序号'), minimumWidth: 80), + GridColumn( + columnName: 'name', + label: _buildGridHeader('名称'), + minimumWidth: 120, + ), + GridColumn( + columnName: 'chineseName', + label: _buildGridHeader('中文名'), + minimumWidth: 120, + ), + ], + onCellTap: (details) { + if (details.column.columnName == 'select') { + final rowIndex = details.rowColumnIndex.rowIndex - 1; + if (rowIndex >= 0 && rowIndex < componentsSource.items.length) { + componentsSource.toggleRowSelection( + rowIndex, + !componentsSource.items[rowIndex].isSelected, + ); + onSelectionChanged?.call( + rowIndex, + componentsSource.items[rowIndex].isSelected, + ); + } + } + }, + ), + ); + }, + ), + ), + ], + ), + ); + } +} diff --git a/win_text_editor/lib/modules/uft_component/widgets/uft_component_left_side.dart b/win_text_editor/lib/modules/uft_component/widgets/uft_component_left_side.dart index 584b127..aec210b 100644 --- a/win_text_editor/lib/modules/uft_component/widgets/uft_component_left_side.dart +++ b/win_text_editor/lib/modules/uft_component/widgets/uft_component_left_side.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:win_text_editor/modules/uft_component/controllers/component_source.dart'; import 'package:win_text_editor/modules/uft_component/controllers/uft_component_controller.dart'; +import 'package:win_text_editor/modules/uft_component/widgets/component_grid.dart'; import 'package:win_text_editor/shared/uft_std_fields/field_data_source.dart'; import 'package:win_text_editor/shared/uft_std_fields/fields_data_grid.dart'; @@ -13,7 +15,7 @@ class UftComponentLeftSide extends StatelessWidget { children: [ Text('$label:'), SizedBox( - width: 200, + width: 150, child: TextField( controller: TextEditingController(text: value), readOnly: true, @@ -29,30 +31,25 @@ class UftComponentLeftSide extends StatelessWidget { 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)), + Row( + children: [ + const SizedBox(width: 8), + _buildTextFieldRow('名称', controller.componentName), + _buildTextFieldRow('中文名', controller.chineseName), + ], ), Expanded( flex: 6, + child: ComponentGrid( + componentsSource: controller.componentsSource as ComponentSource, + onSelectionChanged: (index, isSelected) { + controller.updateFieldSelection(index, isSelected); + }, + ), + ), + Expanded( + flex: 3, child: FieldsDataGrid( fieldsSource: controller.fieldsSource as FieldsDataSource, onSelectionChanged: (index, isSelected) { @@ -60,10 +57,6 @@ class UftComponentLeftSide extends StatelessWidget { }, ), ), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text('索引列表', style: TextStyle(fontWeight: FontWeight.bold)), - ), ], ); } diff --git a/win_text_editor/lib/shared/uft_std_fields/field_data_service.dart b/win_text_editor/lib/shared/uft_std_fields/field_data_service.dart index 6398dc3..f6d246f 100644 --- a/win_text_editor/lib/shared/uft_std_fields/field_data_service.dart +++ b/win_text_editor/lib/shared/uft_std_fields/field_data_service.dart @@ -7,11 +7,7 @@ import 'package:win_text_editor/shared/models/std_filed.dart'; import 'package:xml/xml.dart' as xml; class FieldDataService { - final Logger _logger; - - FieldDataService(this._logger); - - Future findMetadataFile(String filePath) async { + static Future findMetadataFile(String filePath) async { Directory currentDir = File(filePath).parent; const targetDirName = 'metadata'; const targetFileName = 'stdfield.stdfield'; @@ -24,14 +20,14 @@ class FieldDataService { if (await stdFieldFile.exists()) { return stdFieldFile; } else { - _logger.error("没找到标准字段文件 $targetFileName"); + Logger().error("没找到标准字段文件 $targetFileName"); return null; } } // 到达根目录时停止 if (currentDir.path == currentDir.parent.path) { - _logger.error("没有找到元数据目录 $targetDirName"); + Logger().error("没有找到元数据目录 $targetDirName"); return null; } @@ -40,7 +36,7 @@ class FieldDataService { } // 处理标准字段文件并缓存 - Future processStdFieldFile(File stdFieldFile) async { + static Future processStdFieldFile(File stdFieldFile) async { try { final content = await stdFieldFile.readAsString(); final document = xml.XmlDocument.parse(content); @@ -57,15 +53,15 @@ class FieldDataService { } } } catch (e) { - _logger.error("处理标准字段文件时出错: $e"); + Logger().error("处理标准字段文件时出错: $e"); } } - List getDefaultFields() { + static List getDefaultFields() { return [Field('1', '', '', ''), Field('2', '', '', ''), Field('3', '', '', '')]; } - List getDefaultIndexes() { + static List getDefaultIndexes() { return [Index('', false, '', ''), Index('', false, '', '')]; } }