taskDetail.vue 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <template>
  2. <view class="sub-page">
  3. <TopBar title="任务详情" show-back />
  4. <view class="px-4 pt-3">
  5. <!-- 任务状态卡片 -->
  6. <view class="glass-card p-4">
  7. <view class="flex justify-between items-start mb-3">
  8. <view class="flex-1 min-w-0 mr-2">
  9. <text class="notice-title">{{ task.projectName }}</text>
  10. <view class="mt-2 flex items-center">
  11. <StatusTag :status="task.status" />
  12. <text v-if="task.isEmergency" class="emergency-tag ml-2">应急</text>
  13. </view>
  14. </view>
  15. <text class="meta-text flex-shrink-0">{{ task.taskNo }}</text>
  16. </view>
  17. <view class="meta-line mb-2">
  18. <text class="uni-icons uniui-location-filled meta-icon mr-2"></text>
  19. <text class="meta-value flex-1">{{ task.address }}</text>
  20. </view>
  21. <view class="meta-line">
  22. <text class="uni-icons uniui-clock-filled meta-icon mr-2"></text>
  23. <text class="meta-value">{{ task.scheduleDate }} {{ task.scheduleTime }}</text>
  24. </view>
  25. </view>
  26. <!-- 基本信息 -->
  27. <view class="glass-card">
  28. <view class="section-head">
  29. <view class="section-bar mr-2"></view>
  30. <text class="section-title flex-1">基本信息</text>
  31. </view>
  32. <view class="info-row">
  33. <text class="info-label">服务类型</text>
  34. <text class="info-value">{{ task.serviceType }}</text>
  35. </view>
  36. <view class="info-row">
  37. <text class="info-label">客户名称</text>
  38. <text class="info-value">{{ task.customerName }}</text>
  39. </view>
  40. <view class="info-row">
  41. <text class="info-label">联系电话</text>
  42. <text class="info-value">{{ task.customerPhone }}</text>
  43. </view>
  44. <view class="info-row">
  45. <text class="info-label">项目地址</text>
  46. <text class="info-value info-value-left">{{ task.address }}</text>
  47. </view>
  48. <view class="info-row">
  49. <text class="info-label">预约时间</text>
  50. <text class="info-value">{{ task.scheduleDate }} {{ task.scheduleTime }}</text>
  51. </view>
  52. <view class="info-row">
  53. <text class="info-label">紧急程度</text>
  54. <text class="info-value" :class="task.isEmergency ? 'urgent-text' : 'normal-text'">
  55. {{ task.isEmergency ? '紧急' : '普通' }}
  56. </text>
  57. </view>
  58. </view>
  59. <!-- 服务点信息 -->
  60. <view v-if="task.servicePoints && task.servicePoints.length > 0" class="glass-card">
  61. <view class="section-head">
  62. <view class="section-bar mr-2"></view>
  63. <text class="section-title flex-1">服务点信息</text>
  64. </view>
  65. <view
  66. v-for="(point, index) in task.servicePoints"
  67. :key="index"
  68. class="point-row"
  69. >
  70. <view class="flex justify-between items-center mb-2">
  71. <text class="point-name">{{ point.name }}</text>
  72. <text class="type-tag">{{ point.serviceType }}</text>
  73. </view>
  74. <view class="flex items-center">
  75. <text class="uni-icons uniui-location-filled meta-icon mr-1"></text>
  76. <text class="meta-text">{{ point.address }}</text>
  77. </view>
  78. </view>
  79. </view>
  80. <!-- 处理记录 -->
  81. <view class="glass-card">
  82. <view class="section-head">
  83. <view class="section-bar mr-2"></view>
  84. <text class="section-title flex-1">处理记录</text>
  85. </view>
  86. <view class="pb-3 px-2">
  87. <Timeline :records="timelineRecords" />
  88. </view>
  89. </view>
  90. </view>
  91. </view>
  92. </template>
  93. <script setup lang="ts">
  94. import { ref, computed } from 'vue'
  95. import { useTaskStore } from '@/stores/task'
  96. import TopBar from '@/components/common/TopBar.vue'
  97. import StatusTag from '@/components/common/StatusTag.vue'
  98. import Timeline from '@/components/common/Timeline.vue'
  99. import type { TimelineRecord } from '@/components/common/Timeline.vue'
  100. const taskStore = useTaskStore()
  101. const task = computed(() => taskStore.currentTask || {})
  102. const timelineRecords = ref<TimelineRecord[]>([
  103. {
  104. action: '任务已创建,等待调度',
  105. time: '2026-04-20 09:00',
  106. operator: '销售-李明',
  107. role: 'sales',
  108. isCurrent: false
  109. },
  110. {
  111. action: '任务已排班,分配工程一班',
  112. time: '2026-04-20 10:30',
  113. operator: '调度-王调度',
  114. role: 'dispatch',
  115. isCurrent: false
  116. },
  117. {
  118. action: '工程人员已出发',
  119. time: '2026-04-20 11:00',
  120. operator: '施工-张师傅',
  121. role: 'worker',
  122. isCurrent: true
  123. }
  124. ])
  125. </script>
  126. <style scoped>
  127. .sub-page {
  128. max-width: 430px;
  129. margin: 0 auto;
  130. min-height: 100vh;
  131. background-color: #f6fbf9;
  132. padding-bottom: 24px;
  133. }
  134. .glass-card {
  135. background-color: rgba(255, 255, 255, 0.85);
  136. backdrop-filter: blur(14px);
  137. -webkit-backdrop-filter: blur(14px);
  138. border: 1px solid rgba(164, 216, 152, 0.35);
  139. border-radius: 24px;
  140. box-shadow: 0 18px 50px -12px rgba(54, 143, 111, 0.28);
  141. overflow: hidden;
  142. margin-bottom: 12px;
  143. }
  144. .notice-title {
  145. display: block;
  146. font-size: 17px;
  147. font-weight: 700;
  148. color: #1f2937;
  149. line-height: 1.4;
  150. }
  151. .emergency-tag {
  152. padding: 2px 10px;
  153. border-radius: 999px;
  154. font-size: 11px;
  155. font-weight: 600;
  156. color: #ef4444;
  157. background-color: rgba(239, 68, 68, 0.1);
  158. }
  159. .meta-line {
  160. display: flex;
  161. align-items: center;
  162. }
  163. .meta-icon {
  164. font-size: 14px;
  165. color: #9ca3af;
  166. }
  167. .meta-text {
  168. font-size: 12px;
  169. color: #9ca3af;
  170. }
  171. .meta-value {
  172. font-size: 13px;
  173. color: #4b5563;
  174. }
  175. .section-head {
  176. display: flex;
  177. align-items: center;
  178. padding: 16px 16px 12px;
  179. }
  180. .section-bar {
  181. width: 4px;
  182. height: 16px;
  183. border-radius: 2px;
  184. background: linear-gradient(180deg, #368f6f 0%, #5ab8d0 100%);
  185. }
  186. .section-title {
  187. font-size: 16px;
  188. font-weight: 700;
  189. color: #1f2937;
  190. }
  191. .info-row {
  192. display: flex;
  193. align-items: center;
  194. justify-content: space-between;
  195. padding: 12px 16px;
  196. border-top: 1px solid rgba(164, 216, 152, 0.18);
  197. }
  198. .info-label {
  199. font-size: 13px;
  200. color: #9ca3af;
  201. flex-shrink: 0;
  202. margin-right: 12px;
  203. }
  204. .info-value {
  205. font-size: 13px;
  206. color: #1f2937;
  207. text-align: right;
  208. }
  209. .info-value-left {
  210. flex: 1;
  211. }
  212. .urgent-text {
  213. color: #ef4444;
  214. font-weight: 600;
  215. }
  216. .normal-text {
  217. color: #368f6f;
  218. }
  219. .point-row {
  220. padding: 13px 16px;
  221. border-top: 1px solid rgba(164, 216, 152, 0.18);
  222. }
  223. .point-name {
  224. font-size: 14px;
  225. font-weight: 600;
  226. color: #1f2937;
  227. }
  228. .type-tag {
  229. padding: 2px 10px;
  230. border-radius: 999px;
  231. font-size: 11px;
  232. font-weight: 600;
  233. color: #368f6f;
  234. background-color: rgba(164, 216, 152, 0.24);
  235. }
  236. </style>