2025/4/28日团队开发进度报告
管理员审批功能:

RepairOrderController:
package com.example.demo.controller;
import com.example.demo.common.Result;
import com.example.demo.entity.RepairOrder;
import com.example.demo.service.RepairOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/repair-order")
public class RepairOrderController {
@Autowired
private RepairOrderService repairOrderService;
@PostMapping("/create")
public Result createRepairOrder(@RequestBody RepairOrder repairOrder) {
try {
RepairOrder createdOrder = repairOrderService.createRepairOrder(repairOrder);
return Result.success(createdOrder);
} catch (Exception e) {
return Result.fail("创建维修工单失败:" + e.getMessage());
}
}
@GetMapping("/engineer/{engineerId}")
public Result getOrdersByEngineerId(@PathVariable String engineerId) {
try {
List<RepairOrder> orders = repairOrderService.getOrdersByEngineerId(engineerId);
return Result.success(orders);
} catch (Exception e) {
return Result.fail("获取工程师维修工单失败:" + e.getMessage());
}
}
@GetMapping("/list")
public Result getRepairOrderList(@RequestParam(required = false) String engineerId) {
try {
List<RepairOrder> orders;
if (engineerId != null && !engineerId.isEmpty()) {
orders = repairOrderService.getOrdersByEngineerId(engineerId);
} else {
orders = repairOrderService.getAllOrders();
}
return Result.success(orders);
} catch (Exception e) {
return Result.fail("获取维修工单列表失败:" + e.getMessage());
}
}
@PutMapping("/update")
public Result updateRepairOrder(@RequestBody RepairOrder repairOrder) {
try {
boolean updated = repairOrderService.updateOrder(repairOrder);
if (updated) {
return Result.success(repairOrder);
} else {
return Result.fail("未找到对应的维修工单");
}
} catch (Exception e) {
return Result.fail("更新维修工单失败:" + e.getMessage());
}
}
@PutMapping("/status")
public Result updateRepairOrderStatus(@RequestBody Map<String, Object> params) {
try {
Integer orderId = (Integer) params.get("orderId");
String status = (String) params.get("status");
if (orderId == null || status == null) {
return Result.fail("参数不能为空");
}
boolean updated = repairOrderService.updateOrderStatus(orderId, status);
if (updated) {
return Result.success("状态更新成功");
} else {
return Result.fail("未找到对应的维修工单");
}
} catch (Exception e) {
return Result.fail("更新工单状态失败:" + e.getMessage());
}
}
}
repairOrder.js:
import request from '@/utils/request'
// 创建维修工单
export function createRepairOrder(data) {
return request({
url: '/repair-order/create',
method: 'post', // 确保使用 POST 方法
data
})
}
export function updateRepairOrderStatus(data) {
return request({
url: '/repair-order/status',
method: 'put',
data
})
}
// 更新维修工单
export function updateRepairOrder(data) {
return request({
url: '/repair-order/update',
method: 'put',
data
})
}
export function getRepairOrderList(params) {
return request({
url: '/repair-order/list',
method: 'get',
params // 直接传递参数对象
})
}
Approve.vue:
<template>
<div class="approve-container">
<el-card class="approve-card">
<template #header>
<div class="card-header">
<span>维修工单审批</span>
</div>
</template>
<el-table :data="repairOrders" border style="width: 100%" height="450">
<el-table-column prop="orderId" label="工单编号" width="100" />
<el-table-column prop="faultId" label="故障编号" width="100" />
<el-table-column prop="engineerId" label="工程师工号" width="120" />
<el-table-column prop="repairDesc" label="维修描述" min-width="200" show-overflow-tooltip />
<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="dialogVisible"
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.repairDesc }}</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"
@error="handleImageError"
/>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getRepairOrderList } from '@/api/repairOrder'
import { updateRepairOrderStatus } from '@/api/repairOrder'
import { updateFaultStatus, getFault } from '@/api/deviceFault'
import { updateDeviceStatus } from '@/api/device' // 添加这行
const repairOrders = ref([])
const dialogVisible = ref(false)
const currentOrder = ref({})
// 获取工单列表
const fetchRepairOrders = async () => {
try {
const res = await getRepairOrderList()
if (res.code === 200) {
// 只保留状态为"待审批"的工单
repairOrders.value = res.data.filter(order => order.status === '待审批')
} else {
ElMessage.error(res.msg || '获取工单列表失败')
}
} catch (error) {
console.error('获取工单列表失败:', error)
ElMessage.error('获取工单列表失败')
}
}
// 获取状态标签类型
const getStatusType = (status) => {
const statusMap = {
'待处理': 'info',
'处理中': 'warning',
'已完成': 'success',
'待审批': 'warning',
'已关闭': ''
}
return statusMap[status] || ''
}
// 处理审批通过
const handleApprove = async (order) => {
try {
// 更新工单状态
const res = await updateRepairOrderStatus({
orderId: order.orderId,
status: '已完成'
})
if (res.code === 200) {
// 更新故障状态
await updateFaultStatus({
faultId: order.faultId,
status: '已解决'
})
// 获取故障信息以获取设备ID
const faultRes = await getFault(order.faultId)
if (faultRes.code === 200) {
// 更新设备状态为正常
await updateDeviceStatus({
deviceId: faultRes.data.deviceId,
status: '正常'
})
}
ElMessage.success('审批通过')
fetchRepairOrders()
} 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 {
const res = await updateRepairOrderStatus({
orderId: order.orderId,
status: '处理中',
rejectReason: value
})
if (res.code === 200) {
ElMessage.success('已驳回')
fetchRepairOrders()
} else {
ElMessage.error(res.msg || '操作失败')
}
} catch (error) {
console.error('驳回失败:', error)
ElMessage.error('驳回失败')
}
}).catch(() => {})
}
// 查看详情
const handleDetail = (order) => {
currentOrder.value = order
dialogVisible.value = true
}
onMounted(() => {
fetchRepairOrders()
})
// 处理照片显示
const processPhotos = (photos) => {
if (!photos) return []
return photos.split(',').map(photo => {
try {
// 检查是否为有效的 Base64 字符串
if (photo.length % 4 !== 0) {
console.warn('Invalid Base64 length')
return ''
}
// 如果已经是 data:image 格式,直接返回
if (photo.startsWith('data:image')) {
return photo
}
// 尝试检测图片类型(简单判断,可根据实际需求扩展)
let imageType = 'png'
if (photo.startsWith('/9j/')) {
imageType = 'jpeg'
} else if (photo.startsWith('R0lGOD')) {
imageType = 'gif'
}
// 添加适当的 MIME 类型前缀
return `data:image/${imageType};base64,${photo}`
} catch (error) {
console.error('处理图片数据失败:', error)
return ''
}
}).filter(url => url) // 过滤掉无效的图片URL
}
</script>
<style scoped>
.approve-container {
padding: 20px;
}
.card-header {
display: flex;
justify-content: space-between;
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;
}
.photo-error {
border: 1px solid #f56c6c;
display: flex;
align-items: center;
justify-content: center;
color: #f56c6c;
font-size: 12px;
}
</style>

浙公网安备 33010602011771号