fix(pre-planting): 修复预种页面 5 个 UI 问题(纯前端,零后端改动)

=== 问题 1:流水明细合同按钮 500 错误 ===
文件: ledger_detail_page.dart
原因: 预种订单(PPL 前缀)无合同,但流水详情弹窗显示「查看合同/下载合同」按钮
修复: _showTransactionDetail 检测 refOrderId.startsWith('PPL'),
      预种订单传 showContractButtons: false,弹窗不渲染合同按钮区

=== 问题 2:流水备注显示英文 ===
文件: ledger_detail_page.dart
原因: 备注字段存储的是 'Plant payment (from frozen)'(后端写入,不改后端)
修复: _TransactionDetailSheet 展示备注时,若订单号以 PPL 开头则显示「预种」

=== 问题 3:预种明细订单金额单位错误 ===
文件: pre_planting_position_page.dart
修复: '${order.totalAmount.toInt()} USDT' → '${order.totalAmount.toInt()} 绿积分'

=== 问题 4:省市显示数字代码(如 44 · 4401)===
文件: pre_planting_position_page.dart / pre_planting_purchase_page.dart
原因: provinceName/cityName 为 null 时回退显示 provinceCode/cityCode
修复:
  - position 页:条件改为 provinceName != null && cityName != null,无中文名则不显示省市行
  - purchase 页:加载时不再 fallback 到代码;锁定显示无名称时显示「已锁定」;
    购买确认弹窗省市行无名称时显示「-」

=== 问题 5:「合并进度」改为「合成进度」===
文件: pre_planting_position_page.dart / pre_planting_purchase_page.dart
修复: 两处 Text('合并进度') → Text('合成进度')

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
hailin 2026-02-28 06:25:38 -08:00
parent f4c9535e12
commit 2d7b02aa96
3 changed files with 80 additions and 68 deletions

View File

@ -345,9 +345,9 @@ class _PrePlantingPositionPageState
],
),
const SizedBox(height: 16),
//
//
const Text(
'进度',
'进度',
style: TextStyle(
fontSize: 14,
fontFamily: 'Inter',
@ -389,8 +389,8 @@ class _PrePlantingPositionPageState
),
],
),
//
if (pos.hasProvinceCity) ...[
//
if (pos.provinceName != null && pos.cityName != null) ...[
const SizedBox(height: 12),
Row(
children: [
@ -401,7 +401,7 @@ class _PrePlantingPositionPageState
),
const SizedBox(width: 4),
Text(
'${pos.provinceName ?? pos.provinceCode} · ${pos.cityName ?? pos.cityCode}',
'${pos.provinceName!} · ${pos.cityName!}',
style: const TextStyle(
fontSize: 13,
color: Color(0xFF745D43),
@ -594,7 +594,7 @@ class _PrePlantingPositionPageState
const SizedBox(width: 16),
_buildInfoChip(
Icons.monetization_on_outlined,
'${order.totalAmount.toInt()} USDT',
'${order.totalAmount.toInt()} 绿积分',
),
],
),

View File

@ -166,12 +166,12 @@ class _PrePlantingPurchasePageState
_pricePerPortion = pricingConfig.totalPortionPrice.toDouble();
_isLoading = false;
//
// 退
if (position.hasProvinceCity) {
_selectedProvinceCode = position.provinceCode;
_selectedProvinceName = position.provinceName ?? position.provinceCode;
_selectedProvinceName = position.provinceName;
_selectedCityCode = position.cityCode;
_selectedCityName = position.cityName ?? position.cityCode;
_selectedCityName = position.cityName;
}
// 1
@ -445,7 +445,9 @@ class _PrePlantingPurchasePageState
const SizedBox(height: 8),
_buildDialogRow(
'省市',
'${_selectedProvinceName ?? "-"} · ${_selectedCityName ?? "-"}',
_selectedProvinceName != null && _selectedCityName != null
? '$_selectedProvinceName · $_selectedCityName'
: '-',
),
if (_position != null) ...[
const Divider(height: 24, color: Color(0x338B5A2B)),
@ -884,7 +886,7 @@ class _PrePlantingPurchasePageState
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const Text(
'进度',
'进度',
style: TextStyle(
fontSize: 16,
fontFamily: 'Inter',
@ -1037,7 +1039,9 @@ class _PrePlantingPurchasePageState
children: [
Text(
_hasSelectedProvinceCity
? '$_selectedProvinceName · $_selectedCityName'
? (_selectedProvinceName != null && _selectedCityName != null
? '$_selectedProvinceName · $_selectedCityName'
: '已锁定')
: '点击选择省份和城市',
style: TextStyle(
fontSize: 16,

View File

@ -1116,14 +1116,18 @@ class _LedgerDetailPageState extends ConsumerState<LedgerDetailPage>
Future<void> _showTransactionDetail(LedgerEntry entry) async {
if (entry.refOrderId == null) return;
// PPL
final bool isPrePlanting = entry.refOrderId!.startsWith('PPL');
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => _TransactionDetailSheet(
entry: entry,
onViewContract: () => _viewContractPdf(entry.refOrderId!),
onDownloadContract: () => _downloadContractPdf(entry.refOrderId!),
showContractButtons: !isPrePlanting,
onViewContract: isPrePlanting ? null : () => _viewContractPdf(entry.refOrderId!),
onDownloadContract: isPrePlanting ? null : () => _downloadContractPdf(entry.refOrderId!),
),
);
}
@ -1231,13 +1235,15 @@ class _LedgerDetailPageState extends ConsumerState<LedgerDetailPage>
///
class _TransactionDetailSheet extends StatelessWidget {
final LedgerEntry entry;
final VoidCallback onViewContract;
final VoidCallback onDownloadContract;
final VoidCallback? onViewContract;
final VoidCallback? onDownloadContract;
final bool showContractButtons;
const _TransactionDetailSheet({
required this.entry,
required this.onViewContract,
required this.onDownloadContract,
this.onViewContract,
this.onDownloadContract,
this.showContractButtons = true,
});
@override
@ -1326,60 +1332,62 @@ class _TransactionDetailSheet extends StatelessWidget {
_buildDetailRow('交易后余额', '${_formatAmount(entry.balanceAfter!)} 绿积分'),
_buildDetailRow('交易时间', _formatDateTime(entry.createdAt)),
if (entry.memo != null && entry.memo!.isNotEmpty)
_buildDetailRow('备注', entry.memo!),
_buildDetailRow('备注',
entry.refOrderId?.startsWith('PPL') == true ? '预种' : entry.memo!),
],
),
),
// 线
Container(
height: 1,
color: const Color(0x1A8B5A2B),
),
//
Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () {
Navigator.of(context).pop();
onViewContract();
},
icon: const Icon(Icons.visibility_outlined, size: 18),
label: const Text('查看合同'),
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFF5D4037),
side: const BorderSide(color: Color(0xFFD4AF37)),
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
Navigator.of(context).pop();
onDownloadContract();
},
icon: const Icon(Icons.download_outlined, size: 18),
label: const Text('下载合同'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFD4AF37),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
//
if (showContractButtons) ...[
Container(
height: 1,
color: const Color(0x1A8B5A2B),
),
),
Padding(
padding: const EdgeInsets.all(20),
child: Row(
children: [
Expanded(
child: OutlinedButton.icon(
onPressed: () {
Navigator.of(context).pop();
onViewContract?.call();
},
icon: const Icon(Icons.visibility_outlined, size: 18),
label: const Text('查看合同'),
style: OutlinedButton.styleFrom(
foregroundColor: const Color(0xFF5D4037),
side: const BorderSide(color: Color(0xFFD4AF37)),
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
const SizedBox(width: 12),
Expanded(
child: ElevatedButton.icon(
onPressed: () {
Navigator.of(context).pop();
onDownloadContract?.call();
},
icon: const Icon(Icons.download_outlined, size: 18),
label: const Text('下载合同'),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFD4AF37),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
],
),
),
],
],
),
),