From fadd59d1dc1559798504f29709d8c991a961d8ed Mon Sep 17 00:00:00 2001 From: hejl Date: Fri, 30 May 2025 16:08:18 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E4=BB=A3=E7=A0=81=E7=94=9F?= =?UTF-8?q?=E6=88=90=E5=AE=8C=E6=88=90=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/config/uft_macro_list.yaml | 93 ++++++++++++---- .../services/macro_template_service.dart | 8 +- .../controllers/component_source.dart | 39 +++---- .../controllers/uft_component_controller.dart | 14 ++- .../uft_component/models/uft_component.dart | 16 +++ .../services/uft_component_service.dart | 45 +++++++- .../uft_component/widgets/component_grid.dart | 73 +++++------- .../widgets/uft_component_left_side.dart | 105 ++++++++++++++---- .../widgets/uft_component_right_side.dart | 2 +- 9 files changed, 271 insertions(+), 124 deletions(-) diff --git a/win_text_editor/assets/config/uft_macro_list.yaml b/win_text_editor/assets/config/uft_macro_list.yaml index 5b34ff6..0b5ee74 100644 --- a/win_text_editor/assets/config/uft_macro_list.yaml +++ b/win_text_editor/assets/config/uft_macro_list.yaml @@ -1,9 +1,8 @@ templates: 获取记录: - header: "[获取记录][{{tableName}}({{keyName}})" body: | - ][ + [获取记录][{{tableName}}({{keyName}})][ {{#keyFields}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{/keyFields}} @@ -11,8 +10,7 @@ templates: {{#selectedFields}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{/selectedFields}} - ] - footer: | + ] [继续执行] [记录为空][{{tableName}}] { @@ -23,19 +21,15 @@ templates: 获取记录数: - header: "[获取记录数][{{tableName}}][@count]" - body: "" - footer: "" + body: "[获取记录数][{{tableName}}][@count]" 插入记录: - header: "[插入记录][{{tableName}}][" body: | - ][ + [插入记录][{{tableName}}][ {{#fields}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{/fields}} ] - footer: | [索引冲突] { [继续执行] @@ -60,14 +54,12 @@ templates: } 修改记录: - header: "[获取记录][{{tableName}}({{keyName}})" body: | - ][ + [获取记录][{{tableName}}({{keyName}})][ {{#keyFields}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{/keyFields}} - ] - footer: | + ] [继续执行] [记录不为空][{{tableName}}] { @@ -85,14 +77,12 @@ templates: } 删除记录: - header: "[获取记录][{{tableName}}({{keyName}})" body: | - ][ + [获取记录][{{tableName}}({{keyName}})][ {{#keyFields}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{/keyFields}} ] - footer: | [继续执行] [记录不为空][{{tableName}}] { @@ -100,9 +90,8 @@ templates: } 遍历记录: - header: "[遍历记录开始][{{tableName}}({{selectIndexOrKey.name}})" body: | - ][ + [遍历记录开始][{{tableName}}({{selectIndexOrKey.name}})][ {{#selectIndexOrKey.fields}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{/selectIndexOrKey.fields}} @@ -110,9 +99,69 @@ templates: {{#selectedFields}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{/selectedFields}} - ] - footer: | + ] { //balabala } - [遍历记录结束] \ No newline at end of file + [遍历记录结束] + + 插入组件: + body: | + [插入组件][{{name}}][0][ + {{#fields}} + {{name}} = @{{name}} {{^isLast}}, {{/isLast}} + {{/fields}} + ] + + 修改组件: + body: | + [修改组件][{{name}}][@num][ + {{#selectedFields}} + {{name}} = @{{name}} {{^isLast}}, {{/isLast}} + {{/selectedFields}} + ] + + 获取组件: + body: | + {{#hasIndex}}[组件排序][{{name}}(index.name)]{{/hasIndex}} + [获取组件][{{name}}{{#hasIndex}}({{index.name}}){{/hasIndex}}][{{^hasIndex}}[@num][{{/hasIndex}} + {{#hasIndex}} + {{#index.fields}} + {{name}} = @{{name}} {{^isLast}}, {{/isLast}} + {{/index.fields}} + ][ + {{/hasIndex}} + {{#selectedFields}} + {{name}} = @{{name}} {{^isLast}}, {{/isLast}} + {{/selectedFields}} + ] + + 遍历组件: + body: | + {{#hasIndex}}[组件排序][{{name}}({{index.name}})]{{/hasIndex}} + [遍历组件开始][{{name}}{{#hasIndex}}({{index.name}}){{/hasIndex}}][ + {{#hasIndex}} + {{#index.fields}} + {{name}} = @{{name}} {{^isLast}}, {{/isLast}} + {{/index.fields}} + {{/hasIndex}} + ][ + {{#selectedFields}} + {{name}} = @{{name}} {{^isLast}}, {{/isLast}} + {{/selectedFields}} + ] + { + + } + "[遍历组件结束]" + + 组件大小: + body: "[组件大小][{{name}}][@num]" + + 尾部插入组件: + body: | + [插入组件][{{name}}][ + {{#fields}} + {{name}} = @{{name}} {{^isLast}}, {{/isLast}} + {{/fields}} + ] \ No newline at end of file diff --git a/win_text_editor/lib/framework/services/macro_template_service.dart b/win_text_editor/lib/framework/services/macro_template_service.dart index 098a86a..c9cc554 100644 --- a/win_text_editor/lib/framework/services/macro_template_service.dart +++ b/win_text_editor/lib/framework/services/macro_template_service.dart @@ -47,13 +47,7 @@ class MacroTemplateService { final template = getTemplate(templateName); if (template == null) throw Exception('Template $templateName not found'); - buffer.writeln( - [ - _render(template['header'], context), - _render(template['body'], context), - _render(template['footer'], context), - ].join('\n'), - ); + buffer.writeln(_render(template['body'], context)); buffer.writeln(); } 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 index 7e06f81..708389b 100644 --- a/win_text_editor/lib/modules/uft_component/controllers/component_source.dart +++ b/win_text_editor/lib/modules/uft_component/controllers/component_source.dart @@ -15,7 +15,6 @@ class ComponentSource extends SelectableDataSource { .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), @@ -24,28 +23,30 @@ class ComponentSource extends SelectableDataSource { ) .toList(); - get data => items; - @override DataGridRowAdapter buildRow(DataGridRow row) { final rowIndex = effectiveRows.indexOf(row); + final isSelected = items[rowIndex].isSelected; + 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(), + color: isSelected ? Colors.lightBlue.withOpacity(0.3) : null, + cells: [ + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text(row.getCells()[0].value.toString(), overflow: TextOverflow.ellipsis), + ), + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text(row.getCells()[1].value.toString(), overflow: TextOverflow.ellipsis), + ), + Container( + alignment: Alignment.centerLeft, + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Text(row.getCells()[2].value.toString(), overflow: TextOverflow.ellipsis), + ), + ], ); } } 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 09c9b0b..8f0bbc7 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 @@ -6,7 +6,6 @@ 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/shared/base/base_content_controller.dart'; class UftComponentController extends BaseContentController { @@ -110,5 +109,16 @@ class UftComponentController extends BaseContentController { super.dispose(); } - void updateComponentSelection(index, isSelected) {} + void updateComponentSelection(int index, bool isSelected) { + if (index >= 0 && index < (componentsSource as ComponentSource).items.length) { + // 更新当前选中的组件 + _currentUftComponent = (componentsSource as ComponentSource).items[index]; + + // 更新字段数据源 + (fieldsSource as FieldsDataSource).updateData(_currentUftComponent.fields); + + // 通知UI更新 + notifyListeners(); + } + } } 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 1f6e0d0..e4e275b 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,3 +1,4 @@ +import 'package:win_text_editor/modules/memory_table/models/memory_table.dart'; import 'package:win_text_editor/shared/models/std_filed.dart'; class UftComponent implements SelectableItem { @@ -5,6 +6,7 @@ class UftComponent implements SelectableItem { final String name; final String chineseName; final List fields; + List? indexes; @override bool isSelected; @@ -14,11 +16,13 @@ class UftComponent implements SelectableItem { required this.chineseName, required this.fields, this.isSelected = false, + this.indexes, }); List get selectFields => fields.where((field) => field.isSelected).toList(); Map toMap() { + var index = indexes?.firstOrNull; return { 'id': id, 'name': name, @@ -47,6 +51,18 @@ class UftComponent implements SelectableItem { }, ) .toList(), + 'hasIndex': indexes?.isNotEmpty ?? false, + 'index': + index == null + ? null + : { + 'name': index.indexName, + 'fields': + index.fields.asMap().entries.map((entry) { + return {'name': entry.value, 'isLast': entry.key == index.fields.length - 1}; + }).toList(), + 'rule': index.rule, + }, }; } } 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 e415bdb..35889e0 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 @@ -2,6 +2,7 @@ import 'dart:io'; +import 'package:win_text_editor/modules/memory_table/models/memory_table.dart'; 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'; @@ -44,20 +45,48 @@ class UftComponentService { // 4. 解析组件列表 final components = []; - int id = 0; + int id = 1; 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 = []; + List? indexes = []; int index = 1; + // 5.解析是否有索引 + final indexNodes = node.findElements('indexs'); + if (indexNodes.isNotEmpty) { + final attrnames = []; + for (final indexNode in indexNodes) { + final indexName = indexNode.getAttribute('name') ?? ''; + + //5.1 解析索引字段 + final indexFieldNodes = indexNode.findElements('items'); + for (final fieldNode in indexFieldNodes) { + final fieldName = fieldNode.getAttribute('attrname') ?? ''; + if (fieldName.isEmpty) continue; // 跳过没有名称的字段 + attrnames.add(fieldName); + } + + indexes.add(Index(indexName, true, attrnames.join(','), '', false)); + //将索引添加到字段列表中 + // fields.add(Field((index++).toString(), indexName, '索引', 'COMPONENT_INDEX')); + } + } + + // 6. 解析字段列表 + final childNodes = node.children; + for (final childNode in childNodes) { + if (childNode is xml.XmlElement) { + if (childNode.name.local != 'items') continue; // 只处理items节点 + } final name = childNode.getAttribute('name') ?? ''; + if (name.isEmpty) continue; // 跳过没有名称的字段 + // 尝试从缓存获取标准字段信息 final stdField = StdFieldsCache.getData(name); fields.add( @@ -70,7 +99,15 @@ class UftComponentService { ); } - components.add(UftComponent(id: id, name: name, chineseName: chineseName, fields: fields)); + components.add( + UftComponent( + id: id, + name: name, + chineseName: chineseName, + fields: fields, + indexes: indexes, + ), + ); } return components; 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 index 14af7be..20b9f1b 100644 --- a/win_text_editor/lib/modules/uft_component/widgets/component_grid.dart +++ b/win_text_editor/lib/modules/uft_component/widgets/component_grid.dart @@ -1,15 +1,20 @@ 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 { +class ComponentGrid extends StatefulWidget { final ComponentSource componentsSource; final Function(int index, bool isSelected)? onSelectionChanged; const ComponentGrid({super.key, required this.componentsSource, this.onSelectionChanged}); + @override + State createState() => ComponentGridState(); +} + +class ComponentGridState extends State { + final DataGridController _dataGridController = DataGridController(); + Container _buildGridHeader(String text) { return Container( alignment: Alignment.center, @@ -18,26 +23,9 @@ class ComponentGrid extends StatelessWidget { ); } - 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), - ), - ); + void scrollToIndex(int index) { + _dataGridController.scrollToRow(index.toDouble()); + _dataGridController.selectedIndex = index; } @override @@ -52,21 +40,13 @@ class ComponentGrid extends StatelessWidget { return SizedBox( width: constraints.maxWidth, child: SfDataGrid( - source: componentsSource, + controller: _dataGridController, + source: widget.componentsSource, gridLinesVisibility: GridLinesVisibility.both, headerGridLinesVisibility: GridLinesVisibility.both, columnWidthMode: ColumnWidthMode.fitByCellValue, - selectionMode: SelectionMode.none, + selectionMode: SelectionMode.single, 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', @@ -79,18 +59,19 @@ class ComponentGrid extends StatelessWidget { 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, - ); + onSelectionChanged: (addedRows, removedRows) { + if (addedRows.isNotEmpty) { + // 获取选中行的索引 + final rowIndex = widget.componentsSource.effectiveRows + .indexOf(addedRows.first); + + if (rowIndex >= 0 && rowIndex < widget.componentsSource.items.length) { + // 清除所有选择 + for (var i = 0; i < widget.componentsSource.items.length; i++) { + widget.componentsSource.items[i].isSelected = (i == rowIndex); + } + widget.componentsSource.notifyListeners(); + widget.onSelectionChanged?.call(rowIndex, true); } } }, 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 aec210b..efd86e9 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 @@ -5,59 +5,118 @@ import 'package:win_text_editor/modules/uft_component/widgets/component_grid.dar 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'; -class UftComponentLeftSide extends StatelessWidget { +class UftComponentLeftSide extends StatefulWidget { final UftComponentController controller; const UftComponentLeftSide({super.key, required this.controller}); - Widget _buildTextFieldRow(String label, String value) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text('$label:'), - SizedBox( - width: 150, - child: TextField( - controller: TextEditingController(text: value), - readOnly: true, - decoration: const InputDecoration(isDense: true, contentPadding: EdgeInsets.all(8)), - ), + @override + State createState() => _UftComponentLeftSideState(); +} + +class _UftComponentLeftSideState extends State { + final TextEditingController _searchController = TextEditingController(); + final GlobalKey _gridKey = GlobalKey(); + final FocusNode _searchFocusNode = FocusNode(); // 新增焦点节点 + + Widget _buildSearchField() { + return SizedBox( + width: 400, + child: TextField( + controller: _searchController, + focusNode: _searchFocusNode, // 添加焦点控制 + decoration: InputDecoration( + isDense: true, + contentPadding: const EdgeInsets.all(8), + hintText: '输入名称或中文名查找', + suffixIcon: IconButton(icon: const Icon(Icons.search), onPressed: _handleSearch), ), - ], + onSubmitted: (_) => _handleSearch(), // 添加回车键支持 + ), ); } + void _handleSearch() { + final searchText = _searchController.text.trim(); + if (searchText.isEmpty) return; + + final source = widget.controller.componentsSource as ComponentSource; + final items = source.items; + + // 模糊匹配查找 + int? foundIndex; + for (var i = 0; i < items.length; i++) { + if (items[i].chineseName.contains(searchText)) { + foundIndex = i; + break; + } + } + + if (foundIndex == null) { + for (var i = 0; i < items.length; i++) { + if (items[i].name.contains(searchText)) { + foundIndex = i; + break; + } + } + } + + if (foundIndex != null) { + // 更新选中状态 + for (var i = 0; i < items.length; i++) { + items[i].isSelected = (i == foundIndex); + } + source.notifyListeners(); + + // 定位到该行 + _gridKey.currentState?.scrollToIndex(foundIndex); + + // 更新字段表格 + widget.controller.updateComponentSelection(foundIndex, true); + } else { + ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('未找到匹配的记录'))); + } + } + @override Widget build(BuildContext context) { return Column( - crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, children: [ const SizedBox(height: 8), Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const SizedBox(width: 8), - _buildTextFieldRow('名称', controller.componentName), - _buildTextFieldRow('中文名', controller.chineseName), + const Text(' 标准组件', style: TextStyle(fontWeight: FontWeight.bold)), + _buildSearchField(), ], ), Expanded( - flex: 6, + flex: 3, child: ComponentGrid( - componentsSource: controller.componentsSource as ComponentSource, + key: _gridKey, + componentsSource: widget.controller.componentsSource as ComponentSource, onSelectionChanged: (index, isSelected) { - controller.updateFieldSelection(index, isSelected); + widget.controller.updateComponentSelection(index, isSelected); }, ), ), Expanded( flex: 3, child: FieldsDataGrid( - fieldsSource: controller.fieldsSource as FieldsDataSource, + fieldsSource: widget.controller.fieldsSource as FieldsDataSource, onSelectionChanged: (index, isSelected) { - controller.updateFieldSelection(index, isSelected); + widget.controller.updateFieldSelection(index, isSelected); }, ), ), ], ); } + + @override + void dispose() { + _searchController.dispose(); + _searchFocusNode.dispose(); // 释放焦点节点 + super.dispose(); + } } diff --git a/win_text_editor/lib/modules/uft_component/widgets/uft_component_right_side.dart b/win_text_editor/lib/modules/uft_component/widgets/uft_component_right_side.dart index e649679..2e5d584 100644 --- a/win_text_editor/lib/modules/uft_component/widgets/uft_component_right_side.dart +++ b/win_text_editor/lib/modules/uft_component/widgets/uft_component_right_side.dart @@ -75,7 +75,7 @@ class _UftComponentRightSideState extends State { } Widget _buildCheckboxSection() { - final operations = ['获取记录', '获取记录数', '插入记录', '修改记录', '删除记录', '遍历记录']; + final operations = ['插入组件', '修改组件', '获取组件', '遍历组件', '组件大小', '尾部插入组件']; return SizedBox( width: double.infinity,