feat(client): 库存移动卡片品名后缀生产日期 + 商品抽屉生产日期行
- 原型先行(L1):mobile-atoms 新增 .mc-nm .mc-date 原子(xs muted mono 左距8), index.html 登记,m-inventory 卡片模板与详情 sheet 同步补生产日期 - MCard 新增 nmSuffix 插槽镜像 .mc-date;库存窄屏卡片品名后缀生产日期小字 - Product 模型补 production_date(只读侧,不进 toJson);商品抽屉信息区 新增「生产日期」行(分类后、当前库存前,mono) - 重生成库存移动 golden ×6(inventory_list_mobile / m_inventory 三主题) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@@ -14,6 +14,8 @@ class Product {
|
||||
final double? purchasePrice;
|
||||
final double? salePrice;
|
||||
final int? minStock;
|
||||
// 生产日期(YYYY-MM-DD,后端 Date 序列化,空串=未填)
|
||||
final String? productionDate;
|
||||
final String? description;
|
||||
final String? remark;
|
||||
final Map<String, dynamic>? customFields;
|
||||
@@ -41,6 +43,7 @@ class Product {
|
||||
this.purchasePrice,
|
||||
this.salePrice,
|
||||
this.minStock,
|
||||
this.productionDate,
|
||||
this.description,
|
||||
this.remark,
|
||||
this.customFields,
|
||||
@@ -75,6 +78,7 @@ class Product {
|
||||
minStock: json['min_stock'] != null
|
||||
? (json['min_stock'] as num).toInt()
|
||||
: null,
|
||||
productionDate: json['production_date'] as String?,
|
||||
description: json['description'] as String?,
|
||||
remark: json['remark'] as String?,
|
||||
customFields: json['custom_fields'] as Map<String, dynamic>?,
|
||||
|
||||
@@ -613,6 +613,8 @@ class _InventoryListScreenState extends ConsumerState<InventoryListScreen> {
|
||||
return MCard(
|
||||
onTap: item.productId != null ? () => _openEditor(item) : null,
|
||||
nm: item.productName.isEmpty ? '-' : item.productName,
|
||||
// 生产日期小字缀在品名后(原型 .mc-date,2026-07-10 用户口径)
|
||||
nmSuffix: (item.productionDate ?? '').isEmpty ? null : item.productionDate,
|
||||
sub: sub.isEmpty ? null : sub,
|
||||
badges: [DsTextIconBadge(status)],
|
||||
amtWidget: Text.rich(
|
||||
|
||||
@@ -101,6 +101,9 @@ class MCard extends StatelessWidget {
|
||||
/// mc-nm 标题(600 heading)。
|
||||
final String nm;
|
||||
|
||||
/// mc-date 标题后缀(原型 .mc-nm .mc-date:xs muted mono,左距 8);null 不渲染。
|
||||
final String? nmSuffix;
|
||||
|
||||
/// mc-sub 副行(xs muted mono);null 不渲染。
|
||||
final String? sub;
|
||||
|
||||
@@ -119,6 +122,7 @@ class MCard extends StatelessWidget {
|
||||
const MCard({
|
||||
super.key,
|
||||
required this.nm,
|
||||
this.nmSuffix,
|
||||
this.sub,
|
||||
this.badges = const [],
|
||||
this.amt,
|
||||
@@ -151,13 +155,32 @@ class MCard extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(nm,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsBody,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: t.heading)),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||
textBaseline: TextBaseline.alphabetic,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(nm,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsBody,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: t.heading)),
|
||||
),
|
||||
if (nmSuffix != null) ...[
|
||||
const SizedBox(width: 8),
|
||||
Text(nmSuffix!,
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsXs,
|
||||
color: t.muted,
|
||||
fontFamily: AppFonts.mono,
|
||||
fontFamilyFallback:
|
||||
AppFonts.monoFallback)),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (sub != null) ...[
|
||||
const SizedBox(height: 3),
|
||||
Text(sub!,
|
||||
|
||||
@@ -394,6 +394,9 @@ class _ProductEditorDrawerState extends ConsumerState<_ProductEditorDrawer> {
|
||||
if ((p.series ?? '').isNotEmpty) rows.add(('系列', b(p.series!)));
|
||||
if ((p.spec ?? '').isNotEmpty) rows.add(('规格', b(p.spec!)));
|
||||
if ((p.categoryName ?? '').isNotEmpty) rows.add(('分类', b(p.categoryName!)));
|
||||
if ((p.productionDate ?? '').isNotEmpty) {
|
||||
rows.add(('生产日期', b(p.productionDate!, mono: true)));
|
||||
}
|
||||
if (widget.qty != null) {
|
||||
rows.add((
|
||||
'当前库存',
|
||||
|
||||
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 235 KiB |
|
Before Width: | Height: | Size: 205 KiB After Width: | Height: | Size: 229 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 142 KiB After Width: | Height: | Size: 156 KiB |