outing_log_detail_page.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. import 'package:flutter/material.dart';
  2. import 'package:go_router/go_router.dart';
  3. import 'package:flutter_riverpod/flutter_riverpod.dart';
  4. import '../../core/theme/app_colors.dart';
  5. import '../shell/nav_bar_config.dart';
  6. import '../../core/utils/date_utils.dart' as du;
  7. import '../../core/i18n/app_localizations.dart';
  8. import 'outing_log_list_controller.dart';
  9. class OutingLogDetailPage extends ConsumerWidget {
  10. final String id;
  11. const OutingLogDetailPage({super.key, required this.id});
  12. @override
  13. Widget build(BuildContext context, WidgetRef ref) {
  14. final log = mockOutingLogs.firstWhere(
  15. (e) => e.id == id,
  16. orElse: () => mockOutingLogs.first,
  17. );
  18. final l10n = AppLocalizations.of(context);
  19. ref
  20. .read(navBarConfigProvider.notifier)
  21. .update(
  22. NavBarConfig(
  23. title: l10n.get('outingLogDetail'),
  24. showBack: true,
  25. onBack: () => context.pop(),
  26. ),
  27. );
  28. return SingleChildScrollView(
  29. child: Column(
  30. children: [
  31. // 地图占位区
  32. Container(
  33. width: double.infinity,
  34. height: 160,
  35. padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
  36. child: Container(
  37. decoration: BoxDecoration(
  38. color: const Color(0xFFE8F4FD),
  39. borderRadius: BorderRadius.circular(8),
  40. ),
  41. child: const Center(
  42. child: Icon(
  43. Icons.map_outlined,
  44. size: 40,
  45. color: AppColors.primary,
  46. ),
  47. ),
  48. ),
  49. ),
  50. // 信息卡片区
  51. Padding(
  52. padding: const EdgeInsets.all(16),
  53. child: Column(
  54. children: [
  55. // 客户信息
  56. Container(
  57. width: double.infinity,
  58. padding: const EdgeInsets.all(16),
  59. decoration: BoxDecoration(
  60. color: AppColors.bgCard,
  61. borderRadius: BorderRadius.circular(8),
  62. ),
  63. child: Column(
  64. crossAxisAlignment: CrossAxisAlignment.start,
  65. children: [
  66. Text(
  67. log.customerName,
  68. style: const TextStyle(
  69. fontSize: 18,
  70. fontWeight: FontWeight.w700,
  71. color: AppColors.textPrimary,
  72. ),
  73. ),
  74. const SizedBox(height: 8),
  75. Text(
  76. '${log.visitNo} · ${du.DateUtils.formatDate(log.visitDate)}',
  77. style: const TextStyle(
  78. fontSize: 12,
  79. color: AppColors.textSecondary,
  80. ),
  81. ),
  82. const SizedBox(height: 4),
  83. Text(
  84. log.visitLocation,
  85. style: const TextStyle(
  86. fontSize: 13,
  87. color: AppColors.textSecondary,
  88. ),
  89. ),
  90. ],
  91. ),
  92. ),
  93. const SizedBox(height: 16),
  94. // 工作总结
  95. Container(
  96. width: double.infinity,
  97. padding: const EdgeInsets.all(16),
  98. decoration: BoxDecoration(
  99. color: AppColors.bgCard,
  100. borderRadius: BorderRadius.circular(8),
  101. ),
  102. child: Column(
  103. crossAxisAlignment: CrossAxisAlignment.start,
  104. children: [
  105. const Text(
  106. '工作总结',
  107. style: TextStyle(
  108. fontSize: 14,
  109. fontWeight: FontWeight.w600,
  110. color: AppColors.textPrimary,
  111. ),
  112. ),
  113. const SizedBox(height: 8),
  114. Text(
  115. log.visitSummary.isNotEmpty
  116. ? log.visitSummary
  117. : '暂无工作总结',
  118. style: const TextStyle(
  119. fontSize: 14,
  120. color: AppColors.textSecondary,
  121. height: 1.5,
  122. ),
  123. ),
  124. ],
  125. ),
  126. ),
  127. const SizedBox(height: 16),
  128. // 后续计划
  129. Container(
  130. width: double.infinity,
  131. padding: const EdgeInsets.all(16),
  132. decoration: BoxDecoration(
  133. color: AppColors.bgCard,
  134. borderRadius: BorderRadius.circular(8),
  135. ),
  136. child: Column(
  137. crossAxisAlignment: CrossAxisAlignment.start,
  138. children: [
  139. const Text(
  140. '后续计划',
  141. style: TextStyle(
  142. fontSize: 14,
  143. fontWeight: FontWeight.w600,
  144. color: AppColors.textPrimary,
  145. ),
  146. ),
  147. const SizedBox(height: 8),
  148. Text(
  149. log.nextVisitContent.isNotEmpty
  150. ? log.nextVisitContent
  151. : '${du.DateUtils.formatDate(log.nextVisitTime)} · 暂无计划',
  152. style: const TextStyle(
  153. fontSize: 14,
  154. color: AppColors.textSecondary,
  155. height: 1.5,
  156. ),
  157. ),
  158. ],
  159. ),
  160. ),
  161. const SizedBox(height: 16),
  162. // 现场照片
  163. Container(
  164. width: double.infinity,
  165. padding: const EdgeInsets.all(16),
  166. decoration: BoxDecoration(
  167. color: AppColors.bgCard,
  168. borderRadius: BorderRadius.circular(8),
  169. ),
  170. child: Column(
  171. crossAxisAlignment: CrossAxisAlignment.start,
  172. children: [
  173. const Text(
  174. '现场照片',
  175. style: TextStyle(
  176. fontSize: 14,
  177. fontWeight: FontWeight.w600,
  178. color: AppColors.textPrimary,
  179. ),
  180. ),
  181. const SizedBox(height: 12),
  182. Row(
  183. children: [
  184. _buildPhotoPlaceholder(),
  185. const SizedBox(width: 8),
  186. _buildPhotoPlaceholder(),
  187. const SizedBox(width: 8),
  188. _buildPhotoPlaceholder(),
  189. ],
  190. ),
  191. ],
  192. ),
  193. ),
  194. const SizedBox(height: 16),
  195. // 主管点评
  196. Container(
  197. width: double.infinity,
  198. padding: const EdgeInsets.all(12),
  199. decoration: BoxDecoration(
  200. color: AppColors.primaryLight,
  201. borderRadius: BorderRadius.circular(8),
  202. ),
  203. child: Column(
  204. crossAxisAlignment: CrossAxisAlignment.start,
  205. children: [
  206. Row(
  207. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  208. children: [
  209. const Text(
  210. '王经理 · 销售总监',
  211. style: TextStyle(
  212. fontSize: 14,
  213. fontWeight: FontWeight.w600,
  214. color: AppColors.textPrimary,
  215. ),
  216. ),
  217. const Row(
  218. children: [
  219. Icon(
  220. Icons.star,
  221. size: 16,
  222. color: AppColors.warning,
  223. ),
  224. Icon(
  225. Icons.star,
  226. size: 16,
  227. color: AppColors.warning,
  228. ),
  229. Icon(
  230. Icons.star,
  231. size: 16,
  232. color: AppColors.warning,
  233. ),
  234. Icon(
  235. Icons.star,
  236. size: 16,
  237. color: AppColors.warning,
  238. ),
  239. Icon(
  240. Icons.star_border,
  241. size: 16,
  242. color: AppColors.textPlaceholder,
  243. ),
  244. ],
  245. ),
  246. ],
  247. ),
  248. const SizedBox(height: 8),
  249. const Text(
  250. '本次拜访准备充分,方案汇报专业到位。建议后续跟进加快节奏,争取本月内签单。',
  251. style: TextStyle(
  252. fontSize: 14,
  253. color: AppColors.textSecondary,
  254. height: 1.5,
  255. ),
  256. ),
  257. const SizedBox(height: 8),
  258. Text(
  259. du.DateUtils.formatDateTime(log.updateTime),
  260. style: const TextStyle(
  261. fontSize: 12,
  262. color: AppColors.textPlaceholder,
  263. ),
  264. ),
  265. ],
  266. ),
  267. ),
  268. // 底部点评输入区
  269. const SizedBox(height: 16),
  270. Container(
  271. width: double.infinity,
  272. padding: const EdgeInsets.all(16),
  273. decoration: BoxDecoration(
  274. color: AppColors.bgCard,
  275. borderRadius: BorderRadius.circular(8),
  276. ),
  277. child: Row(
  278. children: [
  279. const Row(
  280. children: [
  281. Icon(
  282. Icons.star_border,
  283. size: 20,
  284. color: AppColors.warning,
  285. ),
  286. SizedBox(width: 2),
  287. Icon(
  288. Icons.star_border,
  289. size: 20,
  290. color: AppColors.warning,
  291. ),
  292. SizedBox(width: 2),
  293. Icon(
  294. Icons.star_border,
  295. size: 20,
  296. color: AppColors.warning,
  297. ),
  298. SizedBox(width: 2),
  299. Icon(
  300. Icons.star_border,
  301. size: 20,
  302. color: AppColors.warning,
  303. ),
  304. SizedBox(width: 2),
  305. Icon(
  306. Icons.star_border,
  307. size: 20,
  308. color: AppColors.textPlaceholder,
  309. ),
  310. ],
  311. ),
  312. const SizedBox(width: 12),
  313. Expanded(
  314. child: Container(
  315. padding: const EdgeInsets.symmetric(
  316. horizontal: 12,
  317. vertical: 8,
  318. ),
  319. decoration: BoxDecoration(
  320. color: AppColors.bgPage,
  321. borderRadius: BorderRadius.circular(18),
  322. ),
  323. child: const Text(
  324. '输入点评…',
  325. style: TextStyle(
  326. fontSize: 14,
  327. color: AppColors.textPlaceholder,
  328. ),
  329. ),
  330. ),
  331. ),
  332. const SizedBox(width: 8),
  333. const Text(
  334. '发送',
  335. style: TextStyle(
  336. fontSize: 16,
  337. fontWeight: FontWeight.w600,
  338. color: AppColors.primary,
  339. ),
  340. ),
  341. ],
  342. ),
  343. ),
  344. const SizedBox(height: 24),
  345. ],
  346. ),
  347. ),
  348. ],
  349. ),
  350. );
  351. }
  352. Widget _buildPhotoPlaceholder() {
  353. return Container(
  354. width: 105,
  355. height: 105,
  356. decoration: BoxDecoration(
  357. color: const Color(0xFFD0E8F8),
  358. borderRadius: BorderRadius.circular(4),
  359. ),
  360. );
  361. }
  362. }