feat(client): 入库表单关联商品属性字典(产地/保质期/储存方式/描述文档)
- product_option.dart: 新增 4 个字典 model(Origin/ShelfLife/Storage/DescriptionDoc) - product_option_repository.dart: 新增 4 个 list 方法(listOrigins/ShelfLives/Storages/DescriptionDocs) - product_option_provider.dart: 新增 4 个 FutureProvider 供表单使用 - product_repository.dart: findOrCreate 加 originId/shelfLifeId/storageId/descriptionDocId 可选参数 - stock_in_form_screen.dart: _ItemRow 加 4 个可选 ID 字段;新增 4 个 SearchableOptionField 方法;移动端卡片视图增加 4 个可选字段;提交时传入属性 ID Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -226,7 +226,15 @@ class _StockInFormScreenState extends ConsumerState<StockInFormScreen> {
|
||||
try {
|
||||
final product = await ref
|
||||
.read(productRepositoryProvider)
|
||||
.findOrCreate(name: name, series: series, spec: spec);
|
||||
.findOrCreate(
|
||||
name: name,
|
||||
series: series,
|
||||
spec: spec,
|
||||
originId: item.selectedOriginId,
|
||||
shelfLifeId: item.selectedShelfLifeId,
|
||||
storageId: item.selectedStorageId,
|
||||
descriptionDocId: item.selectedDescriptionDocId,
|
||||
);
|
||||
item.productId = product.id;
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
@@ -761,6 +769,78 @@ class _StockInFormScreenState extends ConsumerState<StockInFormScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _originField(_ItemRow item) {
|
||||
final asyncOrigins = ref.watch(productOriginListProvider);
|
||||
return asyncOrigins.when(
|
||||
loading: () => const LinearProgressIndicator(),
|
||||
error: (_, __) => const Text('加载失败'),
|
||||
data: (origins) => SearchableOptionField(
|
||||
options: origins
|
||||
.map((o) => OptionItem(id: o.id, name: o.name, code: o.code))
|
||||
.toList(),
|
||||
selectedId: item.selectedOriginId,
|
||||
hint: '选择产地(可选)',
|
||||
dialogTitle: '选择产地',
|
||||
isRequired: false,
|
||||
onChanged: (v) => setState(() => item.selectedOriginId = v),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _shelfLifeField(_ItemRow item) {
|
||||
final asyncShelfLives = ref.watch(productShelfLifeListProvider);
|
||||
return asyncShelfLives.when(
|
||||
loading: () => const LinearProgressIndicator(),
|
||||
error: (_, __) => const Text('加载失败'),
|
||||
data: (shelfLives) => SearchableOptionField(
|
||||
options: shelfLives
|
||||
.map((o) => OptionItem(id: o.id, name: o.name, code: o.code))
|
||||
.toList(),
|
||||
selectedId: item.selectedShelfLifeId,
|
||||
hint: '选择保质期(可选)',
|
||||
dialogTitle: '选择保质期',
|
||||
isRequired: false,
|
||||
onChanged: (v) => setState(() => item.selectedShelfLifeId = v),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _storageField(_ItemRow item) {
|
||||
final asyncStorages = ref.watch(productStorageListProvider);
|
||||
return asyncStorages.when(
|
||||
loading: () => const LinearProgressIndicator(),
|
||||
error: (_, __) => const Text('加载失败'),
|
||||
data: (storages) => SearchableOptionField(
|
||||
options: storages
|
||||
.map((o) => OptionItem(id: o.id, name: o.name, code: o.code))
|
||||
.toList(),
|
||||
selectedId: item.selectedStorageId,
|
||||
hint: '选择储存方式(可选)',
|
||||
dialogTitle: '选择储存方式',
|
||||
isRequired: false,
|
||||
onChanged: (v) => setState(() => item.selectedStorageId = v),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _descriptionDocField(_ItemRow item) {
|
||||
final asyncDocs = ref.watch(productDescriptionDocListProvider);
|
||||
return asyncDocs.when(
|
||||
loading: () => const LinearProgressIndicator(),
|
||||
error: (_, __) => const Text('加载失败'),
|
||||
data: (docs) => SearchableOptionField(
|
||||
options: docs
|
||||
.map((o) => OptionItem(id: o.id, name: o.title))
|
||||
.toList(),
|
||||
selectedId: item.selectedDescriptionDocId,
|
||||
hint: '选择介绍文档(可选)',
|
||||
dialogTitle: '选择介绍文档',
|
||||
isRequired: false,
|
||||
onChanged: (v) => setState(() => item.selectedDescriptionDocId = v),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
int _specQtyOf(_ItemRow item) =>
|
||||
ref.read(productSpecListProvider).valueOrNull
|
||||
?.where((o) => o.id == item.selectedSpecId)
|
||||
@@ -864,6 +944,10 @@ class _StockInFormScreenState extends ConsumerState<StockInFormScreen> {
|
||||
MobileCardField('金额', '¥${amount.toStringAsFixed(2)}'),
|
||||
MobileCardField('批次号', null, valueWidget: _batchField(item)),
|
||||
MobileCardField('生产日期', null, valueWidget: _dateField(item)),
|
||||
MobileCardField('产地', null, valueWidget: _originField(item)),
|
||||
MobileCardField('保质期', null, valueWidget: _shelfLifeField(item)),
|
||||
MobileCardField('储存方式', null, valueWidget: _storageField(item)),
|
||||
MobileCardField('介绍文档', null, valueWidget: _descriptionDocField(item)),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -908,6 +992,11 @@ class _ItemRow {
|
||||
int? selectedNameId;
|
||||
int? selectedSeriesId;
|
||||
int? selectedSpecId;
|
||||
// 商品属性字典(可选,入库时关联到商品)
|
||||
int? selectedOriginId;
|
||||
int? selectedShelfLifeId;
|
||||
int? selectedStorageId;
|
||||
int? selectedDescriptionDocId;
|
||||
final TextEditingController qtyCtrl = TextEditingController(text: '1');
|
||||
final TextEditingController priceCtrl = TextEditingController();
|
||||
final TextEditingController batchNoCtrl = TextEditingController();
|
||||
|
||||
Reference in New Issue
Block a user