三拍:审批高级检索

由于一开始使用的是查询四个表的所有结果后进行过滤,因此在着使用过滤的方法不使用sql的模糊查询

<template>
  <div class="all-approve-container">
    <!-- 保持原有的卡片部分不变 -->
    <el-card class="approve-card">
      <template #header>
        <div class="card-header">
          <span>工单审批</span>
          <div class="header-right">
            <el-input
              v-model="filters.engineerId"
              placeholder="工程师工号"
              clearable
              style="width: 120px; margin-right: 15px;"
              @clear="handleFilter"
              @change="handleFilter"
            />
            <el-input
              v-model="filters.deviceId"
              placeholder="设备ID"
              clearable
              style="width: 120px; margin-right: 15px;"
              @clear="handleFilter"
              @change="handleFilter"
            />
            <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 filters = ref({
  engineerId: '',
  deviceId: ''
})

// 修改 fetchOrders 函数末尾的部分
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 之前应用过滤器
    orderList.value = filterOrders(orders)
  } catch (error) {
    console.error('获取工单列表失败:', error)
    ElMessage.error('获取工单列表失败')
  } finally {
    loading.value = false
  }
}

// 修改过滤方法
const filterOrders = (orders) => {
  if (!orders) return []
  return orders.filter(order => {
    const engineerIdMatch = !filters.value.engineerId || 
      (order.engineerId && order.engineerId.toString().toLowerCase().includes(filters.value.engineerId.toLowerCase()))
    const deviceIdMatch = !filters.value.deviceId || 
      (order.deviceId && order.deviceId.toString().toLowerCase().includes(filters.value.deviceId.toLowerCase()))
    return engineerIdMatch && deviceIdMatch
  })
}

// 添加过滤处理函数
const handleFilter = () => {
  // 直接对当前的 orderList 进行过滤,避免重新获取数据
  orderList.value = filterOrders(orderList.value)
}

// 修改类型切换处理函数
const handleTypeChange = () => {
  // 类型改变时需要重新获取数据
  fetchOrders()
}

// 处理审批通过
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 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>
posted @ 2025-05-19 11:07  QixunQiu  阅读(16)  评论(0)    收藏  举报