8 changed files with 308 additions and 141 deletions
@ -0,0 +1,51 @@
@@ -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<UftComponent> { |
||||
ComponentSource( |
||||
List<UftComponent> uftComponents, { |
||||
required Null Function(dynamic index, dynamic isSelected) onSelectionChanged, |
||||
}) : super(uftComponents, onSelectionChanged: onSelectionChanged); |
||||
|
||||
@override |
||||
List<DataGridRow> get rows => |
||||
items |
||||
.map( |
||||
(component) => DataGridRow( |
||||
cells: [ |
||||
DataGridCell<bool>(columnName: 'select', value: component.isSelected), |
||||
DataGridCell<int>(columnName: 'id', value: component.id), |
||||
DataGridCell<String>(columnName: 'name', value: component.name), |
||||
DataGridCell<String>(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<Widget>((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(), |
||||
); |
||||
} |
||||
} |
@ -1,19 +1,85 @@
@@ -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<Field> fields; |
||||
Future<List<UftComponent>> 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 = <UftComponent>[]; |
||||
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 = <Field>[]; |
||||
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; |
||||
} |
||||
} |
||||
} |
||||
|
@ -0,0 +1,106 @@
@@ -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<T extends SelectableItem>( |
||||
BuildContext context, |
||||
SelectableDataSource<T> 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<bool>( |
||||
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, |
||||
); |
||||
} |
||||
} |
||||
}, |
||||
), |
||||
); |
||||
}, |
||||
), |
||||
), |
||||
], |
||||
), |
||||
); |
||||
} |
||||
} |
Loading…
Reference in new issue