feat(client): 基础数据管理新增产地/保质期/储存方式/描述文档 4 个字典维护 Tab
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,16 +22,28 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
final _nameSearchCtrl = TextEditingController();
|
final _nameSearchCtrl = TextEditingController();
|
||||||
final _seriesSearchCtrl = TextEditingController();
|
final _seriesSearchCtrl = TextEditingController();
|
||||||
final _specSearchCtrl = TextEditingController();
|
final _specSearchCtrl = TextEditingController();
|
||||||
|
final _originSearchCtrl = TextEditingController();
|
||||||
|
final _shelfLifeSearchCtrl = TextEditingController();
|
||||||
|
final _storageSearchCtrl = TextEditingController();
|
||||||
|
final _descDocSearchCtrl = TextEditingController();
|
||||||
|
|
||||||
int _namePage = 1; int _namePageSize = 20;
|
int _namePage = 1; int _namePageSize = 20;
|
||||||
int _seriesPage = 1; int _seriesPageSize = 20;
|
int _seriesPage = 1; int _seriesPageSize = 20;
|
||||||
int _specPage = 1; int _specPageSize = 20;
|
int _specPage = 1; int _specPageSize = 20;
|
||||||
|
int _originPage = 1; int _originPageSize = 20;
|
||||||
|
int _shelfLifePage = 1; int _shelfLifePageSize = 20;
|
||||||
|
int _storagePage = 1; int _storagePageSize = 20;
|
||||||
|
int _descDocPage = 1; int _descDocPageSize = 20;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_nameSearchCtrl.dispose();
|
_nameSearchCtrl.dispose();
|
||||||
_seriesSearchCtrl.dispose();
|
_seriesSearchCtrl.dispose();
|
||||||
_specSearchCtrl.dispose();
|
_specSearchCtrl.dispose();
|
||||||
|
_originSearchCtrl.dispose();
|
||||||
|
_shelfLifeSearchCtrl.dispose();
|
||||||
|
_storageSearchCtrl.dispose();
|
||||||
|
_descDocSearchCtrl.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,12 +55,20 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
Tab(text: '商品名称'),
|
Tab(text: '商品名称'),
|
||||||
Tab(text: '系列'),
|
Tab(text: '系列'),
|
||||||
Tab(text: '规格'),
|
Tab(text: '规格'),
|
||||||
|
Tab(text: '产地'),
|
||||||
|
Tab(text: '保质期'),
|
||||||
|
Tab(text: '储存方式'),
|
||||||
|
Tab(text: '描述文档'),
|
||||||
Tab(text: '仓库'),
|
Tab(text: '仓库'),
|
||||||
],
|
],
|
||||||
tabViews: [
|
tabViews: [
|
||||||
_buildNameTab(),
|
_buildNameTab(),
|
||||||
_buildSeriesTab(),
|
_buildSeriesTab(),
|
||||||
_buildSpecTab(),
|
_buildSpecTab(),
|
||||||
|
_buildOriginTab(),
|
||||||
|
_buildShelfLifeTab(),
|
||||||
|
_buildStorageTab(),
|
||||||
|
_buildDescDocTab(),
|
||||||
_buildWarehousesTab(),
|
_buildWarehousesTab(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -455,6 +475,362 @@ class _ProductsScreenState extends ConsumerState<ProductsScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── 产地 tab ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
Widget _buildOriginTab() {
|
||||||
|
final async = ref.watch(productOriginListProvider);
|
||||||
|
return async.when(
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error: (e, _) => _buildError(() => ref.read(productOriginListProvider.notifier).reload()),
|
||||||
|
data: (items) {
|
||||||
|
final keyword = _originSearchCtrl.text.toLowerCase();
|
||||||
|
final filtered = keyword.isEmpty
|
||||||
|
? items
|
||||||
|
: items.where((it) => it.name.toLowerCase().contains(keyword) ||
|
||||||
|
(it.code?.toLowerCase().contains(keyword) ?? false)).toList();
|
||||||
|
final paged = filtered.skip((_originPage - 1) * _originPageSize).take(_originPageSize).toList();
|
||||||
|
return DataTableCard(
|
||||||
|
totalCount: filtered.length,
|
||||||
|
page: _originPage,
|
||||||
|
pageSize: _originPageSize,
|
||||||
|
onPageChanged: (p) => setState(() => _originPage = p),
|
||||||
|
onPageSizeChanged: (s) => setState(() { _originPageSize = s; _originPage = 1; }),
|
||||||
|
toolbar: _buildToolbar(
|
||||||
|
searchCtrl: _originSearchCtrl,
|
||||||
|
hint: '搜索产地/编号',
|
||||||
|
onSearchChanged: () => setState(() => _originPage = 1),
|
||||||
|
onAdd: () => _showOptionDialog(
|
||||||
|
title: '新建产地',
|
||||||
|
hasQuantity: false,
|
||||||
|
onSave: (data) => ref.read(productOriginListProvider.notifier).create(data),
|
||||||
|
),
|
||||||
|
onExport: () => exportExcel(
|
||||||
|
filename: '产地',
|
||||||
|
headers: ['编号', '名称', '备注'],
|
||||||
|
rows: items.map((it) => [it.code ?? '', it.name, it.remark ?? '']).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
mobileCards: paged.map((it) => _optionCard(
|
||||||
|
code: it.code, name: it.name, remark: it.remark,
|
||||||
|
onEdit: () => _showOptionDialog(
|
||||||
|
title: '编辑产地', hasQuantity: false,
|
||||||
|
initial: {'code': it.code ?? '', 'name': it.name, 'remark': it.remark ?? ''},
|
||||||
|
onSave: (data) => ref.read(productOriginListProvider.notifier).updateItem(it.id, data),
|
||||||
|
),
|
||||||
|
onDelete: () => _confirmDelete('删除产地「${it.name}」?',
|
||||||
|
() => ref.read(productOriginListProvider.notifier).delete(it.id)),
|
||||||
|
)).toList(),
|
||||||
|
columns: const [DataColumn(label: Text('编号')), DataColumn(label: Text('名称')), DataColumn(label: Text('备注')), DataColumn(label: Text('操作'))],
|
||||||
|
rows: paged.isEmpty
|
||||||
|
? [DataRow(cells: [const DataCell(SizedBox()), const DataCell(Text('暂无数据', style: TextStyle(color: AppTheme.textSecondary))), const DataCell(SizedBox()), const DataCell(SizedBox())])]
|
||||||
|
: paged.map((it) => DataRow(cells: [
|
||||||
|
DataCell(Text(it.code ?? '-', style: const TextStyle(fontFamily: 'monospace', fontSize: 12, color: AppTheme.textSecondary))),
|
||||||
|
DataCell(Text(it.name, style: const TextStyle(fontWeight: FontWeight.w500))),
|
||||||
|
DataCell(Text(it.remark ?? '-')),
|
||||||
|
DataCell(_actionButtons(
|
||||||
|
onEdit: () => _showOptionDialog(
|
||||||
|
title: '编辑产地', hasQuantity: false,
|
||||||
|
initial: {'code': it.code ?? '', 'name': it.name, 'remark': it.remark ?? ''},
|
||||||
|
onSave: (data) => ref.read(productOriginListProvider.notifier).updateItem(it.id, data),
|
||||||
|
),
|
||||||
|
onDelete: () => _confirmDelete('删除产地「${it.name}」?',
|
||||||
|
() => ref.read(productOriginListProvider.notifier).delete(it.id)),
|
||||||
|
)),
|
||||||
|
])).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 保质期 tab ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
Widget _buildShelfLifeTab() {
|
||||||
|
final async = ref.watch(productShelfLifeListProvider);
|
||||||
|
return async.when(
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error: (e, _) => _buildError(() => ref.read(productShelfLifeListProvider.notifier).reload()),
|
||||||
|
data: (items) {
|
||||||
|
final keyword = _shelfLifeSearchCtrl.text.toLowerCase();
|
||||||
|
final filtered = keyword.isEmpty
|
||||||
|
? items
|
||||||
|
: items.where((it) => it.name.toLowerCase().contains(keyword) ||
|
||||||
|
(it.code?.toLowerCase().contains(keyword) ?? false)).toList();
|
||||||
|
final paged = filtered.skip((_shelfLifePage - 1) * _shelfLifePageSize).take(_shelfLifePageSize).toList();
|
||||||
|
return DataTableCard(
|
||||||
|
totalCount: filtered.length,
|
||||||
|
page: _shelfLifePage,
|
||||||
|
pageSize: _shelfLifePageSize,
|
||||||
|
onPageChanged: (p) => setState(() => _shelfLifePage = p),
|
||||||
|
onPageSizeChanged: (s) => setState(() { _shelfLifePageSize = s; _shelfLifePage = 1; }),
|
||||||
|
toolbar: _buildToolbar(
|
||||||
|
searchCtrl: _shelfLifeSearchCtrl,
|
||||||
|
hint: '搜索保质期/编号',
|
||||||
|
onSearchChanged: () => setState(() => _shelfLifePage = 1),
|
||||||
|
onAdd: () => _showOptionDialog(
|
||||||
|
title: '新建保质期',
|
||||||
|
hasQuantity: false,
|
||||||
|
onSave: (data) => ref.read(productShelfLifeListProvider.notifier).create(data),
|
||||||
|
),
|
||||||
|
onExport: () => exportExcel(
|
||||||
|
filename: '保质期',
|
||||||
|
headers: ['编号', '名称', '备注'],
|
||||||
|
rows: items.map((it) => [it.code ?? '', it.name, it.remark ?? '']).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
mobileCards: paged.map((it) => _optionCard(
|
||||||
|
code: it.code, name: it.name, remark: it.remark,
|
||||||
|
onEdit: () => _showOptionDialog(
|
||||||
|
title: '编辑保质期', hasQuantity: false,
|
||||||
|
initial: {'code': it.code ?? '', 'name': it.name, 'remark': it.remark ?? ''},
|
||||||
|
onSave: (data) => ref.read(productShelfLifeListProvider.notifier).updateItem(it.id, data),
|
||||||
|
),
|
||||||
|
onDelete: () => _confirmDelete('删除保质期「${it.name}」?',
|
||||||
|
() => ref.read(productShelfLifeListProvider.notifier).delete(it.id)),
|
||||||
|
)).toList(),
|
||||||
|
columns: const [DataColumn(label: Text('编号')), DataColumn(label: Text('名称')), DataColumn(label: Text('备注')), DataColumn(label: Text('操作'))],
|
||||||
|
rows: paged.isEmpty
|
||||||
|
? [DataRow(cells: [const DataCell(SizedBox()), const DataCell(Text('暂无数据', style: TextStyle(color: AppTheme.textSecondary))), const DataCell(SizedBox()), const DataCell(SizedBox())])]
|
||||||
|
: paged.map((it) => DataRow(cells: [
|
||||||
|
DataCell(Text(it.code ?? '-', style: const TextStyle(fontFamily: 'monospace', fontSize: 12, color: AppTheme.textSecondary))),
|
||||||
|
DataCell(Text(it.name, style: const TextStyle(fontWeight: FontWeight.w500))),
|
||||||
|
DataCell(Text(it.remark ?? '-')),
|
||||||
|
DataCell(_actionButtons(
|
||||||
|
onEdit: () => _showOptionDialog(
|
||||||
|
title: '编辑保质期', hasQuantity: false,
|
||||||
|
initial: {'code': it.code ?? '', 'name': it.name, 'remark': it.remark ?? ''},
|
||||||
|
onSave: (data) => ref.read(productShelfLifeListProvider.notifier).updateItem(it.id, data),
|
||||||
|
),
|
||||||
|
onDelete: () => _confirmDelete('删除保质期「${it.name}」?',
|
||||||
|
() => ref.read(productShelfLifeListProvider.notifier).delete(it.id)),
|
||||||
|
)),
|
||||||
|
])).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 储存方式 tab ───────────────────────────────────────────
|
||||||
|
|
||||||
|
Widget _buildStorageTab() {
|
||||||
|
final async = ref.watch(productStorageListProvider);
|
||||||
|
return async.when(
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error: (e, _) => _buildError(() => ref.read(productStorageListProvider.notifier).reload()),
|
||||||
|
data: (items) {
|
||||||
|
final keyword = _storageSearchCtrl.text.toLowerCase();
|
||||||
|
final filtered = keyword.isEmpty
|
||||||
|
? items
|
||||||
|
: items.where((it) => it.name.toLowerCase().contains(keyword) ||
|
||||||
|
(it.code?.toLowerCase().contains(keyword) ?? false)).toList();
|
||||||
|
final paged = filtered.skip((_storagePage - 1) * _storagePageSize).take(_storagePageSize).toList();
|
||||||
|
return DataTableCard(
|
||||||
|
totalCount: filtered.length,
|
||||||
|
page: _storagePage,
|
||||||
|
pageSize: _storagePageSize,
|
||||||
|
onPageChanged: (p) => setState(() => _storagePage = p),
|
||||||
|
onPageSizeChanged: (s) => setState(() { _storagePageSize = s; _storagePage = 1; }),
|
||||||
|
toolbar: _buildToolbar(
|
||||||
|
searchCtrl: _storageSearchCtrl,
|
||||||
|
hint: '搜索储存方式/编号',
|
||||||
|
onSearchChanged: () => setState(() => _storagePage = 1),
|
||||||
|
onAdd: () => _showOptionDialog(
|
||||||
|
title: '新建储存方式',
|
||||||
|
hasQuantity: false,
|
||||||
|
onSave: (data) => ref.read(productStorageListProvider.notifier).create(data),
|
||||||
|
),
|
||||||
|
onExport: () => exportExcel(
|
||||||
|
filename: '储存方式',
|
||||||
|
headers: ['编号', '名称', '备注'],
|
||||||
|
rows: items.map((it) => [it.code ?? '', it.name, it.remark ?? '']).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
mobileCards: paged.map((it) => _optionCard(
|
||||||
|
code: it.code, name: it.name, remark: it.remark,
|
||||||
|
onEdit: () => _showOptionDialog(
|
||||||
|
title: '编辑储存方式', hasQuantity: false,
|
||||||
|
initial: {'code': it.code ?? '', 'name': it.name, 'remark': it.remark ?? ''},
|
||||||
|
onSave: (data) => ref.read(productStorageListProvider.notifier).updateItem(it.id, data),
|
||||||
|
),
|
||||||
|
onDelete: () => _confirmDelete('删除储存方式「${it.name}」?',
|
||||||
|
() => ref.read(productStorageListProvider.notifier).delete(it.id)),
|
||||||
|
)).toList(),
|
||||||
|
columns: const [DataColumn(label: Text('编号')), DataColumn(label: Text('名称')), DataColumn(label: Text('备注')), DataColumn(label: Text('操作'))],
|
||||||
|
rows: paged.isEmpty
|
||||||
|
? [DataRow(cells: [const DataCell(SizedBox()), const DataCell(Text('暂无数据', style: TextStyle(color: AppTheme.textSecondary))), const DataCell(SizedBox()), const DataCell(SizedBox())])]
|
||||||
|
: paged.map((it) => DataRow(cells: [
|
||||||
|
DataCell(Text(it.code ?? '-', style: const TextStyle(fontFamily: 'monospace', fontSize: 12, color: AppTheme.textSecondary))),
|
||||||
|
DataCell(Text(it.name, style: const TextStyle(fontWeight: FontWeight.w500))),
|
||||||
|
DataCell(Text(it.remark ?? '-')),
|
||||||
|
DataCell(_actionButtons(
|
||||||
|
onEdit: () => _showOptionDialog(
|
||||||
|
title: '编辑储存方式', hasQuantity: false,
|
||||||
|
initial: {'code': it.code ?? '', 'name': it.name, 'remark': it.remark ?? ''},
|
||||||
|
onSave: (data) => ref.read(productStorageListProvider.notifier).updateItem(it.id, data),
|
||||||
|
),
|
||||||
|
onDelete: () => _confirmDelete('删除储存方式「${it.name}」?',
|
||||||
|
() => ref.read(productStorageListProvider.notifier).delete(it.id)),
|
||||||
|
)),
|
||||||
|
])).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── 描述文档 tab ───────────────────────────────────────────
|
||||||
|
|
||||||
|
Widget _buildDescDocTab() {
|
||||||
|
final async = ref.watch(productDescriptionDocListProvider);
|
||||||
|
return async.when(
|
||||||
|
loading: () => const Center(child: CircularProgressIndicator()),
|
||||||
|
error: (e, _) => _buildError(() => ref.read(productDescriptionDocListProvider.notifier).reload()),
|
||||||
|
data: (items) {
|
||||||
|
final keyword = _descDocSearchCtrl.text.toLowerCase();
|
||||||
|
final filtered = keyword.isEmpty
|
||||||
|
? items
|
||||||
|
: items.where((it) => it.title.toLowerCase().contains(keyword)).toList();
|
||||||
|
final paged = filtered.skip((_descDocPage - 1) * _descDocPageSize).take(_descDocPageSize).toList();
|
||||||
|
return DataTableCard(
|
||||||
|
totalCount: filtered.length,
|
||||||
|
page: _descDocPage,
|
||||||
|
pageSize: _descDocPageSize,
|
||||||
|
onPageChanged: (p) => setState(() => _descDocPage = p),
|
||||||
|
onPageSizeChanged: (s) => setState(() { _descDocPageSize = s; _descDocPage = 1; }),
|
||||||
|
toolbar: _buildToolbar(
|
||||||
|
searchCtrl: _descDocSearchCtrl,
|
||||||
|
hint: '搜索标题',
|
||||||
|
onSearchChanged: () => setState(() => _descDocPage = 1),
|
||||||
|
onAdd: () => _showDescDocDialog(
|
||||||
|
title: '新建描述文档',
|
||||||
|
onSave: (data) => ref.read(productDescriptionDocListProvider.notifier).create(data),
|
||||||
|
),
|
||||||
|
onExport: () => exportExcel(
|
||||||
|
filename: '描述文档',
|
||||||
|
headers: ['标题', '内容', '备注'],
|
||||||
|
rows: items.map((it) => [it.title, it.content ?? '', it.remark ?? '']).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
mobileCards: paged.map((it) => MobileListCard(
|
||||||
|
title: Text(it.title),
|
||||||
|
fields: [
|
||||||
|
if (it.content?.isNotEmpty == true)
|
||||||
|
MobileCardField('内容', it.content),
|
||||||
|
if (it.remark?.isNotEmpty == true)
|
||||||
|
MobileCardField('备注', it.remark),
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => _showDescDocDialog(
|
||||||
|
title: '编辑描述文档',
|
||||||
|
initial: {'title': it.title, 'content': it.content ?? '', 'remark': it.remark ?? ''},
|
||||||
|
onSave: (data) => ref.read(productDescriptionDocListProvider.notifier).updateItem(it.id, data),
|
||||||
|
),
|
||||||
|
child: const Text('编辑', style: TextStyle(fontSize: 13)),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => _confirmDelete('删除描述文档「${it.title}」?',
|
||||||
|
() => ref.read(productDescriptionDocListProvider.notifier).delete(it.id)),
|
||||||
|
child: const Text('删除', style: TextStyle(fontSize: 13, color: AppTheme.danger)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)).toList(),
|
||||||
|
columns: const [DataColumn(label: Text('标题')), DataColumn(label: Text('内容预览')), DataColumn(label: Text('备注')), DataColumn(label: Text('操作'))],
|
||||||
|
rows: paged.isEmpty
|
||||||
|
? [DataRow(cells: [const DataCell(SizedBox()), const DataCell(Text('暂无数据', style: TextStyle(color: AppTheme.textSecondary))), const DataCell(SizedBox()), const DataCell(SizedBox())])]
|
||||||
|
: paged.map((it) {
|
||||||
|
final preview = (it.content ?? '').length > 30
|
||||||
|
? '${it.content!.substring(0, 30)}…'
|
||||||
|
: (it.content ?? '-');
|
||||||
|
return DataRow(cells: [
|
||||||
|
DataCell(Text(it.title, style: const TextStyle(fontWeight: FontWeight.w500))),
|
||||||
|
DataCell(Text(preview, style: const TextStyle(fontSize: 12, color: AppTheme.textSecondary))),
|
||||||
|
DataCell(Text(it.remark ?? '-')),
|
||||||
|
DataCell(_actionButtons(
|
||||||
|
onEdit: () => _showDescDocDialog(
|
||||||
|
title: '编辑描述文档',
|
||||||
|
initial: {'title': it.title, 'content': it.content ?? '', 'remark': it.remark ?? ''},
|
||||||
|
onSave: (data) => ref.read(productDescriptionDocListProvider.notifier).updateItem(it.id, data),
|
||||||
|
),
|
||||||
|
onDelete: () => _confirmDelete('删除描述文档「${it.title}」?',
|
||||||
|
() => ref.read(productDescriptionDocListProvider.notifier).delete(it.id)),
|
||||||
|
)),
|
||||||
|
]);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _showDescDocDialog({
|
||||||
|
required String title,
|
||||||
|
required Future<void> Function(Map<String, dynamic>) onSave,
|
||||||
|
Map<String, dynamic>? initial,
|
||||||
|
}) async {
|
||||||
|
final titleCtrl = TextEditingController(text: initial?['title'] as String? ?? '');
|
||||||
|
final contentCtrl = TextEditingController(text: initial?['content'] as String? ?? '');
|
||||||
|
final remarkCtrl = TextEditingController(text: initial?['remark'] as String? ?? '');
|
||||||
|
final formKey = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
await showAppDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog(
|
||||||
|
title: Text(title),
|
||||||
|
content: SizedBox(
|
||||||
|
width: context.dialogWidth(400),
|
||||||
|
child: Form(
|
||||||
|
key: formKey,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
controller: titleCtrl,
|
||||||
|
decoration: const InputDecoration(labelText: '标题 *(如:酱香型白酒)'),
|
||||||
|
validator: (v) => (v == null || v.trim().isEmpty) ? '请输入标题' : null,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
TextFormField(
|
||||||
|
controller: contentCtrl,
|
||||||
|
decoration: const InputDecoration(labelText: '内容(商品介绍正文)'),
|
||||||
|
maxLines: 5,
|
||||||
|
minLines: 3,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
TextFormField(
|
||||||
|
controller: remarkCtrl,
|
||||||
|
decoration: const InputDecoration(labelText: '备注'),
|
||||||
|
maxLines: 2,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(onPressed: () => Navigator.of(ctx).pop(), child: const Text('取消')),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (!formKey.currentState!.validate()) return;
|
||||||
|
final data = <String, dynamic>{
|
||||||
|
'title': titleCtrl.text.trim(),
|
||||||
|
if (contentCtrl.text.isNotEmpty) 'content': contentCtrl.text.trim(),
|
||||||
|
if (remarkCtrl.text.isNotEmpty) 'remark': remarkCtrl.text.trim(),
|
||||||
|
};
|
||||||
|
Navigator.of(ctx).pop();
|
||||||
|
try {
|
||||||
|
await onSave(data);
|
||||||
|
} catch (e) {
|
||||||
|
if (mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('保存失败:$e'), backgroundColor: AppTheme.danger),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('保存'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ── 仓库 tab ──────────────────────────────────────────────
|
// ── 仓库 tab ──────────────────────────────────────────────
|
||||||
|
|
||||||
Widget _buildWarehousesTab() {
|
Widget _buildWarehousesTab() {
|
||||||
|
|||||||
Reference in New Issue
Block a user