<template>
<div class="all-approve-container">
<!-- 保持原有的卡片部分不变 -->
<el-card class="approve-card">
<template #header>
<div class="card-header">
<span>工单审批</span>
<div class="header-right">
<el-select v-model="currentType" @change="handleTypeChange" style="margin-right: 15px;width: 120px">
<el-option label="全部" value="all" />
<el-option label="巡检工单" value="inspection" />
<el-option label="保养工单" value="maintenance" />
<el-option label="检测工单" value="testing" />
<el-option label="维修工单" value="repair" />
</el-select>
<el-button type="primary" @click="fetchOrders">刷新</el-button>
</div>
</div>
</template>
<el-table :data="orderList" border style="width: 100%" height="450" v-loading="loading">
<el-table-column prop="orderId" label="工单编号" width="100" />
<el-table-column prop="deviceId" label="设备ID" width="120" />
<el-table-column prop="engineerId" label="工程师工号" width="120" />
<el-table-column prop="description" label="工单描述" min-width="200" show-overflow-tooltip />
<el-table-column prop="orderType" label="工单类型" width="100">
<template #default="scope">
<el-tag>{{ getOrderTypeText(scope.row.orderType) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">{{ scope.row.status }}</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<template #default="scope">
<el-button
v-if="scope.row.status === '待审批'"
type="success"
size="small"
@click="handleApprove(scope.row)"
>
通过
</el-button>
<el-button
v-if="scope.row.status === '待审批'"
type="danger"
size="small"
@click="handleReject(scope.row)"
>
驳回
</el-button>
<el-button
type="primary"
size="small"
@click="handleDetail(scope.row)"
>
详情
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 四个不同类型的详情弹窗 -->
<el-dialog v-model="inspectionDialog" title="巡检工单详情" width="600px">
<div class="detail-content">
<el-descriptions :column="1" border>
<el-descriptions-item label="工单编号">{{ currentOrder.orderId }}</el-descriptions-item>
<el-descriptions-item label="计划编号">{{ currentOrder.planId }}</el-descriptions-item>
<el-descriptions-item label="设备ID">{{ currentOrder.deviceId }}</el-descriptions-item>
<el-descriptions-item label="工程师工号">{{ currentOrder.engineerId }}</el-descriptions-item>
<el-descriptions-item label="巡检描述">{{ currentOrder.description }}</el-descriptions-item>
<el-descriptions-item label="巡检照片" v-if="currentOrder.photos">
<div class="photos">
<el-image
v-for="(photo, index) in processPhotos(currentOrder.photos)"
:key="index"
:src="photo"
:preview-src-list="processPhotos(currentOrder.photos)"
fit="cover"
class="photo-item"
/>
</div>
</el-descriptions-item>
</el-descriptions>
</div>
</el-dialog>
<el-dialog v-model="maintenanceDialog" title="保养工单详情" width="600px">
<div class="detail-content">
<el-descriptions :column="1" border>
<el-descriptions-item label="工单编号">{{ currentOrder.orderId }}</el-descriptions-item>
<el-descriptions-item label="计划编号">{{ currentOrder.planId }}</el-descriptions-item>
<el-descriptions-item label="设备ID">{{ currentOrder.deviceId }}</el-descriptions-item>
<el-descriptions-item label="工程师工号">{{ currentOrder.engineerId }}</el-descriptions-item>
<el-descriptions-item label="保养描述">{{ currentOrder.description }}</el-descriptions-item>
<el-descriptions-item label="保养前照片" v-if="currentOrder.beforePhotos">
<div class="photos">
<el-image
v-for="(photo, index) in processPhotos(currentOrder.beforePhotos)"
:key="index"
:src="photo"
:preview-src-list="processPhotos(currentOrder.beforePhotos)"
fit="cover"
class="photo-item"
/>
</div>
</el-descriptions-item>
<el-descriptions-item label="保养后照片" v-if="currentOrder.afterPhotos">
<div class="photos">
<el-image
v-for="(photo, index) in processPhotos(currentOrder.afterPhotos)"
:key="index"
:src="photo"
:preview-src-list="processPhotos(currentOrder.afterPhotos)"
fit="cover"
class="photo-item"
/>
</div>
</el-descriptions-item>
</el-descriptions>
</div>
</el-dialog>
<!-- 检测工单详情弹窗 -->
<el-dialog
v-model="testingDialog"
title="检测工单详情"
width="600px"
>
<div class="detail-content">
<el-descriptions :column="1" border>
<el-descriptions-item label="工单编号">{{ currentOrder.orderId }}</el-descriptions-item>
<el-descriptions-item label="计划编号">{{ currentOrder.planId }}</el-descriptions-item>
<el-descriptions-item label="设备ID">{{ currentOrder.deviceId }}</el-descriptions-item>
<el-descriptions-item label="工程师工号">{{ currentOrder.engineerId }}</el-descriptions-item>
<el-descriptions-item label="检测描述">{{ currentOrder.description }}</el-descriptions-item>
<el-descriptions-item label="检测照片" v-if="currentOrder.photos">
<div class="photos">
<el-image
v-for="(photo, index) in processPhotos(currentOrder.photos)"
:key="index"
:src="photo"
:preview-src-list="processPhotos(currentOrder.photos)"
fit="cover"
class="photo-item"
/>
</div>
</el-descriptions-item>
</el-descriptions>
</div>
</el-dialog>
<!-- 添加维修工单详情弹窗 -->
<el-dialog
v-model="repairDialog"
title="维修工单详情"
width="600px"
>
<div class="detail-content">
<div class="detail-item">
<span class="label">工单编号:</span>
<span>{{ currentOrder.orderId }}</span>
</div>
<div class="detail-item">
<span class="label">故障编号:</span>
<span>{{ currentOrder.faultId }}</span>
</div>
<div class="detail-item">
<span class="label">工程师工号:</span>
<span>{{ currentOrder.engineerId }}</span>
</div>
<div class="detail-item">
<span class="label">维修描述:</span>
<p>{{ currentOrder.description }}</p>
</div>
<div class="detail-item">
<span class="label">安全须知:</span>
<p>{{ currentOrder.safetyNotice }}</p>
</div>
<div class="detail-item">
<span class="label">维修照片:</span>
<div class="photos" v-if="currentOrder.photos">
<el-image
v-for="(photo, index) in processPhotos(currentOrder.photos)"
:key="index"
:src="photo"
:preview-src-list="processPhotos(currentOrder.photos)"
fit="cover"
class="photo-item"
/>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getAllOrders as getInspectionOrders } from '@/api/inspectionOrder'
import { getAllOrders as getMaintenanceOrders } from '@/api/maintenanceOrder'
import { getAllOrders as getTestingOrders } from '@/api/testingOrder'
import { getRepairOrderList } from '@/api/repairOrder'
import { updateOrderStatus as updateInspectionStatus } from '@/api/inspectionOrder'
import { updateOrderStatus as updateMaintenanceStatus } from '@/api/maintenanceOrder'
import { updateOrderStatus as updateTestingStatus } from '@/api/testingOrder'
import { updateRepairOrderStatus } from '@/api/repairOrder'
import { getFault } from '@/api/deviceFault'
import { getDevice } from '@/api/device'
import { getPlanById as getInspectionPlan } from '@/api/inspection'
import { getPlanById as getMaintenancePlan } from '@/api/maintenance'
import { getPlanById as getTestingPlan } from '@/api/testing'
import { getOrderById as getTestingOrderById } from '@/api/testingOrder'
// ref 声明
const loading = ref(false)
const orderList = ref([])
const currentType = ref('all')
const currentOrder = ref({})
const inspectionDialog = ref(false)
const maintenanceDialog = ref(false)
const testingDialog = ref(false)
const repairDialog = ref(false)
// 所有方法定义
const fetchOrders = async () => {
loading.value = true
try {
let orders = []
if (currentType.value === 'all' || currentType.value === 'inspection') {
const inspectionRes = await getInspectionOrders()
if (inspectionRes.code === 200) {
const inspectionOrders = await Promise.all(
inspectionRes.data
.filter(order => order.status === '待审批')
.map(async (order) => {
try {
// 获取巡检计划信息
const planRes = await getInspectionPlan(order.planId)
if (planRes.code === 200 && planRes.data) {
// 获取设备信息
const deviceRes = await getDevice(planRes.data.deviceId)
return {
...order,
orderType: 'inspection',
description: order.inspectionDesc,
deviceId: planRes.data.deviceId,
deviceType: deviceRes.data?.deviceType || '未知',
deviceLocation: deviceRes.data?.location || '未知'
}
}
return {
...order,
orderType: 'inspection',
description: order.inspectionDesc
}
} catch (error) {
console.error('获取巡检计划信息失败:', error)
return {
...order,
orderType: 'inspection',
description: order.inspectionDesc
}
}
})
)
orders = orders.concat(inspectionOrders)
}
}
if (currentType.value === 'all' || currentType.value === 'maintenance') {
const maintenanceRes = await getMaintenanceOrders()
if (maintenanceRes.code === 200) {
const maintenanceOrders = await Promise.all(
maintenanceRes.data
.filter(order => order.status === '待审批')
.map(async (order) => {
try {
// 获取保养计划信息
const planRes = await getMaintenancePlan(order.planId)
if (planRes.code === 200 && planRes.data) {
// 获取设备信息
const deviceRes = await getDevice(planRes.data.deviceId)
return {
...order,
orderType: 'maintenance',
description: order.maintenanceDesc,
deviceId: planRes.data.deviceId,
deviceType: deviceRes.data?.deviceType || '未知',
deviceLocation: deviceRes.data?.location || '未知'
}
}
return {
...order,
orderType: 'maintenance',
description: order.maintenanceDesc
}
} catch (error) {
console.error('获取保养计划信息失败:', error)
return {
...order,
orderType: 'maintenance',
description: order.maintenanceDesc
}
}
})
)
orders = orders.concat(maintenanceOrders)
}
}
// 修改 fetchOrders 函数中的检测工单处理部分
if (currentType.value === 'all' || currentType.value === 'testing') {
const testingRes = await getTestingOrders()
if (testingRes.code === 200) {
const testingOrders = await Promise.all(
testingRes.data
.filter(order => order.status === '待审批')
.map(async (order) => {
try {
// 获取检测工单详细信息
const orderDetailRes = await getTestingOrderById(order.orderId)
const planRes = await getTestingPlan(order.planId)
if (orderDetailRes.code === 200 && planRes.code === 200 && planRes.data) {
const deviceRes = await getDevice(planRes.data.deviceId)
return {
...order,
orderType: 'testing',
description: orderDetailRes.data.testingDesc,
photos: orderDetailRes.data.photos || '',
deviceId: planRes.data.deviceId,
deviceType: deviceRes.data?.deviceType || '未知',
deviceLocation: deviceRes.data?.location || '未知'
}
}
return {
...order,
orderType: 'testing',
description: order.testingDesc || '',
photos: ''
}
} catch (error) {
console.error('获取检测工单信息失败:', error)
return {
...order,
orderType: 'testing',
description: order.testingDesc || '',
photos: ''
}
}
})
)
orders = orders.concat(testingOrders)
}
}
// 维修工单部分保持不变,因为已经通过故障信息获取了设备ID
if (currentType.value === 'all' || currentType.value === 'repair') {
const repairRes = await getRepairOrderList()
if (repairRes.code === 200) {
const repairOrders = await Promise.all(
repairRes.data
.filter(order => order.status === '待审批')
.map(async (order) => {
try {
const faultRes = await getFault(order.faultId)
if (faultRes.code === 200) {
const deviceRes = await getDevice(faultRes.data.deviceId)
return {
...order,
orderType: 'repair',
deviceId: faultRes.data.deviceId,
description: order.repairDesc,
deviceType: deviceRes.data?.deviceType || '未知',
deviceLocation: deviceRes.data?.location || '未知'
}
}
return {
...order,
orderType: 'repair',
description: order.repairDesc
}
} catch (error) {
return {
...order,
orderType: 'repair',
description: order.repairDesc
}
}
})
)
orders = orders.concat(repairOrders)
}
}
orderList.value = orders
} catch (error) {
console.error('获取工单列表失败:', error)
ElMessage.error('获取工单列表失败')
} finally {
loading.value = false
}
}
// 处理审批通过
const handleApprove = async (order) => {
try {
let res
switch (order.orderType) {
case 'inspection':
res = await updateInspectionStatus(order.orderId, '已完成')
break
case 'maintenance':
res = await updateMaintenanceStatus(order.orderId, '已完成')
break
case 'testing':
res = await updateTestingStatus(order.orderId, '已完成')
break
case 'repair':
res = await updateRepairOrderStatus({
orderId: order.orderId,
status: '已完成'
})
break
}
if (res.code === 200) {
ElMessage.success('审批通过')
fetchOrders()
} else {
ElMessage.error(res.msg || '操作失败')
}
} catch (error) {
console.error('审批失败:', error)
ElMessage.error('审批失败')
}
}
// 处理审批驳回
const handleReject = async (order) => {
ElMessageBox.prompt('请输入驳回原因', '驳回工单', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /\S+/,
inputErrorMessage: '驳回原因不能为空'
}).then(async ({ value }) => {
try {
let res
switch (order.orderType) {
case 'inspection':
res = await updateInspectionStatus(order.orderId, '处理中')
break
case 'maintenance':
res = await updateMaintenanceStatus(order.orderId, '处理中')
break
case 'testing':
res = await updateTestingStatus(order.orderId, '处理中')
break
case 'repair':
res = await updateRepairOrderStatus({
orderId: order.orderId,
status: '处理中',
rejectReason: value
})
break
}
if (res.code === 200) {
ElMessage.success('已驳回')
fetchOrders()
} else {
ElMessage.error(res.msg || '操作失败')
}
} catch (error) {
console.error('驳回失败:', error)
ElMessage.error('驳回失败')
}
}).catch(() => {})
}
// 查看详情
const handleDetail = (order) => {
currentOrder.value = order
switch (order.orderType) {
case 'inspection':
inspectionDialog.value = true
break
case 'maintenance':
maintenanceDialog.value = true
break
case 'testing':
testingDialog.value = true
break
case 'repair':
repairDialog.value = true
break
}
}
const handleTypeChange = () => {
fetchOrders()
}
const getOrderTypeText = (type) => {
const typeMap = {
'inspection': '巡检工单',
'maintenance': '保养工单',
'testing': '检测工单',
'repair': '维修工单'
}
return typeMap[type] || type
}
const getStatusType = (status) => {
const statusMap = {
'待处理': 'warning',
'处理中': 'primary',
'已完成': 'success',
'待审批': 'warning'
}
return statusMap[status] || ''
}
const processPhotos = (photos) => {
if (!photos) return []
// 将逗号分隔的字符串转换为数组,并过滤掉空字符串
const photoArray = photos.split(',').filter(item => item.trim() !== '')
// 处理每张照片,确保格式正确并过滤无效数据
return photoArray
.map(photo => {
// 检查并处理 Base64 字符串
if (photo.startsWith('data:image')) {
return photo
}
return `data:image/jpeg;base64,${photo}`
})
.filter(photo => {
// 验证 Base64 数据的有效性
return photo && photo.length > 22 // 最小有效base64长度检查
})
}
// 生命周期钩子
onMounted(() => {
fetchOrders()
})
</script>
<style scoped>
.all-approve-container {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-right {
display: flex;
align-items: center;
}
.detail-content {
padding: 20px;
}
.detail-item {
margin-bottom: 15px;
}
.detail-item .label {
font-weight: bold;
margin-right: 10px;
}
.detail-item p {
margin: 10px 0;
white-space: pre-wrap;
}
.photos {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-top: 10px;
}
.photo-item {
width: 100px;
height: 100px;
border-radius: 4px;
cursor: pointer;
object-fit: cover;
}
</style>