Browse Source

组件代码生成完成。

master
hejl 2 months ago
parent
commit
fadd59d1dc
  1. 93
      win_text_editor/assets/config/uft_macro_list.yaml
  2. 8
      win_text_editor/lib/framework/services/macro_template_service.dart
  3. 39
      win_text_editor/lib/modules/uft_component/controllers/component_source.dart
  4. 14
      win_text_editor/lib/modules/uft_component/controllers/uft_component_controller.dart
  5. 16
      win_text_editor/lib/modules/uft_component/models/uft_component.dart
  6. 45
      win_text_editor/lib/modules/uft_component/services/uft_component_service.dart
  7. 73
      win_text_editor/lib/modules/uft_component/widgets/component_grid.dart
  8. 105
      win_text_editor/lib/modules/uft_component/widgets/uft_component_left_side.dart
  9. 2
      win_text_editor/lib/modules/uft_component/widgets/uft_component_right_side.dart

93
win_text_editor/assets/config/uft_macro_list.yaml

@ -1,9 +1,8 @@
templates: templates:
获取记录: 获取记录:
header: "<M>[获取记录][{{tableName}}({{keyName}})"
body: | body: |
][ <M>[获取记录][{{tableName}}({{keyName}})][
{{#keyFields}} {{#keyFields}}
{{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}}
{{/keyFields}} {{/keyFields}}
@ -11,8 +10,7 @@ templates:
{{#selectedFields}} {{#selectedFields}}
{{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}}
{{/selectedFields}} {{/selectedFields}}
] ]
footer: |
[继续执行] [继续执行]
[记录为空][{{tableName}}] [记录为空][{{tableName}}]
{ {
@ -23,19 +21,15 @@ templates:
获取记录数: 获取记录数:
header: "[获取记录数][{{tableName}}][@count]" body: "[获取记录数][{{tableName}}][@count]"
body: ""
footer: ""
插入记录: 插入记录:
header: "<C>[插入记录][{{tableName}}]["
body: | body: |
][ <C>[插入记录][{{tableName}}][
{{#fields}} {{#fields}}
{{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}}
{{/fields}} {{/fields}}
] ]
footer: |
[索引冲突] [索引冲突]
{ {
[继续执行] [继续执行]
@ -60,14 +54,12 @@ templates:
} }
修改记录: 修改记录:
header: "<M>[获取记录][{{tableName}}({{keyName}})"
body: | body: |
][ <M>[获取记录][{{tableName}}({{keyName}})][
{{#keyFields}} {{#keyFields}}
{{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}}
{{/keyFields}} {{/keyFields}}
] ]
footer: |
[继续执行] [继续执行]
[记录不为空][{{tableName}}] [记录不为空][{{tableName}}]
{ {
@ -85,14 +77,12 @@ templates:
} }
删除记录: 删除记录:
header: "<M>[获取记录][{{tableName}}({{keyName}})"
body: | body: |
][ <M>[获取记录][{{tableName}}({{keyName}})][
{{#keyFields}} {{#keyFields}}
{{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}}
{{/keyFields}} {{/keyFields}}
] ]
footer: |
[继续执行] [继续执行]
[记录不为空][{{tableName}}] [记录不为空][{{tableName}}]
{ {
@ -100,9 +90,8 @@ templates:
} }
遍历记录: 遍历记录:
header: "<M>[遍历记录开始][{{tableName}}({{selectIndexOrKey.name}})"
body: | body: |
][ <M>[遍历记录开始][{{tableName}}({{selectIndexOrKey.name}})][
{{#selectIndexOrKey.fields}} {{#selectIndexOrKey.fields}}
{{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}}
{{/selectIndexOrKey.fields}} {{/selectIndexOrKey.fields}}
@ -110,9 +99,69 @@ templates:
{{#selectedFields}} {{#selectedFields}}
{{name}} = @{{name}} {{^isLast}}, {{/isLast}} {{name}} = @{{name}} {{^isLast}}, {{/isLast}}
{{/selectedFields}} {{/selectedFields}}
] ]
footer: |
{ {
//balabala //balabala
} }
[遍历记录结束] [遍历记录结束]
插入组件:
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}}
]

8
win_text_editor/lib/framework/services/macro_template_service.dart

@ -47,13 +47,7 @@ class MacroTemplateService {
final template = getTemplate(templateName); final template = getTemplate(templateName);
if (template == null) throw Exception('Template $templateName not found'); if (template == null) throw Exception('Template $templateName not found');
buffer.writeln( buffer.writeln(_render(template['body'], context));
[
_render(template['header'], context),
_render(template['body'], context),
_render(template['footer'], context),
].join('\n'),
);
buffer.writeln(); buffer.writeln();
} }

39
win_text_editor/lib/modules/uft_component/controllers/component_source.dart

@ -15,7 +15,6 @@ class ComponentSource extends SelectableDataSource<UftComponent> {
.map( .map(
(component) => DataGridRow( (component) => DataGridRow(
cells: [ cells: [
DataGridCell<bool>(columnName: 'select', value: component.isSelected),
DataGridCell<int>(columnName: 'id', value: component.id), DataGridCell<int>(columnName: 'id', value: component.id),
DataGridCell<String>(columnName: 'name', value: component.name), DataGridCell<String>(columnName: 'name', value: component.name),
DataGridCell<String>(columnName: 'chineseName', value: component.chineseName), DataGridCell<String>(columnName: 'chineseName', value: component.chineseName),
@ -24,28 +23,30 @@ class ComponentSource extends SelectableDataSource<UftComponent> {
) )
.toList(); .toList();
get data => items;
@override @override
DataGridRowAdapter buildRow(DataGridRow row) { DataGridRowAdapter buildRow(DataGridRow row) {
final rowIndex = effectiveRows.indexOf(row); final rowIndex = effectiveRows.indexOf(row);
final isSelected = items[rowIndex].isSelected;
return DataGridRowAdapter( return DataGridRowAdapter(
cells: color: isSelected ? Colors.lightBlue.withOpacity(0.3) : null,
row.getCells().map<Widget>((cell) { cells: [
if (cell.columnName == 'select') { Container(
return Center( alignment: Alignment.centerLeft,
child: Checkbox( padding: const EdgeInsets.symmetric(horizontal: 8),
value: items[rowIndex].isSelected, child: Text(row.getCells()[0].value.toString(), overflow: TextOverflow.ellipsis),
onChanged: (value) => toggleRowSelection(rowIndex, value), ),
), Container(
); alignment: Alignment.centerLeft,
} padding: const EdgeInsets.symmetric(horizontal: 8),
return Container( child: Text(row.getCells()[1].value.toString(), overflow: TextOverflow.ellipsis),
alignment: Alignment.centerLeft, ),
padding: const EdgeInsets.symmetric(horizontal: 8), Container(
child: Text(cell.value.toString(), overflow: TextOverflow.ellipsis), alignment: Alignment.centerLeft,
); padding: const EdgeInsets.symmetric(horizontal: 8),
}).toList(), child: Text(row.getCells()[2].value.toString(), overflow: TextOverflow.ellipsis),
),
],
); );
} }
} }

14
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/modules/uft_component/services/uft_component_service.dart';
import 'package:win_text_editor/shared/models/std_filed.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/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'; import 'package:win_text_editor/shared/base/base_content_controller.dart';
class UftComponentController extends BaseContentController { class UftComponentController extends BaseContentController {
@ -110,5 +109,16 @@ class UftComponentController extends BaseContentController {
super.dispose(); 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();
}
}
} }

16
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'; import 'package:win_text_editor/shared/models/std_filed.dart';
class UftComponent implements SelectableItem { class UftComponent implements SelectableItem {
@ -5,6 +6,7 @@ class UftComponent implements SelectableItem {
final String name; final String name;
final String chineseName; final String chineseName;
final List<Field> fields; final List<Field> fields;
List<Index>? indexes;
@override @override
bool isSelected; bool isSelected;
@ -14,11 +16,13 @@ class UftComponent implements SelectableItem {
required this.chineseName, required this.chineseName,
required this.fields, required this.fields,
this.isSelected = false, this.isSelected = false,
this.indexes,
}); });
List<Field> get selectFields => fields.where((field) => field.isSelected).toList(); List<Field> get selectFields => fields.where((field) => field.isSelected).toList();
Map<String, dynamic> toMap() { Map<String, dynamic> toMap() {
var index = indexes?.firstOrNull;
return { return {
'id': id, 'id': id,
'name': name, 'name': name,
@ -47,6 +51,18 @@ class UftComponent implements SelectableItem {
}, },
) )
.toList(), .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,
},
}; };
} }
} }

45
win_text_editor/lib/modules/uft_component/services/uft_component_service.dart

@ -2,6 +2,7 @@
import 'dart:io'; 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/modules/uft_component/models/uft_component.dart';
import 'package:win_text_editor/shared/data/std_fields_cache.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/shared/models/std_filed.dart';
@ -44,20 +45,48 @@ class UftComponentService {
// 4. // 4.
final components = <UftComponent>[]; final components = <UftComponent>[];
int id = 0; int id = 1;
for (var node in componentNodes) { for (var node in componentNodes) {
if (node.findElements("items").isEmpty) continue; if (node.findElements("items").isEmpty) continue;
id++; id++;
final name = node.getAttribute('name') ?? ''; final name = node.getAttribute('name') ?? '';
final chineseName = node.getAttribute('chineseName') ?? ''; final chineseName = node.getAttribute('chineseName') ?? '';
// 5. Process properties (fields)
final childNodes = node.children;
final fields = <Field>[]; final fields = <Field>[];
List<Index>? indexes = [];
int index = 1; int index = 1;
// 5.
final indexNodes = node.findElements('indexs');
if (indexNodes.isNotEmpty) {
final attrnames = <String>[];
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) { for (final childNode in childNodes) {
if (childNode is xml.XmlElement) {
if (childNode.name.local != 'items') continue; // items节点
}
final name = childNode.getAttribute('name') ?? ''; final name = childNode.getAttribute('name') ?? '';
if (name.isEmpty) continue; //
// //
final stdField = StdFieldsCache.getData(name); final stdField = StdFieldsCache.getData(name);
fields.add( 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; return components;

73
win_text_editor/lib/modules/uft_component/widgets/component_grid.dart

@ -1,15 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_datagrid/datagrid.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/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 ComponentSource componentsSource;
final Function(int index, bool isSelected)? onSelectionChanged; final Function(int index, bool isSelected)? onSelectionChanged;
const ComponentGrid({super.key, required this.componentsSource, this.onSelectionChanged}); const ComponentGrid({super.key, required this.componentsSource, this.onSelectionChanged});
@override
State<ComponentGrid> createState() => ComponentGridState();
}
class ComponentGridState extends State<ComponentGrid> {
final DataGridController _dataGridController = DataGridController();
Container _buildGridHeader(String text) { Container _buildGridHeader(String text) {
return Container( return Container(
alignment: Alignment.center, alignment: Alignment.center,
@ -18,26 +23,9 @@ class ComponentGrid extends StatelessWidget {
); );
} }
Widget _buildCheckboxHeader<T extends SelectableItem>( void scrollToIndex(int index) {
BuildContext context, _dataGridController.scrollToRow(index.toDouble());
SelectableDataSource<T> dataSource, _dataGridController.selectedIndex = index;
) {
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 @override
@ -52,21 +40,13 @@ class ComponentGrid extends StatelessWidget {
return SizedBox( return SizedBox(
width: constraints.maxWidth, width: constraints.maxWidth,
child: SfDataGrid( child: SfDataGrid(
source: componentsSource, controller: _dataGridController,
source: widget.componentsSource,
gridLinesVisibility: GridLinesVisibility.both, gridLinesVisibility: GridLinesVisibility.both,
headerGridLinesVisibility: GridLinesVisibility.both, headerGridLinesVisibility: GridLinesVisibility.both,
columnWidthMode: ColumnWidthMode.fitByCellValue, columnWidthMode: ColumnWidthMode.fitByCellValue,
selectionMode: SelectionMode.none, selectionMode: SelectionMode.single,
columns: [ 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: 'id', label: _buildGridHeader('序号'), minimumWidth: 80),
GridColumn( GridColumn(
columnName: 'name', columnName: 'name',
@ -79,18 +59,19 @@ class ComponentGrid extends StatelessWidget {
minimumWidth: 120, minimumWidth: 120,
), ),
], ],
onCellTap: (details) { onSelectionChanged: (addedRows, removedRows) {
if (details.column.columnName == 'select') { if (addedRows.isNotEmpty) {
final rowIndex = details.rowColumnIndex.rowIndex - 1; //
if (rowIndex >= 0 && rowIndex < componentsSource.items.length) { final rowIndex = widget.componentsSource.effectiveRows
componentsSource.toggleRowSelection( .indexOf(addedRows.first);
rowIndex,
!componentsSource.items[rowIndex].isSelected, if (rowIndex >= 0 && rowIndex < widget.componentsSource.items.length) {
); //
onSelectionChanged?.call( for (var i = 0; i < widget.componentsSource.items.length; i++) {
rowIndex, widget.componentsSource.items[i].isSelected = (i == rowIndex);
componentsSource.items[rowIndex].isSelected, }
); widget.componentsSource.notifyListeners();
widget.onSelectionChanged?.call(rowIndex, true);
} }
} }
}, },

105
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/field_data_source.dart';
import 'package:win_text_editor/shared/uft_std_fields/fields_data_grid.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; final UftComponentController controller;
const UftComponentLeftSide({super.key, required this.controller}); const UftComponentLeftSide({super.key, required this.controller});
Widget _buildTextFieldRow(String label, String value) { @override
return Row( State<UftComponentLeftSide> createState() => _UftComponentLeftSideState();
mainAxisSize: MainAxisSize.min, }
children: [
Text('$label:'), class _UftComponentLeftSideState extends State<UftComponentLeftSide> {
SizedBox( final TextEditingController _searchController = TextEditingController();
width: 150, final GlobalKey<ComponentGridState> _gridKey = GlobalKey();
child: TextField( final FocusNode _searchFocusNode = FocusNode(); //
controller: TextEditingController(text: value),
readOnly: true, Widget _buildSearchField() {
decoration: const InputDecoration(isDense: true, contentPadding: EdgeInsets.all(8)), 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
const SizedBox(height: 8), const SizedBox(height: 8),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
const SizedBox(width: 8), const Text(' 标准组件', style: TextStyle(fontWeight: FontWeight.bold)),
_buildTextFieldRow('名称', controller.componentName), _buildSearchField(),
_buildTextFieldRow('中文名', controller.chineseName),
], ],
), ),
Expanded( Expanded(
flex: 6, flex: 3,
child: ComponentGrid( child: ComponentGrid(
componentsSource: controller.componentsSource as ComponentSource, key: _gridKey,
componentsSource: widget.controller.componentsSource as ComponentSource,
onSelectionChanged: (index, isSelected) { onSelectionChanged: (index, isSelected) {
controller.updateFieldSelection(index, isSelected); widget.controller.updateComponentSelection(index, isSelected);
}, },
), ),
), ),
Expanded( Expanded(
flex: 3, flex: 3,
child: FieldsDataGrid( child: FieldsDataGrid(
fieldsSource: controller.fieldsSource as FieldsDataSource, fieldsSource: widget.controller.fieldsSource as FieldsDataSource,
onSelectionChanged: (index, isSelected) { onSelectionChanged: (index, isSelected) {
controller.updateFieldSelection(index, isSelected); widget.controller.updateFieldSelection(index, isSelected);
}, },
), ),
), ),
], ],
); );
} }
@override
void dispose() {
_searchController.dispose();
_searchFocusNode.dispose(); //
super.dispose();
}
} }

2
win_text_editor/lib/modules/uft_component/widgets/uft_component_right_side.dart

@ -75,7 +75,7 @@ class _UftComponentRightSideState extends State<UftComponentRightSide> {
} }
Widget _buildCheckboxSection() { Widget _buildCheckboxSection() {
final operations = ['获取记录', '获取记录数', '插入记录', '修改记录', '删除记录', '遍历记录']; final operations = ['插入组件', '修改组件', '获取组件', '遍历组件', '组件大小', '尾部插入组件'];
return SizedBox( return SizedBox(
width: double.infinity, width: double.infinity,

Loading…
Cancel
Save