<template>
|
<van-floating-panel v-model:height="panelHeight" :anchors="anchors">
|
<template #header>
|
<div class="custom-header">
|
<div class="bar"></div>
|
</div>
|
</template>
|
<div class="workDetailContainer">
|
<div class="detailTop">
|
<div class="image-container">
|
<van-swipe class="detailSwipe" :autoplay="3000" indicator-color="#4C85FF">
|
<van-swipe-item v-for="(img, index) in getImageList" :key="index">
|
<van-image
|
class="detailImage"
|
:src="img"
|
fit="cover"
|
width="100%"
|
height="200px"
|
@click="openPreview(index)"
|
preview-visible="false"
|
/>
|
</van-swipe-item>
|
</van-swipe>
|
|
<van-image-preview v-model:show="previewShow" :images="getImageList" :initial-index="previewIndex" />
|
<!-- <van-image class="detailImage" :src="currentDetail.photo_url" fit="cover" width="100%" height="200px" /> -->
|
<div class="detailTitle">
|
<div class="titleText">
|
<div class="itemStatus">
|
<span v-if="currentDetail.status === 0" style="background-color: #ff7411"></span>
|
<span v-else-if="currentDetail.status === 2" style="background-color: #ff472f"></span>
|
<span v-else-if="currentDetail.status === 3" style="background-color: #ffc300"></span>
|
<span v-else-if="currentDetail.status === 4" style="background-color: #06d957"></span>
|
<div>{{ currentDetail.event_name }}</div>
|
</div>
|
<div class="timeNavigation">
|
<p>{{ formatDate(currentDetail.create_time) }}</p>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<!-- 步骤条 -->
|
<div class="stepContainer">
|
<van-steps direction="vertical" :active="currentStep">
|
<van-step v-for="(step, index) in displayedSteps" :key="step.status">
|
<div class="horizontal-step">
|
<span class="step-title" :class="getStatusClass(index)">{{ step.title }}</span>
|
|
<div class="step-desc" v-if="stepResponse[index]">
|
<span>{{ stepResponse[index].name || '' }}</span>
|
<span>{{ stepResponse[index].create_time || '' }}</span>
|
</div>
|
</div>
|
</van-step>
|
</van-steps>
|
</div>
|
<!-- 工单内容 -->
|
<div class="workOrderContent">
|
<div class="workOrderTitle">工单内容</div>
|
<div class="workOrderContainer">
|
<div class="orderRow">
|
<div class="rowTitle">工单名称</div>
|
|
<div>{{ currentDetail.event_name }}</div>
|
</div>
|
<div class="orderRow">
|
<div class="rowTitle">工单编号</div>
|
<div>{{ currentDetail.event_num }}</div>
|
</div>
|
<div class="orderRow">
|
<div class="rowTitle">工单类型</div>
|
<div>{{ workOrderTypeName }}</div>
|
</div>
|
<div class="orderRow" v-if="currentDetail.work_type == '0'">
|
<div class="rowTitle">关联任务</div>
|
<div>{{ currentDetail.job_name }}</div>
|
</div>
|
<div class="orderRow">
|
<div class="rowTitle">工单创建人</div>
|
<div>{{ currentDetail.event_num?.slice(0, 2) === 'AI' ? 'AI 小飞' : currentDetail.create_user }}</div>
|
</div>
|
<div class="orderRow" v-if="currentDetail.address">
|
<div class="rowTitle">事件地址</div>
|
<div class="rowAddress">{{ currentDetail.address }}</div>
|
</div>
|
|
<div class="orderRow">
|
<div class="guanlian">
|
<div class="rowTitle">关联算法</div>
|
</div>
|
<div>{{ currentDetail.ai_types }}</div>
|
</div>
|
<div class="orderRow">
|
<div class="rowTitle">发起部门</div>
|
<div>{{ currentDetail.dept_name }}</div>
|
</div>
|
<div class="orderRow">
|
<div class="rowTitle">发起任务时间</div>
|
<div>{{ currentDetail.create_time }}</div>
|
</div>
|
<div class="orderRow">
|
<div class="rowTitle">所属机巢</div>
|
<div>{{ currentDetail.device_names }}</div>
|
</div>
|
<div class="orderRow">
|
<div class="rowTitle">工单内容</div>
|
<div>{{ currentDetail.content }}</div>
|
</div>
|
|
<div class="orderRow" v-if="isEditableProcess">
|
<div class="rowTitle">事件处理详情</div>
|
|
<div>{{ currentDetail.processingDetail }}</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</van-floating-panel>
|
</template>
|
|
<script setup>
|
import { useRoute } from 'vue-router'
|
import { getStepInfo, getList, flowEvent, getTicketInfo } from '/src/api/work/index.js'
|
import { getShowImg, getSmallImg } from '@/utils/util'
|
|
import dayjs from 'dayjs'
|
import { showImagePreview } from 'vant'
|
const anchors = [65, Math.round(0.9 * window.innerHeight)]
|
const panelHeight = ref(anchors[0])
|
|
const allAlgorithms = ref([])
|
const algorithms = ref([])
|
const types = ref([]) //工单类型
|
|
const stepResponse = ref([])
|
const currentStep = ref(0)
|
const route = useRoute()
|
const currentDetail = ref({})
|
|
const isEditableProcess = computed(() => currentDetail.value.status === 3)
|
const props = defineProps({
|
mapCurrentDetail: {
|
type: Object,
|
},
|
})
|
|
// 工单内容
|
const formatDate = dateString => {
|
return dayjs(dateString).format('MM/DD HH:mm')
|
}
|
const allStepConfigs = ref([
|
{ title: '待审核', status: '2' },
|
{ title: '待处理', status: '0' },
|
{ title: '处理中', status: '3' },
|
{ title: '已完成', status: '4' },
|
])
|
|
const getStatusClass = index => {
|
if (index <= currentStep.value) {
|
const status = stepResponse.value[index]?.status
|
const statusClasses = {
|
'2': 'status-pending', // 待审核
|
'0': 'status-waiting', // 待处理
|
'3': 'status-processing', // 处理中
|
'4': 'status-completed', // 已完成
|
}
|
return statusClasses[String(status)] || ''
|
}
|
|
return 'status-default'
|
}
|
// 关联算法
|
const getTicketInfoData = async () => {
|
const response = await getTicketInfo()
|
const { dept_data, event_type, ai_type, info } = response.data.data
|
allAlgorithms.value = info
|
|
types.value = Object.entries(event_type).map(([key, value]) => ({
|
label: value,
|
value: key,
|
}))
|
}
|
|
// 计算要显示的步骤
|
const displayedSteps = computed(() => {
|
return allStepConfigs.value.filter(stepConfig => {
|
return stepResponse.value.some(stepData => String(stepData?.status) === stepConfig.status)
|
})
|
})
|
// 计算工单类型显示名称
|
const workOrderTypeName = computed(() => {
|
const type = types.value.find(item => item.value === currentDetail.value.work_order_type_dict_key)
|
return type ? type.label : currentDetail.value.work_order_type_dict_key
|
})
|
// 步骤条
|
const calculateCurrentStep = status => {
|
const stepIndex = displayedSteps.value.findIndex(step => step.status === String(status))
|
return stepIndex !== -1 ? stepIndex : 0
|
}
|
|
const getStepInfoData = async val => {
|
const res = await getStepInfo(val)
|
stepResponse.value = res.data.data
|
currentStep.value = calculateCurrentStep(currentDetail.value.status)
|
}
|
// 预览图片
|
const previewShow = ref(false)
|
const previewIndex = ref(0)
|
|
const getImageList = computed(() => {
|
const imageArr = []
|
const detail = currentDetail.value
|
|
if (detail.photo_url) {
|
const smallUrl = getSmallImg(detail.photo_url)
|
imageArr.push(smallUrl)
|
}
|
|
if (detail.update_photo_url) {
|
const smallUrl = getSmallImg(detail.update_photo_url)
|
imageArr.push(smallUrl)
|
}
|
|
return imageArr
|
})
|
|
const openPreview = index => {
|
const detail = currentDetail.value
|
const showUrl = getShowImg(detail.photo_url || detail.update_photo_url)
|
showImagePreview({
|
images: [showUrl],
|
startPosition: 0,
|
})
|
previewIndex.value = index
|
previewShow.value = true
|
}
|
watch(
|
() => props.mapCurrentDetail,
|
newVal => {
|
if (newVal?.event_num) {
|
currentDetail.value = newVal
|
getStepInfoData(currentDetail.value.event_num)
|
}
|
},
|
{ immediate: true, deep: true }
|
)
|
onMounted(() => {
|
getTicketInfoData()
|
})
|
</script>
|
|
<style lang="scss" scoped>
|
.van-floating-panel {
|
background: transparent;
|
::v-deep(.custom-header) {
|
.bar {
|
margin: 0 auto;
|
width: 42px;
|
height: 4px;
|
background: rgba(0, 0, 0, 0.5);
|
border-radius: 2px;
|
margin-bottom: 4px;
|
}
|
}
|
::v-deep(.van-floating-panel__content) {
|
border-radius: 20px 20px 0 0;
|
}
|
}
|
.workDetailContainer {
|
padding: 30px 10px 0 10px;
|
.required-mark {
|
color: #ff4d4f; // 红色
|
margin-left: 4px;
|
}
|
.detailTop {
|
.image-container {
|
position: relative;
|
width: 100%;
|
height: 205px;
|
|
.detailImage {
|
width: 100%;
|
height: 100%;
|
display: block;
|
object-fit: cover;
|
}
|
|
.detailTitle {
|
position: absolute;
|
left: 0;
|
top: 0;
|
width: 100%;
|
padding: 5px;
|
height: 30px;
|
background: rgba(7, 7, 7, 0.4);
|
}
|
|
.titleText {
|
display: flex;
|
width: 100%;
|
justify-content: space-between;
|
align-items: center;
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
font-weight: 400;
|
font-size: 13px;
|
color: #ffffff;
|
.itemStatus {
|
display: flex;
|
align-items: center;
|
|
span {
|
display: inline-block;
|
width: 10px;
|
height: 10px;
|
border-radius: 50%;
|
margin-right: 7px;
|
}
|
}
|
}
|
.timeNavigation {
|
display: flex;
|
align-items: center;
|
img {
|
width: 11px;
|
height: 13px;
|
margin-left: 13px;
|
}
|
}
|
}
|
}
|
|
.stepContainer {
|
display: flex;
|
|
border-radius: 5px;
|
padding: 0 10px 0 10px;
|
background: #fff;
|
:deep() {
|
.van-step__circle-container {
|
font-size: 20px;
|
}
|
.van-step__circle {
|
width: 15px;
|
height: 15px;
|
}
|
.van-step--vertical {
|
padding: 20px 20px 20px 0;
|
}
|
.van-step__circle-container,
|
.van-step__line {
|
top: 50%;
|
}
|
.van-step--vertical:not(:last-child):after {
|
border-bottom-width: 0;
|
}
|
}
|
.horizontal-step {
|
display: flex;
|
align-items: center;
|
gap: 12px;
|
|
.step-title {
|
min-width: 60px;
|
background: #e8e8e8;
|
padding: 5px;
|
border-radius: 5px;
|
text-align: center;
|
|
&.status-pending {
|
background-color: #ffebeb; // 待审核
|
color: #ff1414;
|
}
|
|
&.status-waiting {
|
background-color: #fff1e6; // 待处理
|
color: #ff7411;
|
}
|
|
&.status-processing {
|
background-color: #fff6d8; // 处理中
|
color: #ffbb00;
|
}
|
|
&.status-completed {
|
background-color: #e5fcff; // 已完成
|
color: #0291a1;
|
}
|
|
&.status-default {
|
background-color: #e8e8e8; // 默认背景色
|
color: #191919;
|
}
|
}
|
|
.step-desc {
|
display: flex;
|
justify-content: space-between;
|
gap: 28px;
|
color: #222324;
|
font-size: 14px;
|
}
|
.step-desc span:first-child {
|
width: 60px;
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
span:last-child {
|
margin-left: auto;
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
}
|
}
|
|
:deep(.van-step__circle) {
|
width: 8px;
|
height: 8px;
|
}
|
|
.workOrderContent {
|
margin-top: 15px;
|
|
.workOrderTitle {
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
font-weight: bold;
|
font-size: 16px;
|
color: #222324;
|
margin-bottom: 16px;
|
}
|
|
.workOrderContainer {
|
.orderRow {
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
min-height: 48px;
|
border-bottom: 1px solid #f5f5f5;
|
color: #7b7b7b;
|
.guanlian {
|
display: flex;
|
}
|
.rowTitle {
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
font-weight: 400;
|
font-size: 15px;
|
color: #222324;
|
white-space: nowrap;
|
|
span {
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
font-weight: 400;
|
font-size: 10px;
|
color: #3a3a3a;
|
}
|
}
|
.rowAddress {
|
font-size: 14px;
|
// color: #1d6fe9;
|
white-space: nowrap; /* 禁止换行 */
|
overflow: hidden;
|
text-overflow: ellipsis;
|
max-width: 75%;
|
}
|
.selectTrigger {
|
font-family: Source Han Sans CN, Source Han Sans CN;
|
font-weight: 400;
|
font-size: 14px;
|
color: #222324;
|
}
|
}
|
|
.titketName {
|
display: flex;
|
align-items: center;
|
}
|
}
|
}
|
|
.upload-link {
|
color: #1989fa;
|
cursor: pointer;
|
text-decoration: underline;
|
}
|
}
|
</style>
|