taskDetail.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <template>
  2. <view class="app-container pb-24">
  3. <TopBar title="任务详情" showBack />
  4. <view v-if="task" class="px-4 py-4 gap-y-4">
  5. <!-- 施工流程进度 -->
  6. <ConstructionFlowBar :status="task.status" :timeline="task.timeline" />
  7. <!-- 操作按钮区 -->
  8. <view class="card p-4">
  9. <!-- 待确认 -->
  10. <view v-if="task.status === 'pending'" class="gap-y-3">
  11. <view class="bg-blue-50 rounded-xl p-4 mb-4 text-center">
  12. <text class="uni-icons uniui-compose text-4xl block mb-2 text-blue-500"></text>
  13. <text class="text-lg font-semibold text-gray-800 block">待确认任务</text>
  14. <text class="text-gray-500 text-sm mt-1 block">请查看任务详情,确认是否接受</text>
  15. </view>
  16. <button class="w-full py-3 btn-success rounded-xl font-medium flex items-center justify-center gap-2" @click="onConfirm">
  17. <text>确认任务</text>
  18. </button>
  19. <button class="w-full py-3 bg-orange-50 border border-orange-200 text-orange-600 rounded-xl font-medium flex items-center justify-center gap-2" @click="onFeedback">
  20. <text>反馈问题</text>
  21. </button>
  22. <button class="w-full py-3 bg-red-50 border border-red-200 text-red-500 rounded-xl font-medium flex items-center justify-center gap-2" @click="onReject">
  23. <text>拒绝任务</text>
  24. </button>
  25. </view>
  26. <!-- 待出车 -->
  27. <view v-else-if="task.status === 'scheduled'" class="gap-y-3">
  28. <view class="bg-orange-50 rounded-xl p-4 mb-4 text-center">
  29. <text class="uni-icons uniui-cart-filled text-4xl block mb-2 text-orange-500"></text>
  30. <text class="text-lg font-semibold text-gray-800 block">准备出发</text>
  31. <text class="text-gray-500 text-sm mt-1 block">请确认设备准备完毕后出发</text>
  32. </view>
  33. <view class="bg-white rounded-lg p-3 border border-gray-100 mb-3">
  34. <view class="flex justify-between text-sm">
  35. <text class="text-gray-400">目的地</text>
  36. <text class="text-gray-800">{{ task.projectName }}</text>
  37. </view>
  38. <view v-if="task.vehicle" class="flex justify-between text-sm mt-2">
  39. <text class="text-gray-400">车辆</text>
  40. <text class="text-gray-800">{{ task.vehicle }}</text>
  41. </view>
  42. </view>
  43. <button class="w-full py-3 btn-primary rounded-xl font-semibold flex items-center justify-center gap-2" @click="onDepart">
  44. <text>出发</text>
  45. </button>
  46. </view>
  47. <!-- 已出车 -->
  48. <view v-else-if="task.status === 'departed'" class="gap-y-3">
  49. <view class="bg-purple-50 rounded-xl p-4 mb-4 text-center">
  50. <text class="uni-icons uniui-location-filled text-4xl block mb-2 text-purple-500"></text>
  51. <text class="text-lg font-semibold text-gray-800 block">前往现场</text>
  52. <text class="text-gray-500 text-sm mt-1 block">正在前往施工地点</text>
  53. </view>
  54. <button class="w-full py-3 btn-success rounded-xl font-semibold flex items-center justify-center gap-2" @click="onArrive">
  55. <text>到达现场</text>
  56. </button>
  57. </view>
  58. <!-- 已到达(勘验) -->
  59. <view v-else-if="task.status === 'arrived'" class="gap-y-3">
  60. <view class="bg-green-50 rounded-xl p-4 mb-4 text-center">
  61. <text class="uni-icons uniui-location text-4xl block mb-2 text-green-500"></text>
  62. <text class="text-lg font-semibold text-gray-800 block">已到达现场</text>
  63. <text class="text-gray-500 text-sm mt-1 block">请完成现场勘验后开始施工作业</text>
  64. </view>
  65. <button class="w-full py-3 btn-primary rounded-xl font-semibold flex items-center justify-center gap-2" @click="onStartWork">
  66. <text>开始施工</text>
  67. </button>
  68. </view>
  69. <!-- 施工中 -->
  70. <view v-else-if="task.status === 'constructing'" class="gap-y-3">
  71. <view class="bg-blue-50 rounded-xl p-4 mb-4 text-center">
  72. <text class="uni-icons uniui-gear-filled text-4xl block mb-2 text-blue-500"></text>
  73. <text class="text-lg font-semibold text-gray-800 block">施工中</text>
  74. <text class="text-gray-500 text-sm mt-1 block">请按规范完成施工作业</text>
  75. </view>
  76. <button class="w-full py-3 btn-success rounded-xl font-semibold flex items-center justify-center gap-2" @click="onComplete">
  77. <text>完成施工</text>
  78. </button>
  79. </view>
  80. <!-- 待验收 -->
  81. <view v-else-if="task.status === 'inspecting'" class="gap-y-3">
  82. <view class="bg-warning-light rounded-xl p-4 mb-4 text-center">
  83. <text class="uni-icons uniui-person-filled text-4xl block mb-2 text-warning"></text>
  84. <text class="text-lg font-semibold text-gray-800 block">等待验收</text>
  85. <text class="text-gray-500 text-sm mt-1 block">请等待客户或调度验收</text>
  86. </view>
  87. <button class="w-full py-3 btn-success rounded-xl font-semibold flex items-center justify-center gap-2" @click="onAccept">
  88. <text>完成验收</text>
  89. </button>
  90. </view>
  91. <!-- 已完成 -->
  92. <view v-else-if="task.status === 'completed'" class="gap-y-3">
  93. <view class="bg-green-50 rounded-xl p-4 text-center">
  94. <text class="uni-icons uniui-checkmarkempty text-4xl block mb-2 text-green-500"></text>
  95. <text class="text-lg font-semibold text-green-700 block">任务已完成</text>
  96. <text class="text-gray-500 text-sm mt-1 block">该任务已验收通过</text>
  97. </view>
  98. </view>
  99. </view>
  100. <!-- 任务信息 -->
  101. <view class="card p-4">
  102. <view class="detail-section-title mb-4">
  103. <text class="uni-icons uniui-flag-filled text-primary mr-2"></text>
  104. <text>{{ task.projectName }}</text>
  105. <StatusTag :status="task.status" class="ml-auto" />
  106. </view>
  107. <view class="space-y-3">
  108. <view class="detail-row">
  109. <text class="detail-label">任务类型</text>
  110. <text class="detail-value">{{ getTypeText(task.type) }}</text>
  111. </view>
  112. <view class="detail-row">
  113. <text class="detail-label">紧急程度</text>
  114. <text :class="task.urgent === 'high' ? 'text-danger' : 'text-gray-800'">{{ getUrgentText(task.urgent) }}</text>
  115. </view>
  116. <view v-if="task.serviceType" class="detail-row">
  117. <text class="detail-label">施工类型</text>
  118. <text class="detail-value">{{ task.serviceType }}</text>
  119. </view>
  120. <view v-if="task.facilityType" class="detail-row">
  121. <text class="detail-label">设施类型</text>
  122. <text class="detail-value">{{ task.facilityType }}</text>
  123. </view>
  124. <view v-if="task.faultLocation" class="detail-row">
  125. <text class="detail-label">故障位置</text>
  126. <text class="detail-value">{{ task.faultLocation }}</text>
  127. </view>
  128. <view v-if="task.faultDesc" class="detail-row">
  129. <text class="detail-label">故障描述</text>
  130. <text class="detail-value">{{ task.faultDesc }}</text>
  131. </view>
  132. <view v-if="task.assignee" class="detail-row">
  133. <text class="detail-label">负责人</text>
  134. <text class="detail-value">{{ task.assignee }}</text>
  135. </view>
  136. <view v-if="task.vehicle" class="detail-row">
  137. <text class="detail-label">车辆</text>
  138. <text class="detail-value">{{ task.vehicle }}</text>
  139. </view>
  140. <view v-if="task.team" class="detail-row">
  141. <text class="detail-label">班组</text>
  142. <text class="detail-value">{{ task.team }}</text>
  143. </view>
  144. <view v-if="task.scheduledDate" class="detail-row">
  145. <text class="detail-label">计划日期</text>
  146. <text class="detail-value">{{ task.scheduledDate }}</text>
  147. </view>
  148. <view v-if="task.address" class="detail-row">
  149. <text class="detail-label">项目地址</text>
  150. <text class="detail-value text-right flex-1">{{ task.address }}</text>
  151. </view>
  152. <view class="detail-row">
  153. <text class="detail-label">创建时间</text>
  154. <text class="detail-value">{{ formatDate(task.createTime, 'YYYY-MM-DD HH:mm') }}</text>
  155. </view>
  156. </view>
  157. </view>
  158. <!-- 时间轴 -->
  159. <view class="card p-4">
  160. <view class="detail-section-title mb-4">
  161. <text class="uni-icons uniui-time-filled text-primary mr-2"></text>
  162. <text>操作记录</text>
  163. </view>
  164. <Timeline :records="sortedTimeline" />
  165. </view>
  166. </view>
  167. <EmptyState v-else message="任务不存在" />
  168. </view>
  169. </template>
  170. <script setup lang="ts">
  171. import { computed } from 'vue'
  172. import { onLoad } from '@dcloudio/uni-app'
  173. import TopBar from '../../components/common/TopBar.vue'
  174. import StatusTag from '../../components/common/StatusTag.vue'
  175. import EmptyState from '../../components/common/EmptyState.vue'
  176. import Timeline from '../../components/common/Timeline.vue'
  177. import ConstructionFlowBar from '../../components/common/ConstructionFlowBar.vue'
  178. import { useTaskStore } from '../../stores/task'
  179. import { formatDate, getTypeText, getUrgentText, showToast, showModal, goBack } from '../../utils'
  180. import { updateTask } from '../../api/task'
  181. const taskStore = useTaskStore()
  182. const task = computed(() => taskStore.currentTask)
  183. const sortedTimeline = computed(() => {
  184. if (!task.value?.timeline) return []
  185. return [...task.value.timeline].sort((a, b) => new Date(b.time).getTime() - new Date(a.time).getTime())
  186. })
  187. async function advanceStatus(step: number, successMsg: string) {
  188. if (!task.value) return
  189. try {
  190. await taskStore.advanceStep(Number(task.value.id), step)
  191. showToast(successMsg)
  192. await taskStore.fetchTaskDetail(Number(task.value.id))
  193. } catch (error) {
  194. showToast('操作失败')
  195. }
  196. }
  197. function onConfirm() {
  198. advanceStatus(1, '任务已确认')
  199. }
  200. function onDepart() {
  201. advanceStatus(3, '已出车')
  202. }
  203. function onArrive() {
  204. advanceStatus(4, '已到达现场')
  205. }
  206. function onStartWork() {
  207. advanceStatus(5, '开始施工')
  208. }
  209. function onComplete() {
  210. advanceStatus(6, '施工完成')
  211. }
  212. function onAccept() {
  213. advanceStatus(7, '验收完成')
  214. }
  215. async function onReject() {
  216. const res = await showModal({
  217. title: '拒绝任务',
  218. content: '确定要拒绝此任务吗?',
  219. showCancel: true,
  220. confirmText: '确认拒绝',
  221. })
  222. if (res.confirm && task.value) {
  223. try {
  224. await updateTask(Number(task.value.id), { status: 'rejected' } as any)
  225. showToast('已拒绝任务')
  226. goBack()
  227. } catch {
  228. showToast('操作失败')
  229. }
  230. }
  231. }
  232. function onFeedback() {
  233. showModal({
  234. title: '反馈问题',
  235. content: '请描述遇到的问题,将提交给调度中心处理',
  236. showCancel: true,
  237. confirmText: '提交',
  238. success: (res) => {
  239. if (res.confirm) {
  240. showToast('反馈已提交')
  241. }
  242. }
  243. })
  244. }
  245. onLoad((options) => {
  246. if (options?.id) {
  247. taskStore.setCurrentTask(options.id)
  248. }
  249. })
  250. </script>