| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- <template>
- <view class="app-container pb-20">
- <TopBar :title="'任务详情'" show-back />
- <view v-if="taskStore.loading" class="py-20 text-center">
- <text class="text-gray-400">加载中...</text>
- </view>
- <view v-else-if="!task" class="px-4 py-6">
- <EmptyState message="任务不存在" />
- </view>
- <view v-else class="px-4 py-4 gap-y-4">
- <!-- 项目信息 -->
- <uni-card title="项目信息" :is-shadow="true">
- <view class="flex justify-between items-start mb-3">
- <view class="flex-1 min-w-0 mr-3">
- <text class="text-base font-semibold text-gray-800 block">{{ task.projectName }}</text>
- </view>
- <StatusTag :status="task.status" />
- </view>
- <uni-list :border="false">
- <uni-list-item title="地址" :note="task.address || '暂无地址'" icon="uniui-location-filled" />
- <uni-list-item title="客户" :note="task.customerName || '暂无客户'" icon="uniui-person-filled" />
- <uni-list-item
- title="电话"
- :note="task.customerPhone || '暂无电话'"
- icon="uniui-phone-filled"
- @click="callPhone(task.customerPhone)"
- />
- <uni-list-item v-if="task.coopType" title="合作类型" :note="task.coopType" icon="uniui-folder-add-filled" />
- <uni-list-item v-if="task.serviceMode" title="服务方式" :note="task.serviceMode" icon="uniui-calendar-filled" />
- </uni-list>
- </uni-card>
- <!-- 施工信息 -->
- <uni-card title="施工信息" :is-shadow="true">
- <uni-list :border="false">
- <uni-list-item title="施工类型" :note="task.serviceType || '未填写'" icon="uniui-tools" />
- <uni-list-item title="设施类型" :note="task.facilityType || '未填写'" icon="uniui-flag-filled" />
- <uni-list-item title="服务时间" :note="serviceTimeText" icon="uniui-clock-filled" />
- <uni-list-item v-if="task.team" title="施工班组" :note="task.team" icon="uniui-personadd-filled" />
- <uni-list-item v-if="task.vehicleNo" title="车辆" :note="task.vehicleNo" icon="uniui-cart-filled" />
- <uni-list-item v-if="task.acceptType" title="验收方式" :note="task.acceptType" icon="uniui-checkmarkempty" />
- </uni-list>
- </uni-card>
- <!-- 故障信息 -->
- <uni-card v-if="task.faultLocation || task.faultDesc" title="故障信息" :is-shadow="true">
- <uni-list :border="false">
- <uni-list-item v-if="task.faultLocation" title="故障位置" :note="task.faultLocation" icon="uniui-location-filled" />
- <uni-list-item v-if="task.faultDesc" title="故障描述" :note="task.faultDesc" icon="uniui-compose" />
- </uni-list>
- </uni-card>
- <!-- 任务进度 -->
- <uni-card title="任务进度" :is-shadow="true">
- <view class="flex items-center justify-between px-2">
- <view
- v-for="(step, index) in progressSteps"
- :key="step.key"
- class="flex-1 flex flex-col items-center relative"
- >
- <view
- class="w-8 h-8 rounded-full flex items-center justify-center text-xs mb-1"
- :class="step.active ? 'bg-primary text-white' : 'bg-gray-100 text-gray-400'"
- >
- <text class="uni-icons" :class="step.icon"></text>
- </view>
- <text class="text-xs" :class="step.active ? 'text-primary font-medium' : 'text-gray-400'">{{ step.label }}</text>
- </view>
- </view>
- </uni-card>
- <!-- 施工照片 -->
- <uni-card title="施工照片" icon="uniui-camera-filled" :is-shadow="true">
- <ImageUploader v-model="taskImages" tip="上传施工过程照片" />
- </uni-card>
- <!-- 地图位置 -->
- <uni-card title="项目位置" icon="uniui-location-filled" :is-shadow="true">
- <MapView
- :latitude="taskLatitude"
- :longitude="taskLongitude"
- :title="task.projectName"
- :address="task.address"
- :height="180"
- />
- </uni-card>
- <!-- 操作按钮 -->
- <uni-card :is-shadow="true">
- <view class="grid grid-cols-2 gap-3">
- <button
- v-if="canUrge"
- class="py-3 bg-warning text-white rounded-xl text-sm flex items-center justify-center"
- @click="urgeTask"
- >
- <text class="uni-icons uniui-notification-filled mr-1"></text>
- 催单
- </button>
- <button
- v-if="canCancel"
- class="py-3 bg-danger text-white rounded-xl text-sm flex items-center justify-center"
- @click="cancelTask"
- >
- <text class="uni-icons uniui-closeempty mr-1"></text>
- 取消任务
- </button>
- <button
- v-if="canViewAcceptance"
- class="py-3 bg-success text-white rounded-xl text-sm flex items-center justify-center"
- @click="viewAcceptance"
- >
- <text class="uni-icons uniui-checkmarkempty mr-1"></text>
- 查看验收单
- </button>
- <button
- v-if="canRebook"
- class="py-3 bg-primary text-white rounded-xl text-sm flex items-center justify-center"
- @click="rebookTask"
- >
- <text class="uni-icons uniui-calendar-filled mr-1"></text>
- 再次预约
- </button>
- </view>
- </uni-card>
- </view>
- </view>
- </template>
- <script setup lang="ts">
- import { ref, computed, onMounted, onUnmounted } from 'vue'
- import { useTaskStore } from '../../stores/task'
- import { useAuthStore } from '../../stores/auth'
- import TopBar from '../../components/common/TopBar.vue'
- import StatusTag from '../../components/common/StatusTag.vue'
- import EmptyState from '../../components/common/EmptyState.vue'
- import UniCard from '../../components/uni-card/uni-card.vue'
- import UniList from '../../components/uni-list/uni-list.vue'
- import UniListItem from '../../components/uni-list/uni-list-item.vue'
- import { urgeTask as apiUrgeTask, cancelTask as apiCancelTask } from '../../api/task'
- import ImageUploader from '../../components/common/ImageUploader.vue'
- import MapView from '../../components/common/MapView.vue'
- const taskStore = useTaskStore()
- const authStore = useAuthStore()
- const task = computed(() => taskStore.currentTask)
- const taskImages = ref<string[]>([])
- const taskLatitude = computed(() => task.value?.location?.lat || 39.9)
- const taskLongitude = computed(() => task.value?.location?.lng || 116.4)
- const serviceTimeText = computed(() => {
- if (!task.value) return '待定'
- const date = task.value.serviceDate || task.value.scheduleDate || ''
- const time = task.value.serviceTime || task.value.scheduleTime || ''
- return date || time ? `${date} ${time}`.trim() : '待定'
- })
- const progressSteps = computed(() => {
- const status = task.value?.status || 'pending'
- return [
- { key: 'publish', label: '发布', icon: 'uniui-compose', active: true },
- { key: 'dispatch', label: '调度确认', icon: 'uniui-flag-filled', active: ['confirmed', 'scheduled', 'departed', 'arrived', 'constructing', 'inspecting', 'completed'].includes(status) },
- { key: 'constructing', label: '施工中', icon: 'uniui-gear-filled', active: ['constructing', 'inspecting', 'completed'].includes(status) },
- { key: 'completed', label: '完成', icon: 'uniui-checkmarkempty', active: status === 'completed' },
- ]
- })
- const canUrge = computed(() => ['pending', 'confirmed', 'scheduled'].includes(task.value?.status || ''))
- const canCancel = computed(() => ['pending', 'confirmed', 'scheduled'].includes(task.value?.status || ''))
- const canViewAcceptance = computed(() => task.value?.status === 'completed')
- const canRebook = computed(() => task.value?.status === 'completed')
- function callPhone(phone: string | undefined) {
- if (!phone) return
- uni.makePhoneCall({ phoneNumber: phone })
- }
- async function urgeTask() {
- if (!task.value) return
- try {
- await apiUrgeTask(
- Number(task.value.id),
- '客户催单',
- Number(authStore.user?.id || 0),
- authStore.user?.name || '销售'
- )
- uni.showToast({ title: '催单成功', icon: 'success' })
- } catch (error) {
- uni.showToast({ title: '催单失败', icon: 'none' })
- }
- }
- async function cancelTask() {
- if (!task.value) return
- uni.showModal({
- title: '确认取消',
- content: '确定要取消此任务吗?',
- success: async (res) => {
- if (res.confirm) {
- try {
- await apiCancelTask(
- Number(task.value!.id),
- '销售取消',
- Number(authStore.user?.id || 0),
- authStore.user?.name || '销售'
- )
- uni.showToast({ title: '取消成功', icon: 'success' })
- await taskStore.fetchTaskDetail(Number(task.value!.id))
- } catch (error) {
- uni.showToast({ title: '取消失败', icon: 'none' })
- }
- }
- },
- })
- }
- function viewAcceptance() {
- uni.showToast({ title: '验收单功能开发中', icon: 'none' })
- }
- function rebookTask() {
- uni.navigateTo({ url: '/subPackages/pages-common/publishTask' })
- }
- onMounted(() => {
- if (!task.value) {
- const pages = getCurrentPages()
- const currentPage = pages[pages.length - 1] as any
- const taskId = currentPage.options?.id
- if (taskId) {
- taskStore.fetchTaskDetail(Number(taskId))
- }
- }
- })
- onUnmounted(() => {
- taskStore.currentTask = null
- })
- </script>
|