diff --git a/documents/PB UFT模块迁移方案.docx b/documents/PB UFT模块迁移方案.docx index 84b5453..e378b76 100644 Binary files a/documents/PB UFT模块迁移方案.docx and b/documents/PB UFT模块迁移方案.docx differ diff --git a/win_text_editor/lib/modules/content_search/widgets/results_view.dart b/win_text_editor/lib/modules/content_search/widgets/results_view.dart index 3b34372..bee127c 100644 --- a/win_text_editor/lib/modules/content_search/widgets/results_view.dart +++ b/win_text_editor/lib/modules/content_search/widgets/results_view.dart @@ -132,7 +132,7 @@ class ResultsView extends StatelessWidget { headerGridLinesVisibility: GridLinesVisibility.both, allowSorting: false, allowFiltering: false, - columnWidthMode: ColumnWidthMode.none, + columnWidthMode: ColumnWidthMode.fill, isScrollbarAlwaysShown: true, allowColumnsResizing: true, // 关键开关 columnResizeMode: ColumnResizeMode.onResizeEnd, diff --git a/win_text_editor/lib/modules/data_format/controllers/data_format_controller.dart b/win_text_editor/lib/modules/data_format/controllers/data_format_controller.dart index b4073fa..11eec65 100644 --- a/win_text_editor/lib/modules/data_format/controllers/data_format_controller.dart +++ b/win_text_editor/lib/modules/data_format/controllers/data_format_controller.dart @@ -1,8 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:win_text_editor/modules/data_format/services/mustache_service.dart'; import 'package:win_text_editor/shared/base/base_content_controller.dart'; import 'grid_view_controller.dart'; class DataFormatController extends BaseContentController { final GridViewController gridController; + String _templateText = ''; + + // 使用ValueNotifier来管理结果文本 + final ValueNotifier _resultTextNotifier = ValueNotifier(''); + + // 暴露notifier给外部访问 + ValueNotifier get resultTextNotifier => _resultTextNotifier; //---------------初始化方法---- @@ -13,11 +22,71 @@ class DataFormatController extends BaseContentController { //设置跨控制器状态协同 void _setupCrossControllerCommunication() {} - //----------------业务入口方法----- + // Set template text from the editor + void setTemplateText(String text) { + _templateText = text; + } + + // Apply the template to grid data + void applyTemplate() { + if (_templateText.isEmpty) { + _resultTextNotifier.value = 'Error: Template is empty'; + return; + } + + // Validate template syntax + if (!MustacheService.validateTemplate(_templateText)) { + _resultTextNotifier.value = 'Error: Invalid Mustache template syntax'; + return; + } + + // Get variables from template + final templateVars = MustacheService.getTemplateVariables(_templateText); + if (templateVars.isEmpty) { + _resultTextNotifier.value = 'Error: No variables found in template'; + return; + } - //--------------------私有方法--------- + // Get grid data + final gridData = gridController.csvData; + if (gridData.isEmpty || gridData.length < 2) { + _resultTextNotifier.value = 'Error: No CSV data loaded'; + return; + } + + final headers = gridData.first; + final dataRows = gridData.sublist(1); + + // Check if all template variables exist in headers + final missingVars = templateVars.where((varName) => !headers.contains(varName)).toList(); + if (missingVars.isNotEmpty) { + _resultTextNotifier.value = 'Error: Template variables not found in CSV headers: ${missingVars.join(', ')}'; + return; + } + + // Process each row + final resultBuffer = StringBuffer(); + for (final row in dataRows) { + final rowData = {}; + for (var i = 0; i < headers.length; i++) { + if (i < row.length) { + rowData[headers[i]] = row[i]; + } else { + rowData[headers[i]] = ''; + } + } + + try { + final rendered = MustacheService.applyTemplate(_templateText, rowData); + resultBuffer.writeln(rendered); + } catch (e) { + resultBuffer.writeln('Error processing row: $e'); + } + } + + _resultTextNotifier.value = resultBuffer.toString(); + } - //-----------框架回调-- @override void onOpenFile(String filePath) {} @@ -28,6 +97,7 @@ class DataFormatController extends BaseContentController { @override void dispose() { + _resultTextNotifier.dispose(); // 记得在dispose时释放资源 gridController.dispose(); super.dispose(); } diff --git a/win_text_editor/lib/modules/data_format/controllers/grid_view_controller.dart b/win_text_editor/lib/modules/data_format/controllers/grid_view_controller.dart index 75b05ea..ff175d9 100644 --- a/win_text_editor/lib/modules/data_format/controllers/grid_view_controller.dart +++ b/win_text_editor/lib/modules/data_format/controllers/grid_view_controller.dart @@ -2,6 +2,21 @@ import 'package:win_text_editor/shared/base/safe_notifier.dart'; class GridViewController extends SafeNotifier { + List> _csvData = []; + + List> get csvData => _csvData; + + void setCsvData(List> data) { + _csvData = data; + notifyListeners(); + } + + @override + void dispose() { + // Clean up any resources + super.dispose(); + } + void reset() { // 重置状态的方法 safeNotify(); diff --git a/win_text_editor/lib/modules/data_format/services/mustache_service.dart b/win_text_editor/lib/modules/data_format/services/mustache_service.dart new file mode 100644 index 0000000..0805de8 --- /dev/null +++ b/win_text_editor/lib/modules/data_format/services/mustache_service.dart @@ -0,0 +1,57 @@ +// mustache_service.dart +import 'package:mustache_template/mustache_template.dart'; + +class MustacheService { + // Validate Mustache template syntax + static bool validateTemplate(String template) { + try { + Template(template); + return true; + } catch (e) { + return false; + } + } + + // Get variables from template + static Set getTemplateVariables(String template) { + try { + final parsed = Template(template); + // Use the source to parse variables manually + return _parseVariablesFromSource(parsed.source); + } catch (e) { + return {}; + } + } + + // Apply template to a row of data + static String applyTemplate(String template, Map data) { + try { + return Template(template).renderString(data); + } catch (e) { + return 'Error applying template: $e'; + } + } + + // Helper method to parse variables from template source + static Set _parseVariablesFromSource(String source) { + final variables = {}; + final pattern = RegExp(r'{{\s*([^{}\s]+)\s*}}'); + final matches = pattern.allMatches(source); + + for (final match in matches) { + if (match.groupCount >= 1) { + final variable = match.group(1)!; + // Skip sections and special tags + if (!variable.startsWith('#') && + !variable.startsWith('/') && + !variable.startsWith('^') && + !variable.startsWith('>') && + !variable.startsWith('!')) { + variables.add(variable); + } + } + } + + return variables; + } +} diff --git a/win_text_editor/lib/modules/data_format/widgets/data_format_view.dart b/win_text_editor/lib/modules/data_format/widgets/data_format_view.dart index 6aec06c..ec97bcf 100644 --- a/win_text_editor/lib/modules/data_format/widgets/data_format_view.dart +++ b/win_text_editor/lib/modules/data_format/widgets/data_format_view.dart @@ -39,23 +39,17 @@ class _DataFormatViewState extends State { ], child: Padding( padding: const EdgeInsets.all(8.0), - child: Column(children: [const SizedBox(height: 8), Expanded(child: _buildMainContent())]), - ), - ); - } - - Widget _buildMainContent() { - return Consumer( - builder: (context, controller, _) { - return const Row( + child: Row( children: [ - // 左侧 GridView (50%宽度) - Expanded(flex: 1, child: Card(child: DataGridView())), + Expanded( + flex: 1, + child: Card(child: DataGridView(controller: _controller.gridController)), + ), // 右侧 FormatText 面板 (50%宽度) - Expanded(flex: 1, child: FormatTextPanel()), + Expanded(flex: 1, child: FormatTextPanel(controller: _controller)), ], - ); - }, + ), + ), ); } } diff --git a/win_text_editor/lib/modules/data_format/widgets/format_text_panel.dart b/win_text_editor/lib/modules/data_format/widgets/format_text_panel.dart index dba8700..a402a56 100644 --- a/win_text_editor/lib/modules/data_format/widgets/format_text_panel.dart +++ b/win_text_editor/lib/modules/data_format/widgets/format_text_panel.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:win_text_editor/modules/data_format/controllers/data_format_controller.dart'; import 'package:win_text_editor/shared/components/editor_toolbar.dart'; import 'package:win_text_editor/shared/components/text_editor.dart'; class FormatTextPanel extends StatelessWidget { - const FormatTextPanel({super.key}); + final DataFormatController controller; + + const FormatTextPanel({super.key, required this.controller}); @override Widget build(BuildContext context) { @@ -11,38 +14,46 @@ class FormatTextPanel extends StatelessWidget { child: Column( children: [ // 上部模板编辑器 (固定高度200px) - const SizedBox( + SizedBox( height: 200, - child: TextEditor(tabId: 'format_template', title: 'Mustache模板'), + child: TextEditor( + tabId: 'format_template', + title: 'Mustache模板', + onContentChanged: controller.setTemplateText, + ), ), const SizedBox(height: 6), // 下部结果编辑器 (扩展高度) Expanded( - child: TextEditor( - tabId: 'format_result', - title: '转换结果', - toolbarBuilder: - (context, state) => EditorToolbar( - title: '转换结果', - text: state.currentText, - isLoading: state.isLoading, - showOpenFileButton: false, // 隐藏打开文件按钮 - customButtons: [ - ToolbarButtonConfig( - icon: Icons.code, - tooltip: '格式化', - onPressed: () => _applyFormat(state.currentText), + child: ValueListenableBuilder( + valueListenable: controller.resultTextNotifier, + builder: (context, resultText, _) { + return TextEditor( + tabId: 'format_result', + title: '转换结果', + initialContent: resultText, + toolbarBuilder: + (context, state) => EditorToolbar( + title: '转换结果', + text: state.currentText, + isLoading: state.isLoading, + showOpenFileButton: false, + customButtons: [ + ToolbarButtonConfig( + icon: Icons.code, + tooltip: '格式化', + onPressed: () => controller.applyTemplate(), + ), + ], + onCopyToClipboard: state.copyToClipboard, + onSaveFile: state.saveFile, ), - ], - onCopyToClipboard: state.copyToClipboard, - onSaveFile: state.saveFile, - ), + ); + }, ), ), ], ), ); } - - void _applyFormat(String currentText) {} } diff --git a/win_text_editor/lib/modules/data_format/widgets/grid_view.dart b/win_text_editor/lib/modules/data_format/widgets/grid_view.dart index d700b8b..d9ff106 100644 --- a/win_text_editor/lib/modules/data_format/widgets/grid_view.dart +++ b/win_text_editor/lib/modules/data_format/widgets/grid_view.dart @@ -3,9 +3,12 @@ import 'package:syncfusion_flutter_datagrid/datagrid.dart'; import 'package:file_picker/file_picker.dart'; import 'dart:io'; import 'package:csv/csv.dart'; +import 'package:win_text_editor/modules/data_format/controllers/grid_view_controller.dart'; class DataGridView extends StatefulWidget { - const DataGridView({super.key}); + final GridViewController controller; + + const DataGridView({super.key, required this.controller}); @override State createState() => _DataGridViewState(); @@ -127,6 +130,8 @@ class _DataGridViewState extends State { setState(() { _csvData = dataWithIndex; }); + + widget.controller.setCsvData(dataWithIndex); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('加载CSV文件失败: $e'))); diff --git a/win_text_editor/pubspec.lock b/win_text_editor/pubspec.lock index e2ec863..1c15196 100644 --- a/win_text_editor/pubspec.lock +++ b/win_text_editor/pubspec.lock @@ -248,6 +248,14 @@ packages: url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted version: "1.16.0" + mustache_template: + dependency: "direct main" + description: + name: mustache_template + sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c + url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" + source: hosted + version: "2.0.0" nested: dependency: transitive description: @@ -401,18 +409,18 @@ packages: dependency: transitive description: name: syncfusion_flutter_core - sha256: "9f0a4593f7642b2f106e329734d0e5fc746baf8d0a59495eec586cd0d9ba7d02" + sha256: a2427697bfad5b611db78ea4c4daef82d3350b83c729a8dc37959662a31547f9 url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "22.2.12" + version: "23.2.7" syncfusion_flutter_datagrid: dependency: "direct main" description: name: syncfusion_flutter_datagrid - sha256: ae93228333ebed39bc59c90bc40cfd3d5a0361591a330fe551b355d3a49a265c + sha256: "9f621f6344d2ed7ea3a8d0ff5c145c174f1e227d6d8851290591ceb718e44600" url: "https://mirrors.tuna.tsinghua.edu.cn/dart-pub/" source: hosted - version: "22.2.12" + version: "23.2.7" term_glyph: dependency: transitive description: diff --git a/win_text_editor/pubspec.yaml b/win_text_editor/pubspec.yaml index b943312..768fc1f 100644 --- a/win_text_editor/pubspec.yaml +++ b/win_text_editor/pubspec.yaml @@ -18,10 +18,11 @@ dependencies: expandable: ^5.0.1 collection: ^1.17.0 path: ^1.8.0 - syncfusion_flutter_datagrid: ^22.1.40 + syncfusion_flutter_datagrid: ^23.1.40 flutter_js: ^0.8.3 xml: ^6.5.0 csv: ^6.0.0 + mustache_template: ^2.0.0 dev_dependencies: flutter_test: