feat(pre-planting): Mobile App 预种合并详情页完整实现
[2026-02-17] 预种合并详情页面 (pre_planting_merge_detail_page.dart) 完整功能: - 合并信息卡片:合并号、合并时间、份数→树数、总价值、省市 - 合同签署状态卡片:待签署/已签署/已过期,含签署时间 - 挖矿状态卡片:已开启/未开启,含开启时间 - 来源订单列表:编号圆标 + 订单号 + 金额,逐条展示 5 笔订单 - 签约确认弹窗:列出签约后解锁的权限(交易/提现/授权/挖矿) - 底部签约按钮:仅待签署状态显示,含加载状态 - 签约成功后自动刷新页面状态 UI 风格与全局一致:渐变背景、金色主色调、卡片容器 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
99f5070552
commit
d248f92443
|
|
@ -1,18 +1,794 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../../../core/di/injection_container.dart';
|
||||
import '../../../../core/services/pre_planting_service.dart';
|
||||
|
||||
/// [2026-02-17] 预种合并详情页面(占位 - 待完整实现)
|
||||
// ============================================
|
||||
// [2026-02-17] 预种合并详情页面
|
||||
// ============================================
|
||||
//
|
||||
// 显示一次合并(5 份 → 1 棵树)的完整详情,包括:
|
||||
// - 合并信息:合并号、合并时间、树数
|
||||
// - 合同签署状态:待签署 / 已签署(含签署时间)
|
||||
// - 挖矿状态:未开启 / 已开启(含开启时间)
|
||||
// - 来源订单列表:组成本次合并的 5 笔预种订单号
|
||||
// - 签约按钮:待签署状态下显示,点击触发合同签署
|
||||
//
|
||||
// === 页面入口 ===
|
||||
// 从 PrePlantingPositionPage 的合并记录列表点击进入
|
||||
// 路由参数:mergeNo(合并记录号)
|
||||
//
|
||||
// === 合同签署流程 ===
|
||||
// 点击签约 → 调用 signMergeContract API → 刷新页面
|
||||
// 签约后:解锁交易/提现/授权限制 + 开启挖矿
|
||||
|
||||
/// 预种合并详情页面
|
||||
///
|
||||
/// 功能:显示合并记录详情、合同签署状态、挖矿状态、来源订单列表
|
||||
class PrePlantingMergeDetailPage extends StatelessWidget {
|
||||
/// 显示合并记录详情、合同签署状态、挖矿状态,
|
||||
/// 并提供合同签署入口。
|
||||
class PrePlantingMergeDetailPage extends ConsumerStatefulWidget {
|
||||
final String mergeNo;
|
||||
|
||||
const PrePlantingMergeDetailPage({super.key, required this.mergeNo});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('合并详情')),
|
||||
body: Center(child: Text('合并详情页 - $mergeNo - 开发中')),
|
||||
ConsumerState<PrePlantingMergeDetailPage> createState() =>
|
||||
_PrePlantingMergeDetailPageState();
|
||||
}
|
||||
|
||||
class _PrePlantingMergeDetailPageState
|
||||
extends ConsumerState<PrePlantingMergeDetailPage> {
|
||||
/// 合并详情数据
|
||||
PrePlantingMerge? _merge;
|
||||
|
||||
/// 是否正在加载
|
||||
bool _isLoading = true;
|
||||
|
||||
/// 是否正在签约
|
||||
bool _isSigning = false;
|
||||
|
||||
/// 加载错误信息
|
||||
String? _errorMessage;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadDetail();
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 数据加载
|
||||
// ============================================
|
||||
|
||||
/// 加载合并详情
|
||||
Future<void> _loadDetail() async {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_errorMessage = null;
|
||||
});
|
||||
|
||||
try {
|
||||
final service = ref.read(prePlantingServiceProvider);
|
||||
final merge = await service.getMergeDetail(widget.mergeNo);
|
||||
|
||||
setState(() {
|
||||
_merge = merge;
|
||||
_isLoading = false;
|
||||
});
|
||||
} catch (e) {
|
||||
debugPrint('[PrePlantingMergeDetail] 加载详情失败: $e');
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
_errorMessage = '加载详情失败,请重试';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// 签署合同
|
||||
Future<void> _signContract() async {
|
||||
if (_isSigning || _merge == null) return;
|
||||
|
||||
// 确认弹窗
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (ctx) => AlertDialog(
|
||||
backgroundColor: const Color(0xFFFFF7E6),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
title: const Text(
|
||||
'确认签署合同',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
content: const Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'签署合同后将:',
|
||||
style: TextStyle(
|
||||
fontSize: 15,
|
||||
color: Color(0xFF5D4037),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
_BulletPoint('解锁 eUSDT 交易权限'),
|
||||
_BulletPoint('解锁提现权限'),
|
||||
_BulletPoint('解锁授权申请权限'),
|
||||
_BulletPoint('开启挖矿收益分配'),
|
||||
SizedBox(height: 12),
|
||||
Text(
|
||||
'此操作不可撤销,请确认。',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF745D43),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(false),
|
||||
child: const Text(
|
||||
'取消',
|
||||
style: TextStyle(color: Color(0xFF745D43), fontSize: 16),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(true),
|
||||
child: const Text(
|
||||
'确认签署',
|
||||
style: TextStyle(
|
||||
color: Color(0xFFD4AF37),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirmed != true || !mounted) return;
|
||||
|
||||
setState(() => _isSigning = true);
|
||||
|
||||
try {
|
||||
final service = ref.read(prePlantingServiceProvider);
|
||||
final updatedMerge = await service.signMergeContract(widget.mergeNo);
|
||||
|
||||
debugPrint('[PrePlantingMergeDetail] 签约成功: ${updatedMerge.mergeNo}');
|
||||
|
||||
setState(() {
|
||||
_merge = updatedMerge;
|
||||
_isSigning = false;
|
||||
});
|
||||
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('合同签署成功!交易、提现和授权权限已解锁'),
|
||||
backgroundColor: Color(0xFF4CAF50),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('[PrePlantingMergeDetail] 签约失败: $e');
|
||||
if (mounted) {
|
||||
setState(() => _isSigning = false);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('签约失败: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 返回上一页
|
||||
void _goBack() {
|
||||
context.pop();
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// UI 构建
|
||||
// ============================================
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [Color(0xFFFFF7E6), Color(0xFFEAE0C8)],
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Column(
|
||||
children: [
|
||||
_buildHeader(),
|
||||
Expanded(
|
||||
child: _isLoading
|
||||
? _buildLoadingState()
|
||||
: _errorMessage != null
|
||||
? _buildErrorState()
|
||||
: _buildContent(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 顶部导航栏
|
||||
Widget _buildHeader() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFFFF7E6).withValues(alpha: 0.8),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: _goBack,
|
||||
child: Container(
|
||||
width: 32,
|
||||
height: 32,
|
||||
alignment: Alignment.center,
|
||||
child: const Icon(
|
||||
Icons.arrow_back_ios,
|
||||
color: Color(0xFFD4AF37),
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
GestureDetector(
|
||||
onTap: _goBack,
|
||||
child: const Text(
|
||||
'返回',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
height: 1.5,
|
||||
color: Color(0xFFD4AF37),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 42),
|
||||
const Expanded(
|
||||
child: Text(
|
||||
'合并详情',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.25,
|
||||
letterSpacing: -0.27,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 加载中
|
||||
Widget _buildLoadingState() {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Color(0xFFD4AF37),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 错误状态
|
||||
Widget _buildErrorState() {
|
||||
return Center(
|
||||
child: GestureDetector(
|
||||
onTap: _loadDetail,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.error_outline, color: Color(0xFFE65100), size: 48),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_errorMessage!,
|
||||
style: const TextStyle(fontSize: 16, color: Color(0xFFE65100)),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'点击重试',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFFD4AF37),
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 主内容
|
||||
Widget _buildContent() {
|
||||
final merge = _merge!;
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 合并信息卡片
|
||||
_buildMergeInfoCard(merge),
|
||||
const SizedBox(height: 16),
|
||||
// 合同状态卡片
|
||||
_buildContractStatusCard(merge),
|
||||
const SizedBox(height: 16),
|
||||
// 挖矿状态卡片
|
||||
_buildMiningStatusCard(merge),
|
||||
const SizedBox(height: 16),
|
||||
// 来源订单列表
|
||||
_buildSourceOrdersCard(merge),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// 底部签约按钮(仅待签署时显示)
|
||||
if (merge.contractStatus == PrePlantingContractStatus.pending)
|
||||
_buildSignButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 合并信息卡片
|
||||
Widget _buildMergeInfoCard(PrePlantingMerge merge) {
|
||||
return _buildCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 顶部图标 + 标题
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFD4AF37).withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(24),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.park,
|
||||
color: Color(0xFFD4AF37),
|
||||
size: 28,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'预种合并 · 1 棵树',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
merge.mergeNo,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: const Color(0xFF745D43).withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const Divider(height: 1, color: Color(0x338B5A2B)),
|
||||
const SizedBox(height: 16),
|
||||
// 信息行
|
||||
_buildDetailRow('合并时间', _formatDateTime(merge.mergedAt)),
|
||||
const SizedBox(height: 8),
|
||||
_buildDetailRow('合并份数', '${merge.sourceOrderNos.length} 份 → 1 棵树'),
|
||||
const SizedBox(height: 8),
|
||||
_buildDetailRow(
|
||||
'总价值',
|
||||
'${(merge.sourceOrderNos.length * 3171).toString()} USDT',
|
||||
),
|
||||
if (merge.selectedProvince != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
_buildDetailRow(
|
||||
'省市',
|
||||
'${merge.selectedProvince} · ${merge.selectedCity ?? "-"}',
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 合同状态卡片
|
||||
Widget _buildContractStatusCard(PrePlantingMerge merge) {
|
||||
final isPending =
|
||||
merge.contractStatus == PrePlantingContractStatus.pending;
|
||||
final isSigned =
|
||||
merge.contractStatus == PrePlantingContractStatus.signed;
|
||||
|
||||
return _buildCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
isSigned ? Icons.check_circle : Icons.edit_document,
|
||||
color: isSigned
|
||||
? const Color(0xFF2E7D32)
|
||||
: const Color(0xFFE65100),
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'合同签署',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
_buildStatusBadge(
|
||||
isPending ? '待签署' : (isSigned ? '已签署' : '已过期'),
|
||||
isSigned
|
||||
? const Color(0xFF2E7D32)
|
||||
: (isPending
|
||||
? const Color(0xFFE65100)
|
||||
: const Color(0xFF757575)),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (merge.contractSignedAt != null) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'签署时间:${_formatDateTime(merge.contractSignedAt!)}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: const Color(0xFF745D43).withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (isPending) ...[
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
'签署合同后将解锁交易、提现和授权权限',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFFE65100),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 挖矿状态卡片
|
||||
Widget _buildMiningStatusCard(PrePlantingMerge merge) {
|
||||
final isMining = merge.isMiningEnabled;
|
||||
|
||||
return _buildCard(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
isMining ? Icons.flash_on : Icons.flash_off_outlined,
|
||||
color:
|
||||
isMining ? const Color(0xFFD4AF37) : const Color(0xFF757575),
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
'算力挖矿',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
if (merge.miningEnabledAt != null)
|
||||
Text(
|
||||
'开启时间:${_formatDateTime(merge.miningEnabledAt!)}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: const Color(0xFF745D43).withValues(alpha: 0.7),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildStatusBadge(
|
||||
isMining ? '已开启' : '未开启',
|
||||
isMining ? const Color(0xFF2E7D32) : const Color(0xFF757575),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 来源订单列表卡片
|
||||
Widget _buildSourceOrdersCard(PrePlantingMerge merge) {
|
||||
return _buildCard(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'来源订单(${merge.sourceOrderNos.length} 笔)',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
...merge.sourceOrderNos.asMap().entries.map((entry) {
|
||||
final index = entry.key;
|
||||
final orderNo = entry.value;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: index < merge.sourceOrderNos.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(
|
||||
orderNo,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'Inter',
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
'3,171 USDT',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Color(0xFF745D43),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 底部签约按钮
|
||||
Widget _buildSignButton() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 24),
|
||||
child: GestureDetector(
|
||||
onTap: _isSigning ? null : _signContract,
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: _isSigning
|
||||
? const Color(0xFFD4AF37).withValues(alpha: 0.5)
|
||||
: const Color(0xFFD4AF37),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: _isSigning
|
||||
? null
|
||||
: const [
|
||||
BoxShadow(
|
||||
color: Color(0x1A000000),
|
||||
blurRadius: 15,
|
||||
offset: Offset(0, 10),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0x1A000000),
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Center(
|
||||
child: _isSigning
|
||||
? const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2,
|
||||
color: Colors.white,
|
||||
),
|
||||
)
|
||||
: const Text(
|
||||
'签署合同',
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'Inter',
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.56,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 通用组件
|
||||
// ============================================
|
||||
|
||||
/// 通用卡片容器
|
||||
Widget _buildCard({required Widget child}) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0x99FFFFFF),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: const [
|
||||
BoxShadow(
|
||||
color: Color(0x1A000000),
|
||||
blurRadius: 6,
|
||||
offset: Offset(0, 4),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Color(0x1A000000),
|
||||
blurRadius: 4,
|
||||
offset: Offset(0, 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
/// 详情行(标签 + 值)
|
||||
Widget _buildDetailRow(String label, String value) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF745D43),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
value,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF5D4037),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
/// 状态徽章
|
||||
Widget _buildStatusBadge(String text, Color color) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.12),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
text,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// 格式化日期时间
|
||||
String _formatDateTime(DateTime dt) {
|
||||
return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-'
|
||||
'${dt.day.toString().padLeft(2, '0')} '
|
||||
'${dt.hour.toString().padLeft(2, '0')}:'
|
||||
'${dt.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
}
|
||||
|
||||
/// 项目列表的圆点文字组件(用于确认弹窗)
|
||||
class _BulletPoint extends StatelessWidget {
|
||||
final String text;
|
||||
const _BulletPoint(this.text);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 4),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 6),
|
||||
child: Icon(
|
||||
Icons.check_circle_outline,
|
||||
color: Color(0xFFD4AF37),
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
color: Color(0xFF5D4037),
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue