From 9751ac730bfde2b0dff6e7d5a7b0d7d99cd0b416 Mon Sep 17 00:00:00 2001 From: hejl Date: Tue, 24 Jun 2025 09:40:55 +0800 Subject: [PATCH] =?UTF-8?q?=E7=95=8C=E9=9D=A2=E6=90=9E=E5=AE=8C=EF=BC=8C?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E5=BC=80=E6=90=9E=E5=89=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/code_creater_view.dart | 168 +++---------- .../code_creater/widgets/node_table.dart | 226 ++++++++++++++++++ .../components/my_pluto_dropdown_column.dart | 73 ++++++ 3 files changed, 336 insertions(+), 131 deletions(-) create mode 100644 win_text_editor/lib/modules/code_creater/widgets/node_table.dart create mode 100644 win_text_editor/lib/shared/components/my_pluto_dropdown_column.dart diff --git a/win_text_editor/lib/modules/code_creater/widgets/code_creater_view.dart b/win_text_editor/lib/modules/code_creater/widgets/code_creater_view.dart index 7193189..fc09f51 100644 --- a/win_text_editor/lib/modules/code_creater/widgets/code_creater_view.dart +++ b/win_text_editor/lib/modules/code_creater/widgets/code_creater_view.dart @@ -1,14 +1,11 @@ import 'dart:ui'; import 'package:flutter/material.dart'; -import 'package:pluto_grid/pluto_grid.dart'; import 'package:provider/provider.dart'; import 'package:win_text_editor/framework/controllers/tab_items_controller.dart'; import 'package:win_text_editor/modules/code_creater/controllers/code_creater_controller.dart'; -import 'package:win_text_editor/modules/outline/models/outline_node.dart'; +import 'package:win_text_editor/modules/code_creater/widgets/node_table.dart'; import 'package:win_text_editor/shared/components/code_generation_components.dart'; -import 'package:win_text_editor/shared/components/my_pluto_column.dart'; -import 'package:win_text_editor/shared/components/my_pluto_configuration.dart'; class CodeCreaterView extends StatefulWidget { final String tabId; @@ -21,29 +18,12 @@ class CodeCreaterView extends StatefulWidget { class _CodeCreaterViewState extends State { late final CodeCreaterController _controller; bool _isControllerFromTabManager = false; - PlutoGridStateManager? _stateManager; - - final List operations = ['逻辑服务', '逻辑函数', '原子服务', '原子函数']; final TextEditingController _codeController = TextEditingController(); - + final List operations = ['逻辑服务', '逻辑函数', '原子服务', '原子函数']; String? _selectedOperation = '原子函数'; get tabManager => Provider.of(context, listen: false); - final ScrollController _scrollController = ScrollController(); - - // 动态计算表格宽度 - double _calculateTableWidth() { - const columnSpacing = 16.0; - double totalWidth = 0; - - for (var column in _buildColumns()) { - totalWidth += column.width + columnSpacing; - } - - return totalWidth + 100; // 额外留白 - } - @override void initState() { super.initState(); @@ -58,22 +38,20 @@ class _CodeCreaterViewState extends State { tabManager.registerController(widget.tabId, _controller); } - _controller.addListener(_handleControllerUpdate); // 添加监听 + _controller.addListener(_handleControllerUpdate); } void _handleControllerUpdate() { - setState(() {}); // 控制器通知时刷新 UI + setState(() {}); } @override void dispose() { - _controller.removeListener(_handleControllerUpdate); // 移除监听 - _scrollController.dispose(); // 释放滚动控制器 + _controller.removeListener(_handleControllerUpdate); if (!_isControllerFromTabManager) { _controller.dispose(); } _codeController.dispose(); - super.dispose(); } @@ -81,48 +59,22 @@ class _CodeCreaterViewState extends State { Widget build(BuildContext context) { return Row( children: [ - // 左侧 PlutoGrid (40%) + // 左侧表格 (40%) Expanded( flex: 4, child: Padding( padding: const EdgeInsets.all(8.0), - child: ScrollConfiguration( - behavior: ScrollConfiguration.of(context).copyWith( - dragDevices: { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - PointerDeviceKind.stylus, - PointerDeviceKind.trackpad, - }, - ), - child: Scrollbar( - controller: _scrollController, // 需要添加的控制器 - thickness: 12.0, - thumbVisibility: true, - interactive: true, // Windows必须设置为true - child: SingleChildScrollView( - controller: _scrollController, // 使用同一个控制器 - scrollDirection: Axis.horizontal, - child: SizedBox( - width: _calculateTableWidth(), // 动态计算宽度 - child: PlutoGrid( - key: ValueKey('pluto_grid_${_controller.members.length}'), - configuration: MyPlutoGridConfiguration(), - columns: _buildColumns(), - mode: PlutoGridMode.normal, - rows: _controller.members.isEmpty ? [] : _buildRows(_controller), - noRowsWidget: const Center(child: Text('无大纲节点')), - onLoaded: (PlutoGridOnLoadedEvent event) { - _stateManager ??= event.stateManager; - }, - ), - ), - ), - ), + child: NodeTable( + members: _controller.members, + onMoveMember: _moveMember, + onDeleteMember: _deleteMember, + onMoveToTop: _moveToTop, + onMoveToBottom: _moveToBottom, ), ), ), - // 右侧 CodeGenerationSection (60%) + + // 右侧代码生成区 (60%) Expanded( flex: 6, child: Padding( @@ -143,82 +95,38 @@ class _CodeCreaterViewState extends State { ); } - List _buildColumns() { - return [ - MyPlutoColumn(title: '#', field: 'rowNum', width: 40), - MyPlutoColumn(title: '成员', field: 'content', type: PlutoColumnType.text(), width: 300), - MyPlutoColumn(title: '选择类型', field: 'type', editable: true, width: 200), - MyPlutoColumn( - title: '操作', - field: 'action', - width: 150, - renderer: (rendererContext) { - final result = rendererContext.row.cells['action']!.value as OutlineNode; - final index = rendererContext.row.cells['rowNum']!.value - 1; - final canMoveUp = index > 0; - final canMoveDown = index < _controller.members.length - 1; - - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - icon: Icon( - Icons.arrow_upward, - size: 12, - color: canMoveUp ? Colors.blue : Colors.grey, - ), - onPressed: - canMoveUp ? () => _moveMember(context, rendererContext.row, result, -1) : null, - ), - IconButton( - icon: Icon( - Icons.arrow_downward, - size: 12, - color: canMoveDown ? Colors.blue : Colors.grey, - ), - onPressed: - canMoveDown ? () => _moveMember(context, rendererContext.row, result, 1) : null, - ), - IconButton( - icon: const Icon(Icons.delete_forever, size: 12, color: Colors.red), - onPressed: () => _deleteMember(context, rendererContext.row, result), - ), - ], - ); - }, - ), - ]; - } - - void _moveMember(BuildContext context, PlutoRow row, OutlineNode result, int direction) { - final index = row.cells['rowNum']!.value - 1; + void _moveMember(int index, int direction) { final newIndex = index + direction; - if (newIndex >= 0 && newIndex < _controller.members.length) { setState(() { final member = _controller.members.removeAt(index); _controller.members.insert(newIndex, member); - _controller.notifyListeners(); // 通知监听器更新 }); } } - List _buildRows(CodeCreaterController controller) { - return [ - ...controller.members.asMap().entries.map((entry) { - final index = entry.key; - final member = entry.value; - return PlutoRow( - key: ValueKey('${member.hashCode}_$index'), // 复合key确保唯一性 - cells: { - 'rowNum': PlutoCell(value: index + 1), - 'content': PlutoCell(value: member.title), - 'type': PlutoCell(value: member.value), - 'action': PlutoCell(value: member), - }, - ); - }), - ]; + void _moveToTop(int index) { + if (index > 0) { + setState(() { + final member = _controller.members.removeAt(index); + _controller.members.insert(0, member); + }); + } + } + + void _moveToBottom(int index) { + if (index < _controller.members.length - 1) { + setState(() { + final member = _controller.members.removeAt(index); + _controller.members.add(member); + }); + } + } + + void _deleteMember(int index) { + setState(() { + _controller.members.removeAt(index); + }); } void _selectOperation(String? operation) { @@ -235,6 +143,4 @@ class _CodeCreaterViewState extends State { _codeController.text = ''; } } - - void _deleteMember(BuildContext context, PlutoRow row, OutlineNode result) {} } diff --git a/win_text_editor/lib/modules/code_creater/widgets/node_table.dart b/win_text_editor/lib/modules/code_creater/widgets/node_table.dart new file mode 100644 index 0000000..fc52c73 --- /dev/null +++ b/win_text_editor/lib/modules/code_creater/widgets/node_table.dart @@ -0,0 +1,226 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:pluto_grid/pluto_grid.dart'; +import 'package:win_text_editor/modules/outline/models/outline_node.dart'; +import 'package:win_text_editor/shared/components/my_pluto_column.dart'; +import 'package:win_text_editor/shared/components/my_pluto_configuration.dart'; +import 'package:win_text_editor/shared/components/my_pluto_dropdown_column.dart'; + +class NodeTable extends StatefulWidget { + final List members; + final Function(int, int) onMoveMember; + final Function(int) onDeleteMember; + final Function(int) onMoveToTop; + final Function(int) onMoveToBottom; + + const NodeTable({ + super.key, + required this.members, + required this.onMoveMember, + required this.onDeleteMember, + required this.onMoveToTop, + required this.onMoveToBottom, + }); + + @override + State createState() => _NodeTableState(); +} + +class _NodeTypeData { + final String originalType; + String currentSelection; + + _NodeTypeData(this.originalType, this.currentSelection); +} + +class _NodeTableState extends State { + PlutoGridStateManager? _stateManager; + int? _selectedRowIndex; + final ScrollController _scrollController = ScrollController(); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + // Header with controls + _buildHeader(), + // Table + Expanded( + child: ScrollConfiguration( + behavior: ScrollConfiguration.of(context).copyWith( + dragDevices: { + PointerDeviceKind.touch, + PointerDeviceKind.mouse, + PointerDeviceKind.stylus, + PointerDeviceKind.trackpad, + }, + ), + child: Scrollbar( + controller: _scrollController, + thickness: 12.0, + thumbVisibility: true, + interactive: true, + child: SingleChildScrollView( + controller: _scrollController, + scrollDirection: Axis.horizontal, + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.4, + child: PlutoGrid( + key: ValueKey('pluto_grid_${widget.members.length}'), + configuration: MyPlutoGridConfiguration(), + columns: _buildColumns(), + mode: PlutoGridMode.normal, + rows: widget.members.isEmpty ? [] : _buildRows(widget.members), + noRowsWidget: const Center(child: Text('')), + onLoaded: (PlutoGridOnLoadedEvent event) { + _stateManager = event.stateManager; + _stateManager?.setSelectingMode(PlutoGridSelectingMode.row); + _stateManager?.addListener(_handleRowSelection); + }, + onRowDoubleTap: (event) { + _selectedRowIndex = event.rowIdx; + }, + ), + ), + ), + ), + ), + ), + ], + ); + } + + Widget _buildHeader() { + final canMoveUp = _selectedRowIndex != null && _selectedRowIndex! > 0; + final canMoveDown = _selectedRowIndex != null && _selectedRowIndex! < widget.members.length - 1; + final canDelete = _selectedRowIndex != null; + + return Padding( + padding: const EdgeInsets.all(2.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('已添加节点', style: TextStyle(fontWeight: FontWeight.bold)), + Row( + children: [ + IconButton( + icon: Icon( + Icons.arrow_upward, + size: 14, + color: canMoveUp ? Colors.blue : Colors.grey, + ), + onPressed: canMoveUp ? () => widget.onMoveMember(_selectedRowIndex!, -1) : null, + tooltip: '上移一行', + ), + IconButton( + icon: Icon( + Icons.vertical_align_top, + size: 14, + color: canMoveUp ? Colors.blue : Colors.grey, + ), + onPressed: canMoveUp ? () => widget.onMoveToTop(_selectedRowIndex!) : null, + tooltip: '上移到顶', + ), + IconButton( + icon: Icon( + Icons.arrow_downward, + size: 14, + color: canMoveDown ? Colors.blue : Colors.grey, + ), + onPressed: canMoveDown ? () => widget.onMoveMember(_selectedRowIndex!, 1) : null, + tooltip: '下移一行', + ), + IconButton( + icon: Icon( + Icons.vertical_align_bottom, + size: 14, + color: canMoveDown ? Colors.blue : Colors.grey, + ), + onPressed: canMoveDown ? () => widget.onMoveToBottom(_selectedRowIndex!) : null, + tooltip: '下移到底', + ), + IconButton( + icon: Icon(Icons.delete, size: 14, color: canDelete ? Colors.red : Colors.grey), + onPressed: canDelete ? () => widget.onDeleteMember(_selectedRowIndex!) : null, + tooltip: '删除行', + ), + ], + ), + ], + ), + ); + } + + void _handleRowSelection() { + if (_stateManager?.currentRow == null) return; + setState(() { + _selectedRowIndex = _stateManager?.currentRow?.sortIdx; + }); + } + + final Map _nodeTypeData = {}; + + List _buildColumns() { + return [ + MyPlutoColumn(title: '#', field: 'rowNum', width: 40), + MyPlutoColumn(title: '成员', field: 'content', width: 300), + MyPlutoDropdownColumn( + title: '操作类型', + field: 'type', + width: 120, + optionsBuilder: (rowKey) { + final data = _nodeTypeData[rowKey as ValueKey]; + return _getOptions(data?.originalType ?? ''); + }, + ), + ]; + } + + List _buildRows(List members) { + return members + .asMap() + .map((index, member) { + final rowKey = ValueKey('${member.hashCode}_$index'); + final options = _getOptions(member.value); + final initialValue = options.isNotEmpty ? options.first : ''; + + _nodeTypeData[rowKey] = _NodeTypeData(member.value, initialValue); + + return MapEntry( + index, + PlutoRow( + key: rowKey, + cells: { + 'rowNum': PlutoCell(value: index + 1), + 'content': PlutoCell(value: member.title), + 'type': PlutoCell(value: initialValue), + }, + ), + ); + }) + .values + .toList(); + } + + List _getOptions(String type) { + switch (type) { + case 'Atom': + case 'Business': + return ['普通调用', '事务调用']; + case 'UFTTable': + return ['遍历记录', '获取记录', '插入记录', '修改记录', '删除记录']; + case 'Component': + return ['遍历组件', '获取组件', '插入组件', '修改组件', '尾部插入组件']; + default: + return ['']; // 确保返回非空列表 + } + } + + @override + void dispose() { + _stateManager?.removeListener(_handleRowSelection); + _scrollController.dispose(); + super.dispose(); + } +} diff --git a/win_text_editor/lib/shared/components/my_pluto_dropdown_column.dart b/win_text_editor/lib/shared/components/my_pluto_dropdown_column.dart new file mode 100644 index 0000000..72535a6 --- /dev/null +++ b/win_text_editor/lib/shared/components/my_pluto_dropdown_column.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:pluto_grid/pluto_grid.dart'; + +class MyPlutoDropdownColumn extends PlutoColumn { + final List Function(Key rowKey)? optionsBuilder; + + MyPlutoDropdownColumn({ + required String title, + required String field, + this.optionsBuilder, + double width = 200, + bool checkable = false, + bool enableSorting = false, + bool autoSize = false, + PlutoColumnTextAlign textAlign = PlutoColumnTextAlign.start, + }) : super( + title: title, + field: field, + type: PlutoColumnType.text(defaultValue: ''), + width: width, + enableRowChecked: checkable, + enableContextMenu: false, + enableEditingMode: true, + enableSorting: enableSorting, + readOnly: false, + backgroundColor: Colors.grey[100], + textAlign: textAlign, + titleTextAlign: PlutoColumnTextAlign.center, + suppressedAutoSize: !autoSize, + renderer: (rendererContext) { + final rowKey = rendererContext.row.key; + final options = optionsBuilder?.call(rowKey) ?? []; + final currentValue = rendererContext.row.cells[field]!.value.toString(); + + return DropdownButton( + value: options.contains(currentValue) + ? currentValue + : (options.isNotEmpty ? options.first : ''), + items: options + .map((value) => DropdownMenuItem( + value: value, + child: Text(value), + )) + .toList(), + onChanged: (newValue) { + if (newValue != null) { + rendererContext.stateManager.changeCellValue( + rendererContext.row.cells[field]!, + newValue, + ); + } + }, + // 美化样式 + icon: const Padding( + padding: EdgeInsets.only(right: 8.0), + child: Icon(Icons.arrow_drop_down, size: 24), + ), + iconSize: 24, + isExpanded: true, + underline: Container(), // 移除下划线 + style: TextStyle( + fontSize: 14, + color: Colors.black87, + ), + dropdownColor: Colors.white, + elevation: 2, + borderRadius: BorderRadius.circular(4), + // 对齐方式 + alignment: Alignment.centerLeft, + ); + }, + ); +}