staff/pagesA/task_hall/detail.vue

566 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="li-message-page">
<!-- 自定义导航栏 -->
<wd-navbar :bordered="false"
custom-style="background: transparent !important; backdrop-filter: blur(10px) !important; -webkit-backdrop-filter: blur(10px) !important;"
safeAreaInsetTop fixed placeholder>
<template #left>
<view class="li-ml-15 li-mt-10 li-flex li-items-center">
<text v-if="hasMultiplePages" class="ri-arrow-left-s-line li-text-70"
@click="toPages({type:'nav'})"></text>
<text v-if="!hasMultiplePages" class="ri-home-5-line li-text-55 li-mb-8 li-mr-10"
@click="toPages({type:'home'})"></text>
<text class="li-text-42">工单详情</text>
</view>
</template>
</wd-navbar>
<!-- 导航栏背景 -->
<view class="nav-bg-layer"></view>
<!-- card -->
<view class="bg-#FFFFFF li-w-92% li-mx-auto li-rd-20 li-task-card li-pb-25 overflow-hidden li-mt-40">
<view class="li-flex li-items-start li-justify-between bg-#f9f9f9">
<view class="li-mb-20">
<view class="li-flex li-items-center li-text-#5f5f5f li-pl-20 li-pt-20 li-text-28 li-mb-12">
<text class="ri-file-list-line li-mr-3"></text>
<text>{{ticketInfo?.ticket_no}}</text>
<text class="ri-file-copy-line li-text-#009aff li-ml-15" @click="copyTicketNo"></text>
</view>
<text class="li-pl-20 li-text-28 li-text-#5f5f5f">创建时间:&nbsp;{{ticketInfo?.create_time}}</text>
</view>
<view v-if="ticketInfo?.status !== undefined" class="status-tag li-text-28 li-px-40 li-py-6"
:class="['status-' + ticketInfo.status]">
{{getStatusText(ticketInfo.status)}}
</view>
</view>
<view class="li-flex li-items-start li-pt-20 li-pl-20">
<text v-if="ticketInfo?.status !== undefined" :class="getStatusIcon(ticketInfo.status)"
class="li-mr-12 li-text-40 li-pt-2"></text>
<view class="li-flex li-flex-col">
<text
class="li-text-32">{{ticketInfo?.status !== undefined ? getStatusText(ticketInfo.status) : ''}}</text>
<text
class="li-text-28 li-text-#666">{{ticketInfo?.status !== undefined ? getStatusDescription(ticketInfo.status) : ''}}</text>
</view>
</view>
</view>
<!-- card -->
<view class="bg-#FFFFFF li-w-92% li-mx-auto li-mt-20 li-rd-20 li-task-card li-pb-30 overflow-hidden">
<view class="li-pl-30 li-pt-30">业主信息</view>
<view class="li-pl-30 li-pr-30 li-mt-30">
<view class="li-flex li-items-center li-justify-between li-text-28">
<text class="li-w-150 li-text-#9a9a9a">业主姓名</text>
<text class="li-w-400 li-single-line li-text-right">{{ticketInfo?.order?.name}}</text>
</view>
<view class="li-flex li-items-center li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">联系方式</text>
<view class="li-flex li-items-center li-w-400 li-single-line justify-between">
<!-- 左侧留空 -->
<view></view>
<!-- 右侧内容 -->
<view @click="callNumber" class="li-flex li-items-center">
<text class="ri-phone-line li-text-#009aff li-mr-6"></text>
<text>{{ticketInfo?.order?.mobile}}</text>
</view>
</view>
</view>
<view class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text v-if="ticketInfo?.type =='F4' || ticketInfo?.type =='F2'"
class="li-w-150 li-text-#9a9a9a">上门地址</text>
<text v-if="ticketInfo?.type =='F6'" class="li-w-150 li-text-#9a9a9a">报修地址</text>
<text
class="li-w-400 li-two-line li-text-right">{{ticketInfo?.order?.village_name}}{{ticketInfo?.order?.region_name}}{{ticketInfo?.order?.cell_name}}{{ticketInfo?.order?.house_name}}</text>
</view>
<view v-if="ticketInfo?.type =='F2'"
class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">商品备注</text>
<text class="li-w-400 li-two-line li-text-right">{{ticketInfo?.order?.address}}</text>
</view>
<view v-if="ticketInfo?.type =='F4'"
class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">预约时间</text>
<text class="li-w-400 li-two-line li-text-right">{{ticketInfo?.order?.appoint_date}}
{{ticketInfo?.order?.appoint_time}}</text>
</view>
<view v-if="ticketInfo?.type =='F4'"
class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">建筑面积</text>
<text class="li-w-400 li-two-line li-text-right">{{ticketInfo?.order?.building_area}}</text>
</view>
<view v-if="ticketInfo?.type =='F4'"
class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">订单状态</text>
<view class="li-w-400 li-text-right">
<text :class="['info-tag', 'status-' + ticketInfo?.order?.status]">
{{getOrderStatusText(ticketInfo?.order?.status)}}
</text>
</view>
</view>
<view v-if="ticketInfo?.order?.remark"
class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">工单备注</text>
<text class="li-w-400 li-two-line li-text-right">{{ticketInfo?.order?.remark}}</text>
</view>
<view v-if="ticketInfo.type == 'F6'"
class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">报修内容</text>
<text class="li-w-400 li-two-line li-text-right">{{ticketInfo?.order?.content}}</text>
</view>
<view class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">工单类型</text>
<view class="li-w-400 li-text-right">
<text v-if="ticketInfo?.type === 'F2'" class="info-tag type-delivery">商品</text>
<text v-if="ticketInfo?.type === 'F4'" class="info-tag type-measure">量房</text>
<text v-if="ticketInfo?.type === 'F6'" class="info-tag type-repair">维修</text>
</view>
</view>
<view v-if="(ticketInfo?.type =='F4' || ticketInfo?.type =='F6') && ticketInfo.order.images"
class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">附件上传</text>
<view class="li-flex li-flex-col li-w-400 justify-end">
<view class="li-flex justify-between li-items-center li-mt-20">
<!-- 左侧留空 -->
<view></view>
<!-- 右侧内容 -->
<view class="li-flex li-items-center li-flex-wrap">
<image
v-for="(item, index) in ticketInfo.order.images.split(',')"
:key="index"
class="li-w-110 li-h-110 li-rd-10 li-mr-10 li-mb-10"
:src="item"
mode="aspectFill"
@click="previewImage(item)">
</image>
</view>
</view>
</view>
</view>
<view v-if="ticketInfo?.type =='F2' && ticketInfo?.product?.length>0"
class="li-flex li-items-start li-justify-between li-mt-30 li-text-28">
<text class="li-w-150 li-text-#9a9a9a">商品信息</text>
<view class="li-w-400 li-flex li-flex-col">
<!-- 商品列表 -->
<view v-for="(item, index) in ticketInfo.product" :key="index" class="li-product-item li-mb-20">
<view class="li-flex li-items-center">
<!-- 商品图片 -->
<image class="li-product-image" :src="item.picture" mode="aspectFill"
@click="previewImage(item.picture)">
</image>
<!-- 商品信息 -->
<view class="li-product-info">
<text class="li-product-name">{{item.product_name}}</text>
<text class="li-product-spec">{{item.spec_name || ''}}</text>
<view class="li-qty">×{{item.num}}</view>
</view>
</view>
<!-- 分隔线 -->
<view v-if="index < ticketInfo.product.length - 1" class="li-divider"></view>
</view>
<!-- 商品总计 -->
<view v-if="ticketInfo.product.length > 1" class="li-product-count">
共 {{ticketInfo.product.length}} 件商品
</view>
</view>
</view>
</view>
</view>
<view class="li-w-85% li-mx-auto li-mt-90">
<wd-button @click="sumbit" :loading="bntloading" block custom-class="custom-shadow">&nbsp;接单</wd-button>
</view>
<wd-toast />
<zero-loading type="wobble" v-if="loading"></zero-loading>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { useNavigation } from '@/hooks/useNavigation'
import { ticketPoolInfo, ticketAccept } from '@/api/ticket'
import { useToast } from '@/uni_modules/wot-design-uni'
import { throttle } from '@/utils/common'
const Toast = useToast()
const loading = ref<boolean>(false)
const bntloading = ref<boolean>(false)
const ticketInfo = ref<any>({
ticket_no: '',
create_time: '',
status: undefined,
type: '',
order: {
name: '',
mobile: '',
region_name: '',
cell_name: '',
house_name: '',
address: '',
appoint_date: '',
appoint_time: '',
building_area: '',
status: 0,
images: '',
remark: ''
}
})
// 使用导航 composable
const {
hasMultiplePages,
isTabBarPage,
checkRouteStack
} = useNavigation()
onLoad((q) => {
checkRouteStack()
if (q.ticket_id) {
loadInfo(q.ticket_id)
}
})
const loadInfo = async (ticket_id : string) => {
try {
loading.value = true
const res = await ticketPoolInfo({ ticket_id })
if (res.data) {
ticketInfo.value = {
...ticketInfo.value,
...res.data
}
}
} catch (error) {
Toast.error('加载失败,请重试')
console.error('加载工单信息失败:', error)
} finally {
loading.value = false
}
}
// 复制工单号
const copyTicketNo = () => {
if (!ticketInfo.value?.ticket_no) return
uni.setClipboardData({
data: ticketInfo.value.ticket_no,
showToast: false,
success: () => {
Toast.success('工单号已复制')
}
})
}
const callNumber = () => {
// uni.makePhoneCall({
// phoneNumber: ticketInfo.value.order.mobile
// });
}
const sumbit = throttle(async () => {
bntloading.value = true
var res = await ticketAccept({ ticket_id: ticketInfo.value.ticket_id })
bntloading.value = false
if (res.code == 200) {
Toast.success('已接单')
setTimeout(() => {
uni.navigateBack({
delta: 1
})
}, 500)
}
}, 500)
const toPages = (item : any) => {
switch (item.type) {
case 'nav':
uni.navigateBack({
delta: 1
});
break;
case 'home':
uni.switchTab({
url: '/pages/index/index'
})
break;
default:
break;
}
}
// 状态配置
const statusConfig = {
0: {
color: '#1890ff',
bgColor: '#f0f9ff',
icon: 'ri-time-line',
text: '待接单',
description: '工单待处理,点击按钮确认接单'
},
1: {
color: '#1890ff',
bgColor: '#f0f9ff',
icon: 'ri-truck-line',
text: '已派单',
description: '工单已分配,请及时处理'
},
2: {
color: '#52c41a',
bgColor: '#f0fff5',
icon: 'ri-checkbox-circle-line',
text: '已完成',
description: '工单已完成处理'
},
3: {
color: '#8c8c8c',
bgColor: '#f5f5f5',
icon: 'ri-close-circle-line',
text: '已取消',
description: '工单已取消'
}
}
// 订单状态配置
const orderStatusConfig = {
0: { text: '已提交', color: '#ff9d00', bgColor: '#fff7e6' },
1: { text: '已完成', color: '#52c41a', bgColor: '#f6ffed' },
2: { text: '已取消', color: '#8c8c8c', bgColor: '#fafafa' }
}
// 工单类型配置
const typeConfig = {
F2: { text: '配送', color: '#1890ff', bgColor: '#f0f7ff' },
F4: { text: '量房', color: '#722ed1', bgColor: '#f9f0ff' },
F6: { text: '维修', color: '#13c2c2', bgColor: '#f0fafa' }
}
// 获取状态颜色
const getStatusColor = (status : number) => {
return statusConfig[status]?.color || '#666666'
}
// 获取状态背景色
const getStatusBgColor = (status : number) => {
return statusConfig[status]?.bgColor || '#f5f5f5'
}
// 获取状态文字
const getStatusText = (status : number) => {
return statusConfig[status]?.text || '未知状态'
}
// 获取状态图标
const getStatusIcon = (status : number) => {
return `${statusConfig[status]?.icon} li-text-${getStatusColor(status).substring(1)}`
}
// 获取状态描述
const getStatusDescription = (status : number) => {
return statusConfig[status]?.description || '状态未知'
}
// 获取订单状态文字
const getOrderStatusText = (status : number) => {
return orderStatusConfig[status]?.text || '未知状态'
}
// 预览商品图片
const previewImage = (image) => {
if (!image) return
uni.previewImage({
urls: [image],
current: image
})
}
</script>
<style lang="scss">
.li-message-page {
width: 100%;
min-height: 100vh;
background: linear-gradient(to bottom,
rgba(217, 237, 255, 0.9) 0%,
rgba(217, 237, 255, 0.9) 20%,
rgba(207, 207, 207, 0.2) 40%,
rgba(207, 207, 207, 0.2) 60%,
rgba(207, 207, 207, 0.2) 80%,
rgba(207, 207, 207, 0.2) 100%);
background-attachment: fixed;
}
// 导航栏背景层
.nav-bg-layer {
position: fixed;
top: 0;
left: 0;
right: 0;
height: var(--window-top);
background: linear-gradient(to bottom,
rgba(217, 237, 255, 0.95),
rgba(217, 237, 255, 0.85));
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
z-index: 998;
}
.li-task-card {
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.status-tag {
border-radius: 0 0 0 25rpx;
&.status-0 {
color: #1890ff;
background-color: #f0f9ff;
}
&.status-1 {
color: #1890ff;
background-color: #f0f9ff;
}
&.status-2 {
color: #52c41a;
background-color: #f0fff5;
}
&.status-3 {
color: #8c8c8c;
background-color: #f5f5f5;
}
}
.info-tag {
display: inline-block;
padding: 4rpx 16rpx;
font-size: 24rpx;
border-radius: 4rpx;
&.type-delivery {
color: #1890ff;
background-color: #f0f7ff;
border: 1px solid #e6f4ff;
}
&.type-measure {
color: #722ed1;
background-color: #f9f0ff;
border: 1px solid #f4e6ff;
}
&.type-repair {
color: #13c2c2;
background-color: #f0fafa;
border: 1px solid #e6f7f7;
}
&.status-0 {
color: #ff9d00;
background-color: #fff7e6;
border: 1px solid #ffedc7;
}
&.status-1 {
color: #52c41a;
background-color: #f6ffed;
border: 1px solid #e8ffd1;
}
&.status-2 {
color: #8c8c8c;
background-color: #fafafa;
border: 1px solid #f0f0f0;
}
}
::v-deep .wd-button {
width: 160rpx !important;
height: 65rpx !important;
min-width: 160rpx !important;
}
.custom-shadow {
height: 85rpx !important;
font-size: 32rpx !important;
box-shadow: 0 3px 1px -2px rgb(0 0 0 / 20%), 0 2px 2px 0 rgb(0 0 0 / 14%), 0 1px 5px 0 rgb(0 0 0 / 12%);
background-color: #2EA1EA !important;
width: 100% !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
}
// 商品项样式
.li-product-item {
position: relative;
padding-bottom: 15rpx;
.li-product-image {
width: 120rpx;
height: 120rpx;
flex-shrink: 0;
border-radius: 10rpx;
background-color: #f9f9f9;
border: 1px solid #f5f5f5;
}
.li-product-info {
flex: 1;
margin-left: 20rpx;
overflow: hidden;
padding-right: 10rpx;
}
.li-product-name {
font-size: 28rpx;
color: #333;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
.li-product-spec {
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
display: block;
}
.li-qty {
font-size: 26rpx;
color: #666;
margin-top: 10rpx;
}
.li-divider {
height: 1px;
background-color: #f5f5f5;
margin-top: 15rpx;
width: 100%;
}
}
.li-product-count {
font-size: 26rpx;
color: #999;
text-align: right;
margin-top: 10rpx;
padding-top: 15rpx;
border-top: 1px solid #f0f0f0;
}
</style>