fix(pre-planting): 合并详情显示实际份数和金额,不再硬编码
问题:合并详情页"合并份数"显示订单条数(7)而非实际份数(10), "总价值"硬编码 订单数×1887,每笔订单金额也硬编码 1,887。 修复: 后端 getMergeDetail: - 新增 sourceOrders[] 含每笔订单的 portionCount + totalAmount - 新增 totalPortions(总份数)和 totalAmount(总金额) 前端 PrePlantingMerge model: - 新增 MergeSourceOrder 类 - 新增 sourceOrders/totalPortions/totalAmount 字段 前端合并详情页: - "合并份数"用 totalPortions 替代 sourceOrderNos.length - "总价值"用 totalAmount 替代硬编码计算 - 来源订单列表显示每笔实际金额和份数(多份时显示"N份"前缀) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a8e06e2eda
commit
532be9a561
|
|
@ -397,6 +397,9 @@ export class PrePlantingApplicationService {
|
||||||
): Promise<{
|
): Promise<{
|
||||||
mergeNo: string;
|
mergeNo: string;
|
||||||
sourceOrderNos: string[];
|
sourceOrderNos: string[];
|
||||||
|
sourceOrders: { orderNo: string; portionCount: number; totalAmount: number }[];
|
||||||
|
totalPortions: number;
|
||||||
|
totalAmount: number;
|
||||||
treeCount: number;
|
treeCount: number;
|
||||||
contractStatus: string;
|
contractStatus: string;
|
||||||
contractSignedAt: Date | null;
|
contractSignedAt: Date | null;
|
||||||
|
|
@ -408,9 +411,28 @@ export class PrePlantingApplicationService {
|
||||||
if (!merge || merge.userId !== userId) {
|
if (!merge || merge.userId !== userId) {
|
||||||
throw new NotFoundException(`合并记录不存在: ${mergeNo}`);
|
throw new NotFoundException(`合并记录不存在: ${mergeNo}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询源订单的份数和金额
|
||||||
|
const orderRecords = await tx.prePlantingOrder.findMany({
|
||||||
|
where: { orderNo: { in: merge.sourceOrderNos } },
|
||||||
|
select: { orderNo: true, portionCount: true, totalAmount: true },
|
||||||
|
orderBy: { createdAt: 'asc' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const sourceOrders = orderRecords.map((o) => ({
|
||||||
|
orderNo: o.orderNo,
|
||||||
|
portionCount: o.portionCount,
|
||||||
|
totalAmount: Number(o.totalAmount),
|
||||||
|
}));
|
||||||
|
const totalPortions = sourceOrders.reduce((sum, o) => sum + o.portionCount, 0);
|
||||||
|
const totalAmount = sourceOrders.reduce((sum, o) => sum + o.totalAmount, 0);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mergeNo: merge.mergeNo,
|
mergeNo: merge.mergeNo,
|
||||||
sourceOrderNos: merge.sourceOrderNos,
|
sourceOrderNos: merge.sourceOrderNos,
|
||||||
|
sourceOrders,
|
||||||
|
totalPortions,
|
||||||
|
totalAmount,
|
||||||
treeCount: merge.treeCount,
|
treeCount: merge.treeCount,
|
||||||
contractStatus: merge.contractStatus,
|
contractStatus: merge.contractStatus,
|
||||||
contractSignedAt: merge.contractSignedAt,
|
contractSignedAt: merge.contractSignedAt,
|
||||||
|
|
|
||||||
|
|
@ -165,9 +165,33 @@ class PrePlantingOrder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 预种合并记录(5 份 → 1 棵树)
|
/// 预种合并记录(5 份 → 1 棵树)
|
||||||
|
/// 合并来源订单详情
|
||||||
|
class MergeSourceOrder {
|
||||||
|
final String orderNo;
|
||||||
|
final int portionCount;
|
||||||
|
final double totalAmount;
|
||||||
|
|
||||||
|
MergeSourceOrder({
|
||||||
|
required this.orderNo,
|
||||||
|
required this.portionCount,
|
||||||
|
required this.totalAmount,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory MergeSourceOrder.fromJson(Map<String, dynamic> json) {
|
||||||
|
return MergeSourceOrder(
|
||||||
|
orderNo: json['orderNo'] ?? '',
|
||||||
|
portionCount: json['portionCount'] ?? 1,
|
||||||
|
totalAmount: (json['totalAmount'] ?? 0).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PrePlantingMerge {
|
class PrePlantingMerge {
|
||||||
final String mergeNo;
|
final String mergeNo;
|
||||||
final List<String> sourceOrderNos; // 5 笔来源订单号
|
final List<String> sourceOrderNos; // 来源订单号列表
|
||||||
|
final List<MergeSourceOrder> sourceOrders; // 来源订单详情(含份数和金额)
|
||||||
|
final int totalPortions; // 合并总份数(应为 10)
|
||||||
|
final double totalAmount; // 合并总金额(各订单实际支付之和)
|
||||||
final int treeCount; // 合并产生的树数(固定 1)
|
final int treeCount; // 合并产生的树数(固定 1)
|
||||||
final PrePlantingContractStatus contractStatus;
|
final PrePlantingContractStatus contractStatus;
|
||||||
final DateTime? contractSignedAt;
|
final DateTime? contractSignedAt;
|
||||||
|
|
@ -179,6 +203,9 @@ class PrePlantingMerge {
|
||||||
PrePlantingMerge({
|
PrePlantingMerge({
|
||||||
required this.mergeNo,
|
required this.mergeNo,
|
||||||
required this.sourceOrderNos,
|
required this.sourceOrderNos,
|
||||||
|
this.sourceOrders = const [],
|
||||||
|
this.totalPortions = 0,
|
||||||
|
this.totalAmount = 0,
|
||||||
required this.treeCount,
|
required this.treeCount,
|
||||||
required this.contractStatus,
|
required this.contractStatus,
|
||||||
this.contractSignedAt,
|
this.contractSignedAt,
|
||||||
|
|
@ -195,12 +222,19 @@ class PrePlantingMerge {
|
||||||
bool get isMiningEnabled => miningEnabledAt != null;
|
bool get isMiningEnabled => miningEnabledAt != null;
|
||||||
|
|
||||||
factory PrePlantingMerge.fromJson(Map<String, dynamic> json) {
|
factory PrePlantingMerge.fromJson(Map<String, dynamic> json) {
|
||||||
|
final sourceOrders = (json['sourceOrders'] as List<dynamic>?)
|
||||||
|
?.map((e) => MergeSourceOrder.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
return PrePlantingMerge(
|
return PrePlantingMerge(
|
||||||
mergeNo: json['mergeNo'] ?? '',
|
mergeNo: json['mergeNo'] ?? '',
|
||||||
sourceOrderNos: (json['sourceOrderNos'] as List<dynamic>?)
|
sourceOrderNos: (json['sourceOrderNos'] as List<dynamic>?)
|
||||||
?.map((e) => e.toString())
|
?.map((e) => e.toString())
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[],
|
[],
|
||||||
|
sourceOrders: sourceOrders,
|
||||||
|
totalPortions: json['totalPortions'] ?? sourceOrders.fold(0, (sum, o) => sum + o.portionCount),
|
||||||
|
totalAmount: (json['totalAmount'] ?? sourceOrders.fold(0.0, (sum, o) => sum + o.totalAmount)).toDouble(),
|
||||||
treeCount: json['treeCount'] ?? 1,
|
treeCount: json['treeCount'] ?? 1,
|
||||||
contractStatus: _parseContractStatus(json['contractStatus']),
|
contractStatus: _parseContractStatus(json['contractStatus']),
|
||||||
contractSignedAt: json['contractSignedAt'] != null
|
contractSignedAt: json['contractSignedAt'] != null
|
||||||
|
|
|
||||||
|
|
@ -320,11 +320,11 @@ class _PrePlantingMergeDetailPageState
|
||||||
// 信息行
|
// 信息行
|
||||||
_buildDetailRow('合并时间', _formatDateTime(merge.mergedAt)),
|
_buildDetailRow('合并时间', _formatDateTime(merge.mergedAt)),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_buildDetailRow('合并份数', '${merge.sourceOrderNos.length} 份 → 1 棵树'),
|
_buildDetailRow('合并份数', '${merge.totalPortions > 0 ? merge.totalPortions : merge.sourceOrderNos.length} 份 → 1 棵树'),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
_buildDetailRow(
|
_buildDetailRow(
|
||||||
'总价值',
|
'总价值',
|
||||||
'${(merge.sourceOrderNos.length * 1887).toString()} 绿积分',
|
'${merge.totalAmount > 0 ? _formatNumber(merge.totalAmount) : _formatNumber(merge.sourceOrderNos.length * 1887.0)} 绿积分',
|
||||||
),
|
),
|
||||||
if (merge.selectedProvince != null) ...[
|
if (merge.selectedProvince != null) ...[
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
@ -458,7 +458,7 @@ class _PrePlantingMergeDetailPageState
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'来源订单(${merge.sourceOrderNos.length} 笔)',
|
'来源订单(${merge.sourceOrderNos.length} 笔,共 ${merge.totalPortions > 0 ? merge.totalPortions : merge.sourceOrderNos.length} 份)',
|
||||||
style: const TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontFamily: 'Inter',
|
fontFamily: 'Inter',
|
||||||
|
|
@ -467,6 +467,60 @@ class _PrePlantingMergeDetailPageState
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
|
// 优先使用 sourceOrders(含份数和金额),降级用 sourceOrderNos
|
||||||
|
if (merge.sourceOrders.isNotEmpty)
|
||||||
|
...merge.sourceOrders.asMap().entries.map((entry) {
|
||||||
|
final index = entry.key;
|
||||||
|
final order = entry.value;
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: index < merge.sourceOrders.length - 1 ? 8 : 0,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color:
|
||||||
|
const Color(0xFFD4AF37).withValues(alpha: 0.15),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
'${index + 1}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Color(0xFFD4AF37),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
order.orderNo,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: 'Inter',
|
||||||
|
color: Color(0xFF5D4037),
|
||||||
|
),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
'${order.portionCount > 1 ? "${order.portionCount}份 " : ""}${_formatNumber(order.totalAmount)} 绿积分',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 13,
|
||||||
|
color: Color(0xFF745D43),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
else
|
||||||
...merge.sourceOrderNos.asMap().entries.map((entry) {
|
...merge.sourceOrderNos.asMap().entries.map((entry) {
|
||||||
final index = entry.key;
|
final index = entry.key;
|
||||||
final orderNo = entry.value;
|
final orderNo = entry.value;
|
||||||
|
|
@ -507,13 +561,6 @@ class _PrePlantingMergeDetailPageState
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Text(
|
|
||||||
'1,887 绿积分',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 13,
|
|
||||||
color: Color(0xFF745D43),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -650,6 +697,20 @@ class _PrePlantingMergeDetailPageState
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 格式化金额(整数不带小数点,带千分位)
|
||||||
|
String _formatNumber(double value) {
|
||||||
|
if (value == value.truncateToDouble()) {
|
||||||
|
return value.toInt().toString().replaceAllMapped(
|
||||||
|
RegExp(r'(\d)(?=(\d{3})+(?!\d))'),
|
||||||
|
(m) => '${m[1]},',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value.toStringAsFixed(2).replaceAllMapped(
|
||||||
|
RegExp(r'(\d)(?=(\d{3})+(?!\d))'),
|
||||||
|
(m) => '${m[1]},',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// 格式化日期时间
|
/// 格式化日期时间
|
||||||
String _formatDateTime(DateTime dt) {
|
String _formatDateTime(DateTime dt) {
|
||||||
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-'
|
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-'
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue