feat(client): 出入库单详情抽屉明细增加生产日期+批次号列(两端,抽屉加宽 660,原型同步)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,8 +60,9 @@ class StockInItem {
|
||||
costPrice: (json['cost_price'] as num?)?.toDouble() ?? 0,
|
||||
costAmount: (json['cost_amount'] as num?)?.toDouble() ?? 0,
|
||||
returnedQuantity: (json['returned_quantity'] as num?)?.toDouble() ?? 0,
|
||||
batchNo: json['batch_no'] as String?,
|
||||
productionDate: json['production_date'] as String?,
|
||||
// 批次号/生产日期:优先明细行快照,回退 product 关联(对齐 stock_out.dart 写法)
|
||||
batchNo: lineOrProduct('batch_no', 'batch_no'),
|
||||
productionDate: lineOrProduct('production_date', 'production_date'),
|
||||
productName: lineOrProduct('product_name', 'name'),
|
||||
productCode: lineOrProduct('product_code', 'code'),
|
||||
productSeries: lineOrProduct('series', 'series'),
|
||||
|
||||
@@ -414,7 +414,8 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
// 下拉刷新 = 桌面「刷新」
|
||||
onRefresh: () async {
|
||||
ref.read(stockInListProvider.notifier).reload();
|
||||
await Future<void>.delayed(const Duration(milliseconds: 600));
|
||||
await Future<void>.delayed(
|
||||
const Duration(milliseconds: 600));
|
||||
},
|
||||
mobileCards:
|
||||
orders.map((o) => _orderCard(context, o)).toList(),
|
||||
@@ -995,6 +996,10 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
||||
price: it.costPrice == 0 ? '待定价' : _num.format(it.costPrice),
|
||||
amount: it.costAmount == 0 ? '待定价' : _num.format(it.costAmount),
|
||||
pending: it.costPrice == 0,
|
||||
productionDate: it.productionDate != null
|
||||
? it.productionDate!.substring(0, 10)
|
||||
: null,
|
||||
batchNo: it.batchNo,
|
||||
))
|
||||
.toList(),
|
||||
totalText: o.costTotal != null ? _money.format(o.costTotal) : '—',
|
||||
|
||||
@@ -1076,6 +1076,10 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
||||
cost: admin ? _num.format(it.costPrice) : null,
|
||||
profit: admin ? (pending ? '待定价' : _num.format(profit)) : null,
|
||||
pending: pending,
|
||||
productionDate: it.productionDate != null
|
||||
? it.productionDate!.substring(0, 10)
|
||||
: null,
|
||||
batchNo: it.batchNo,
|
||||
);
|
||||
}).toList(),
|
||||
totalText: o.saleTotal != null ? _money.format(o.saleTotal) : '—',
|
||||
|
||||
@@ -41,7 +41,7 @@ Future<T?> showOrderDetailDrawer<T>(
|
||||
barrierColor: _kDrawerMask,
|
||||
transitionDuration: const Duration(milliseconds: 250),
|
||||
pageBuilder: (ctx, _, __) {
|
||||
final w = math.min(600.0, MediaQuery.of(ctx).size.width * 0.94);
|
||||
final w = math.min(660.0, MediaQuery.of(ctx).size.width * 0.94);
|
||||
return Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Material(
|
||||
@@ -63,7 +63,8 @@ Future<T?> showOrderDetailDrawer<T>(
|
||||
);
|
||||
}
|
||||
|
||||
/// 抽屉明细行(对齐原型 .lines .lr,5 列:商品·编码 / 系列·规格 / 数量 / 单价 / 金额)。
|
||||
/// 抽屉明细行(对齐原型 .lines .lr,入库 6 列 / 出库管理员 8 列:
|
||||
/// 商品·编码 / 系列·规格 / 生产日期·批次号 / 数量 / 单价(/成本价/售价/总售价) / 利润)。
|
||||
class OrderLine {
|
||||
final String name;
|
||||
final String code;
|
||||
@@ -77,6 +78,9 @@ class OrderLine {
|
||||
final String? cost;
|
||||
final String? profit;
|
||||
final bool pending; // 单价<=0:单价/金额显示待定价样式
|
||||
// 生产日期 / 批次号(2026-07-14,出入库单详情明细新增双行列)
|
||||
final String? productionDate;
|
||||
final String? batchNo;
|
||||
const OrderLine({
|
||||
required this.name,
|
||||
required this.code,
|
||||
@@ -88,6 +92,8 @@ class OrderLine {
|
||||
this.cost,
|
||||
this.profit,
|
||||
this.pending = false,
|
||||
this.productionDate,
|
||||
this.batchNo,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -327,8 +333,11 @@ class OrderDetailDrawer extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
/// 窄屏明细行(原型:左 商品名 + 系列·×数量 小字,右 金额 mono / 待定价徽章)。
|
||||
/// 窄屏明细行(原型:左 商品名 + 系列·×数量 小字,中 生产日期/批次号双行列
|
||||
/// (两值都空则不渲染,避免空占位),右 金额 mono / 待定价徽章)。
|
||||
Widget _mLineRow(dynamic t, OrderLine l, bool last) {
|
||||
final hasDateBatch = (l.productionDate?.isNotEmpty ?? false) ||
|
||||
(l.batchNo?.isNotEmpty ?? false);
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 11),
|
||||
decoration: last
|
||||
@@ -351,6 +360,30 @@ class OrderDetailDrawer extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
if (hasDateBatch) ...[
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
(l.productionDate?.isNotEmpty ?? false)
|
||||
? l.productionDate!
|
||||
: '—',
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsSm,
|
||||
fontFamily: AppFonts.mono,
|
||||
fontFamilyFallback: AppFonts.monoFallback,
|
||||
color: t.text)),
|
||||
Text((l.batchNo?.isNotEmpty ?? false) ? l.batchNo! : '—',
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsXs,
|
||||
fontFamily: AppFonts.mono,
|
||||
fontFamilyFallback: AppFonts.monoFallback,
|
||||
color: t.faint)),
|
||||
],
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 10),
|
||||
l.pending
|
||||
? const DsTextIconBadge('待定价')
|
||||
@@ -450,6 +483,7 @@ class _LinesTable extends StatelessWidget {
|
||||
c1: Text('商品 · 编码',
|
||||
style: _hStyle(t), overflow: TextOverflow.ellipsis),
|
||||
c2: Text('系列 / 规格', style: _hStyle(t)),
|
||||
cDate: Text('生产日期 / 批次号', style: _hStyle(t)),
|
||||
c3: Text('数量', textAlign: TextAlign.right, style: _hStyle(t)),
|
||||
c4: _withProfit
|
||||
? Text('成本价', textAlign: TextAlign.right, style: _hStyle(t))
|
||||
@@ -506,6 +540,30 @@ class _LinesTable extends StatelessWidget {
|
||||
TextStyle(fontSize: AppDims.fsXs, color: t.muted)),
|
||||
],
|
||||
),
|
||||
cDate: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
(lines[i].productionDate?.isNotEmpty ?? false)
|
||||
? lines[i].productionDate!
|
||||
: '—',
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsSm,
|
||||
fontFamily: AppFonts.mono,
|
||||
fontFamilyFallback: AppFonts.monoFallback,
|
||||
color: t.text)),
|
||||
Text(
|
||||
(lines[i].batchNo?.isNotEmpty ?? false)
|
||||
? lines[i].batchNo!
|
||||
: '—',
|
||||
style: TextStyle(
|
||||
fontSize: AppDims.fsXs,
|
||||
fontFamily: AppFonts.mono,
|
||||
fontFamilyFallback: AppFonts.monoFallback,
|
||||
color: t.faint)),
|
||||
],
|
||||
),
|
||||
c3: Text(lines[i].qty,
|
||||
textAlign: TextAlign.right,
|
||||
style: const TextStyle(
|
||||
@@ -557,11 +615,12 @@ class _LinesTable extends StatelessWidget {
|
||||
color: accent ? t.text : t.text));
|
||||
}
|
||||
|
||||
// 5 列布局(对齐原型 grid 1fr 124 40 64 78);c6/c7 非空时为出库 7 列形态
|
||||
// (数量/成本价/售价/总售价/利润,数值列等宽收窄)。
|
||||
// 6 列布局(入库,对齐原型 grid 1fr 110 72 40 64 78);c6/c7 非空时为出库
|
||||
// 8 列形态(商品/系列规格/生产日期批次号/数量/成本价/售价/总售价/利润,数值列等宽收窄)。
|
||||
Widget _lineRow(BuildContext context,
|
||||
{required Widget c1,
|
||||
required Widget c2,
|
||||
required Widget cDate,
|
||||
required Widget c3,
|
||||
required Widget c4,
|
||||
required Widget c5,
|
||||
@@ -573,15 +632,18 @@ class _LinesTable extends StatelessWidget {
|
||||
children: [
|
||||
Expanded(child: c1),
|
||||
const SizedBox(width: 8),
|
||||
// 5 列(入库)时中间列加宽左移:商品列不再吃掉全部余宽,
|
||||
// 6 列(入库)时中间列加宽左移:商品列不再吃掉全部余宽,
|
||||
// 「进价(单瓶)」表头一行放得下(2026-07-03 用户反馈)。
|
||||
SizedBox(width: wide ? 100 : 124, child: c2),
|
||||
// 2026-07-14:新增「生产日期/批次号」列,入库侧挤压 124→110、88→78 腾位。
|
||||
SizedBox(width: wide ? 100 : 110, child: c2),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(width: 72, child: cDate),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(width: wide ? 32 : 40, child: c3),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(width: wide ? 62 : 88, child: c4),
|
||||
SizedBox(width: wide ? 62 : 78, child: c4),
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(width: wide ? 62 : 88, child: c5),
|
||||
SizedBox(width: wide ? 62 : 78, child: c5),
|
||||
if (c6 != null) ...[
|
||||
const SizedBox(width: 8),
|
||||
SizedBox(width: 62, child: c6),
|
||||
|
||||
Reference in New Issue
Block a user