fix(client): 出入库抽屉生产日期截取防空串崩溃
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,16 @@ DateTime? parseYmd(String? s) {
|
|||||||
return DateTime.tryParse(t);
|
return DateTime.tryParse(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 安全截取日期字符串前 10 位(`yyyy-MM-dd`)。
|
||||||
|
///
|
||||||
|
/// null 或空串一律返回 null(视为「无值」,供上层「两值皆空不渲染」的逻辑判断);
|
||||||
|
/// 长度不足 10 原样返回。用于兜底后端 `Date` 零值序列化为 `""`(而非 null)、
|
||||||
|
/// 或历史导入数据缺失生产日期的场景,避免 `''.substring(0, 10)` 抛 RangeError。
|
||||||
|
String? shortDate(String? v) {
|
||||||
|
if (v == null || v.isEmpty) return null;
|
||||||
|
return v.length >= 10 ? v.substring(0, 10) : v;
|
||||||
|
}
|
||||||
|
|
||||||
/// 组合年/月/日为 `yyyy-MM-dd`,自动把超出当月的「日」clamp 回当月最大值
|
/// 组合年/月/日为 `yyyy-MM-dd`,自动把超出当月的「日」clamp 回当月最大值
|
||||||
/// (如 2 月选 31 → 28/29)。任一为空返回 null。
|
/// (如 2 月选 31 → 28/29)。任一为空返回 null。
|
||||||
String? composeYmd(int? year, int? month, int? day) {
|
String? composeYmd(int? year, int? month, int? day) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import '../../core/utils/date_util.dart';
|
||||||
import '../../core/utils/dialog_util.dart';
|
import '../../core/utils/dialog_util.dart';
|
||||||
import '../../core/exceptions.dart';
|
import '../../core/exceptions.dart';
|
||||||
import '../../core/utils/print_util.dart' show LabelData;
|
import '../../core/utils/print_util.dart' show LabelData;
|
||||||
@@ -996,9 +997,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
price: it.costPrice == 0 ? '待定价' : _num.format(it.costPrice),
|
price: it.costPrice == 0 ? '待定价' : _num.format(it.costPrice),
|
||||||
amount: it.costAmount == 0 ? '待定价' : _num.format(it.costAmount),
|
amount: it.costAmount == 0 ? '待定价' : _num.format(it.costAmount),
|
||||||
pending: it.costPrice == 0,
|
pending: it.costPrice == 0,
|
||||||
productionDate: it.productionDate != null
|
productionDate: shortDate(it.productionDate),
|
||||||
? it.productionDate!.substring(0, 10)
|
|
||||||
: null,
|
|
||||||
batchNo: it.batchNo,
|
batchNo: it.batchNo,
|
||||||
))
|
))
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
import 'package:lucide_icons_flutter/lucide_icons.dart';
|
||||||
|
|
||||||
|
import '../../core/utils/date_util.dart';
|
||||||
import '../../core/utils/dialog_util.dart';
|
import '../../core/utils/dialog_util.dart';
|
||||||
import '../../core/exceptions.dart';
|
import '../../core/exceptions.dart';
|
||||||
import '../../core/responsive/responsive.dart';
|
import '../../core/responsive/responsive.dart';
|
||||||
@@ -1076,9 +1077,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
cost: admin ? _num.format(it.costPrice) : null,
|
cost: admin ? _num.format(it.costPrice) : null,
|
||||||
profit: admin ? (pending ? '待定价' : _num.format(profit)) : null,
|
profit: admin ? (pending ? '待定价' : _num.format(profit)) : null,
|
||||||
pending: pending,
|
pending: pending,
|
||||||
productionDate: it.productionDate != null
|
productionDate: shortDate(it.productionDate),
|
||||||
? it.productionDate!.substring(0, 10)
|
|
||||||
: null,
|
|
||||||
batchNo: it.batchNo,
|
batchNo: it.batchNo,
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:jiu_client/core/utils/date_util.dart';
|
||||||
|
|
||||||
|
/// 2026-07-14 Critical 修复回归:出入库详情抽屉构造 OrderLine 时对
|
||||||
|
/// `productionDate` 做 `substring(0, 10)`,只防 null 不防空串——后端
|
||||||
|
/// `Date.MarshalJSON` 对零值日期序列化为 `""`(非 null),历史导入数据里
|
||||||
|
/// 大量商品无生产日期,`''.substring(0, 10)` 会抛 RangeError 导致打开
|
||||||
|
/// 详情抽屉直接崩溃。`shortDate` 需把空串也当「无值」归一为 null。
|
||||||
|
void main() {
|
||||||
|
group('shortDate', () {
|
||||||
|
test('null 返回 null', () {
|
||||||
|
expect(shortDate(null), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('空串返回 null(回归:曾经 substring(0, 10) 抛 RangeError)', () {
|
||||||
|
expect(shortDate(''), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('标准 yyyy-MM-dd 原样截取前 10 位', () {
|
||||||
|
expect(shortDate('2021-01-27'), '2021-01-27');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('带时间戳的日期截取前 10 位', () {
|
||||||
|
expect(shortDate('2021-01-27T00:00:00Z'), '2021-01-27');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('长度不足 10 原样返回', () {
|
||||||
|
expect(shortDate('2021'), '2021');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user