Compare commits
No commits in common. '0be8b8cfcbe7be01ff9b4c92ab9ffb08f4c714bd' and 'ecde205e00f80ad532c6d706dfca7bc45f6dbeb4' have entirely different histories.
0be8b8cfcb
...
ecde205e00
33 changed files with 113 additions and 721 deletions
@ -1,2 +1,2 @@ |
|||||||
outline_name_black_list: |
outline_name_black_list: |
||||||
- 历史,日志,名称,比例,数量,金额,次数,属性,对应,分类,姓名,单位,总数,行使,子项,占比,记录,列表,目标,字段,字符串,动作,方式,类型,类别 |
- 历史,日志,名称,比例,数量,金额,次数,属性,对应,分类,姓名,单位,总数,行使,子项,占比,记录,列表,目标,字段,字符串,动作,方式 |
@ -1,79 +0,0 @@ |
|||||||
import 'package:flutter/material.dart'; |
|
||||||
|
|
||||||
void main2() => runApp(MyApp()); |
|
||||||
|
|
||||||
class MyApp extends StatelessWidget { |
|
||||||
@override |
|
||||||
Widget build(BuildContext context) { |
|
||||||
return MaterialApp(home: DragIconDemo()); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
class DragIconDemo extends StatefulWidget { |
|
||||||
@override |
|
||||||
_DragIconDemoState createState() => _DragIconDemoState(); |
|
||||||
} |
|
||||||
|
|
||||||
class _DragIconDemoState extends State<DragIconDemo> { |
|
||||||
// 跟踪图标在哪个容器中(1或2) |
|
||||||
int iconContainer = 1; |
|
||||||
|
|
||||||
@override |
|
||||||
Widget build(BuildContext context) { |
|
||||||
return Scaffold( |
|
||||||
appBar: AppBar(title: Text('图标拖拽示例')), |
|
||||||
body: Row( |
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly, |
|
||||||
children: [ |
|
||||||
// 第一个容器 |
|
||||||
buildContainer( |
|
||||||
containerNumber: 1, |
|
||||||
child: iconContainer == 1 ? buildDraggableIcon() : null, |
|
||||||
), |
|
||||||
|
|
||||||
// 第二个容器 |
|
||||||
buildContainer( |
|
||||||
containerNumber: 2, |
|
||||||
child: iconContainer == 2 ? buildDraggableIcon() : null, |
|
||||||
), |
|
||||||
], |
|
||||||
), |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// 构建可拖拽图标 |
|
||||||
Widget buildDraggableIcon() { |
|
||||||
return Draggable<int>( |
|
||||||
data: iconContainer, // 传递当前容器编号作为数据 |
|
||||||
feedback: const Icon(Icons.star, size: 50, color: Colors.amber), |
|
||||||
childWhenDragging: const Opacity( |
|
||||||
opacity: 0.5, |
|
||||||
child: Icon(Icons.star, size: 50, color: Colors.amber), |
|
||||||
), |
|
||||||
child: const Icon(Icons.star, size: 50, color: Colors.amber), |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
// 构建容器(同时也是拖放目标) |
|
||||||
Widget buildContainer({required int containerNumber, Widget? child}) { |
|
||||||
return DragTarget<int>( |
|
||||||
builder: (context, candidateData, rejectedData) { |
|
||||||
return Container( |
|
||||||
width: 150, |
|
||||||
height: 150, |
|
||||||
decoration: BoxDecoration( |
|
||||||
border: Border.all(color: Colors.blue, width: 2), |
|
||||||
borderRadius: BorderRadius.circular(10), |
|
||||||
), |
|
||||||
child: Center(child: child), |
|
||||||
); |
|
||||||
}, |
|
||||||
onWillAcceptWithDetails: (data) => true, // 接受任何拖拽数据 |
|
||||||
onAcceptWithDetails: (data) { |
|
||||||
setState(() { |
|
||||||
iconContainer = containerNumber; // 更新图标位置 |
|
||||||
}); |
|
||||||
}, |
|
||||||
); |
|
||||||
} |
|
||||||
} |
|
@ -1,25 +0,0 @@ |
|||||||
import 'package:win_text_editor/framework/controllers/logger.dart'; |
|
||||||
import 'package:win_text_editor/modules/outline/models/outline_node.dart'; |
|
||||||
import 'package:win_text_editor/shared/base/base_content_controller.dart'; |
|
||||||
|
|
||||||
class CodeCreaterController extends BaseContentController { |
|
||||||
List<OutlineNode> members = []; |
|
||||||
|
|
||||||
@override |
|
||||||
void onOpenFile(String filePath, {dynamic appendArg}) { |
|
||||||
// TODO: implement onOpenFile |
|
||||||
} |
|
||||||
|
|
||||||
@override |
|
||||||
void onOpenFolder(String folderPath) { |
|
||||||
// TODO: implement onOpenFolder |
|
||||||
} |
|
||||||
|
|
||||||
@override |
|
||||||
void onDropOutlineNode(OutlineNode node) { |
|
||||||
members.add(node); |
|
||||||
notifyListeners(); |
|
||||||
} |
|
||||||
|
|
||||||
genCodeString(List<String> list) {} |
|
||||||
} |
|
@ -1,240 +0,0 @@ |
|||||||
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/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; |
|
||||||
const CodeCreaterView({super.key, required this.tabId}); |
|
||||||
|
|
||||||
@override |
|
||||||
State<CodeCreaterView> createState() => _CodeCreaterViewState(); |
|
||||||
} |
|
||||||
|
|
||||||
class _CodeCreaterViewState extends State<CodeCreaterView> { |
|
||||||
late final CodeCreaterController _controller; |
|
||||||
bool _isControllerFromTabManager = false; |
|
||||||
PlutoGridStateManager? _stateManager; |
|
||||||
|
|
||||||
final List<String> operations = ['逻辑服务', '逻辑函数', '原子服务', '原子函数']; |
|
||||||
final TextEditingController _codeController = TextEditingController(); |
|
||||||
|
|
||||||
String? _selectedOperation = '原子函数'; |
|
||||||
|
|
||||||
get tabManager => Provider.of<TabItemsController>(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(); |
|
||||||
|
|
||||||
final controllerFromManager = tabManager.getController(widget.tabId); |
|
||||||
if (controllerFromManager != null) { |
|
||||||
_controller = controllerFromManager; |
|
||||||
_isControllerFromTabManager = true; |
|
||||||
} else { |
|
||||||
_controller = CodeCreaterController(); |
|
||||||
_isControllerFromTabManager = false; |
|
||||||
tabManager.registerController(widget.tabId, _controller); |
|
||||||
} |
|
||||||
|
|
||||||
_controller.addListener(_handleControllerUpdate); // 添加监听 |
|
||||||
} |
|
||||||
|
|
||||||
void _handleControllerUpdate() { |
|
||||||
setState(() {}); // 控制器通知时刷新 UI |
|
||||||
} |
|
||||||
|
|
||||||
@override |
|
||||||
void dispose() { |
|
||||||
_controller.removeListener(_handleControllerUpdate); // 移除监听 |
|
||||||
_scrollController.dispose(); // 释放滚动控制器 |
|
||||||
if (!_isControllerFromTabManager) { |
|
||||||
_controller.dispose(); |
|
||||||
} |
|
||||||
_codeController.dispose(); |
|
||||||
|
|
||||||
super.dispose(); |
|
||||||
} |
|
||||||
|
|
||||||
@override |
|
||||||
Widget build(BuildContext context) { |
|
||||||
return Row( |
|
||||||
children: [ |
|
||||||
// 左侧 PlutoGrid (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; |
|
||||||
}, |
|
||||||
), |
|
||||||
), |
|
||||||
), |
|
||||||
), |
|
||||||
), |
|
||||||
), |
|
||||||
), |
|
||||||
// 右侧 CodeGenerationSection (60%) |
|
||||||
Expanded( |
|
||||||
flex: 6, |
|
||||||
child: Padding( |
|
||||||
padding: const EdgeInsets.all(4.0), |
|
||||||
child: CodeGenerationSection( |
|
||||||
title: '生成代码', |
|
||||||
codeController: _codeController, |
|
||||||
onNodeDropped: (node) => _controller.onDropOutlineNode(node), |
|
||||||
child: OperationRadioSection( |
|
||||||
operations: operations, |
|
||||||
selectedOperation: _selectedOperation, |
|
||||||
onOperationSelected: _selectOperation, |
|
||||||
), |
|
||||||
), |
|
||||||
), |
|
||||||
), |
|
||||||
], |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
List<PlutoColumn> _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; |
|
||||||
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<PlutoRow> _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 _selectOperation(String? operation) { |
|
||||||
setState(() { |
|
||||||
_selectedOperation = operation; |
|
||||||
_updateDisplay(); |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
void _updateDisplay() { |
|
||||||
if (_selectedOperation != null) { |
|
||||||
_codeController.text = _controller.genCodeString([_selectedOperation!]) ?? ''; |
|
||||||
} else { |
|
||||||
_codeController.text = ''; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void _deleteMember(BuildContext context, PlutoRow row, OutlineNode result) {} |
|
||||||
} |
|
@ -1,33 +0,0 @@ |
|||||||
import 'package:path/path.dart' as path; |
|
||||||
import 'package:win_text_editor/modules/outline/models/outline_node.dart'; |
|
||||||
import 'package:win_text_editor/modules/outline/services/component_service.dart'; |
|
||||||
import 'package:win_text_editor/modules/outline/services/uft_object_service.dart'; |
|
||||||
|
|
||||||
class CodePartner { |
|
||||||
final String? name; |
|
||||||
final List<String> fields; |
|
||||||
|
|
||||||
CodePartner({required this.name, required this.fields}); |
|
||||||
|
|
||||||
factory CodePartner.fromOutlineNode(OutlineNode node) { |
|
||||||
String? name; |
|
||||||
List<String> fields = []; |
|
||||||
switch (node.value) { |
|
||||||
case "UFTTable": |
|
||||||
final List<String>? values = UftObjectService.uftObjectMap[node.title]; |
|
||||||
if (values != null && values.isNotEmpty) { |
|
||||||
name = path.basenameWithoutExtension(values[0]); |
|
||||||
fields.addAll(values.sublist(1)); |
|
||||||
} |
|
||||||
break; |
|
||||||
case "Component": |
|
||||||
name = node.name; |
|
||||||
final List<String>? values = ComponentService.componentFieldMap[node.name]; |
|
||||||
if (values != null && values.isNotEmpty) { |
|
||||||
fields.addAll(values.sublist(1)); |
|
||||||
} |
|
||||||
break; |
|
||||||
} |
|
||||||
return CodePartner(name: name, fields: fields); |
|
||||||
} |
|
||||||
} |
|
@ -1,10 +1,7 @@ |
|||||||
import 'package:flutter/material.dart'; |
import 'package:flutter/material.dart'; |
||||||
import 'package:win_text_editor/modules/outline/models/outline_node.dart'; |
|
||||||
|
|
||||||
abstract class BaseContentController with ChangeNotifier { |
abstract class BaseContentController with ChangeNotifier { |
||||||
void onOpenFolder(String folderPath); |
void onOpenFolder(String folderPath); |
||||||
|
|
||||||
void onOpenFile(String filePath, {dynamic appendArg}); |
void onOpenFile(String filePath, {dynamic appendArg}); |
||||||
|
|
||||||
void onDropOutlineNode(OutlineNode node); |
|
||||||
} |
} |
||||||
|
@ -1,22 +0,0 @@ |
|||||||
import 'package:mustache_template/mustache.dart'; |
|
||||||
|
|
||||||
void main() { |
|
||||||
final template = Template(''' |
|
||||||
{{#value}}Value exists: {{value}},{{/value}} |
|
||||||
others... |
|
||||||
'''); |
|
||||||
|
|
||||||
print(template.renderString({'value': '有内容'})); |
|
||||||
|
|
||||||
// ✅ 正确做法1:key 完全不存在 |
|
||||||
print(template.renderString({'value': ''})); |
|
||||||
// 输出: "No value provided" |
|
||||||
|
|
||||||
// ✅ 正确做法2:显式设置为 null(需确保非空安全环境) |
|
||||||
print(template.renderString({'value': null})); |
|
||||||
// 输出: "No value provided" |
|
||||||
|
|
||||||
// ❌ 以下会报错: |
|
||||||
// print(template.renderString({'value': ''})); // 空字符串 |
|
||||||
// print(template.renderString({'value': 'Hello'})); // 非空字符串 |
|
||||||
} |
|
Loading…
Reference in new issue