<template>
|
<el-dialog
|
v-model="visible"
|
title="报修全过程"
|
width="820px"
|
destroy-on-close
|
@closed="onClosed"
|
>
|
<div v-loading="loading" class="repair-detail">
|
<template v-if="detail">
|
<div class="phase-block">
|
<div class="phase-title">1. 报修登记</div>
|
<el-descriptions :column="2" border size="small">
|
<el-descriptions-item label="设备名称">{{ detail.deviceName || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="规格型号">{{ detail.deviceModel || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="报修日期">{{ fmtDate(detail.repairTime) }}</el-descriptions-item>
|
<el-descriptions-item label="报修人">{{ detail.repairName || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="验收人">{{ detail.acceptanceName || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="维修人">{{ detail.maintenanceName || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="故障现象" :span="2">{{ detail.remark || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="当前状态" :span="2">
|
<el-tag v-if="detail.status === 0" type="warning" size="small">待维修</el-tag>
|
<el-tag v-else-if="detail.status === 3" type="info" size="small">待验收</el-tag>
|
<el-tag v-else-if="detail.status === 1" type="success" size="small">完成</el-tag>
|
<el-tag v-else-if="detail.status === 2" type="danger" size="small">维修失败</el-tag>
|
<span v-else>—</span>
|
</el-descriptions-item>
|
</el-descriptions>
|
<div class="img-row-label">设备问题图片</div>
|
<div v-if="problemImages.length" class="img-grid">
|
<el-image
|
v-for="img in problemImages"
|
:key="img.id"
|
:src="img.url"
|
fit="cover"
|
class="thumb"
|
:preview-src-list="problemImages.map((i) => i.url)"
|
preview-teleported
|
/>
|
</div>
|
<el-empty v-else description="暂无设备问题图片" :image-size="56" />
|
</div>
|
|
<div v-if="hasMaintenanceStep" class="phase-block">
|
<div class="phase-title">2. 维修处理</div>
|
<el-descriptions :column="2" border size="small">
|
<el-descriptions-item label="维修人">{{ detail.maintenanceName || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="维修时间">{{ fmtDateTime(detail.maintenanceTime) }}</el-descriptions-item>
|
<el-descriptions-item label="维修结果" :span="2">{{ detail.maintenanceResult || "—" }}</el-descriptions-item>
|
</el-descriptions>
|
<div v-if="maintainImages.length" class="img-row-label">维修完成图片</div>
|
<div v-if="maintainImages.length" class="img-grid">
|
<el-image
|
v-for="img in maintainImages"
|
:key="img.id"
|
:src="img.url"
|
fit="cover"
|
class="thumb"
|
:preview-src-list="maintainImages.map((i) => i.url)"
|
preview-teleported
|
/>
|
</div>
|
</div>
|
|
<div v-if="detail.status === 1" class="phase-block">
|
<div class="phase-title">3. 验收</div>
|
<el-descriptions :column="2" border size="small">
|
<el-descriptions-item label="验收人">{{ detail.acceptanceName || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="验收时间">{{ fmtDateTime(detail.acceptanceTime) }}</el-descriptions-item>
|
<el-descriptions-item label="验收备注" :span="2">{{ detail.acceptanceRemark || "—" }}</el-descriptions-item>
|
</el-descriptions>
|
</div>
|
</template>
|
</div>
|
<template #footer>
|
<el-button type="primary" @click="visible = false">关闭</el-button>
|
</template>
|
</el-dialog>
|
</template>
|
|
<script setup>
|
import { computed } from "vue";
|
import dayjs from "dayjs";
|
import { getRepairById, getRepairFileList } from "@/api/equipmentManagement/repair";
|
import { isProblemRepairFile, isMaintainRepairFile } from "@/api/equipmentManagement/repairFileType.js";
|
|
defineOptions({ name: "RepairDetailModal" });
|
|
const props = defineProps({
|
/** 与列表页、附件弹窗一致,用于拼静态资源完整地址(子组件无全局 proxy 时需由父页传入) */
|
javaApi: {
|
type: String,
|
default: "",
|
},
|
});
|
|
const visible = ref(false);
|
const loading = ref(false);
|
const detail = ref(null);
|
const problemImages = ref([]);
|
const maintainImages = ref([]);
|
|
const apiBase = computed(
|
() => props.javaApi || import.meta.env.VITE_APP_BASE_API || ""
|
);
|
|
const hasMaintenanceStep = computed(() => {
|
const s = detail.value?.status;
|
return s === 2 || s === 3 || s === 1;
|
});
|
|
const fmtDate = (v) => (v ? dayjs(v).format("YYYY-MM-DD") : "—");
|
const fmtDateTime = (v) => (v ? dayjs(v).format("YYYY-MM-DD HH:mm:ss") : "—");
|
|
/** 与设备报修 index.vue 中 getFileAccessUrl + normalizeFileUrl 逻辑一致 */
|
const buildFileUrl = (file = {}) => {
|
let raw = "";
|
if (file.link) {
|
if (String(file.link).startsWith("http")) return file.link;
|
raw = file.link;
|
} else {
|
raw = file.url || "";
|
}
|
if (!raw) return "";
|
if (String(raw).startsWith("http")) return raw;
|
let fileUrl = raw;
|
if (fileUrl && fileUrl.indexOf("\\") > -1) {
|
const lowerPath = fileUrl.toLowerCase();
|
const uploadPathIndex = lowerPath.indexOf("uploadpath");
|
if (uploadPathIndex > -1) {
|
fileUrl = fileUrl.substring(uploadPathIndex).replace(/\\/g, "/");
|
} else {
|
fileUrl = fileUrl.replace(/\\/g, "/");
|
}
|
}
|
fileUrl = fileUrl.replace(/^\/?uploadPath/, "/profile");
|
if (!fileUrl.startsWith("http")) {
|
if (!fileUrl.startsWith("/")) fileUrl = "/" + fileUrl;
|
fileUrl = apiBase.value + fileUrl;
|
}
|
return fileUrl;
|
};
|
|
const open = async (id) => {
|
if (!id) return;
|
visible.value = true;
|
loading.value = true;
|
detail.value = null;
|
problemImages.value = [];
|
maintainImages.value = [];
|
try {
|
const { data } = await getRepairById(id);
|
detail.value = data || null;
|
const fileRes = await getRepairFileList(id);
|
const list = Array.isArray(fileRes?.data) ? fileRes.data : [];
|
problemImages.value = list
|
.filter((f) => isProblemRepairFile(f.type))
|
.map((f) => ({ id: f.id, url: buildFileUrl(f) }));
|
maintainImages.value = list
|
.filter((f) => isMaintainRepairFile(f.type))
|
.map((f) => ({ id: f.id, url: buildFileUrl(f) }));
|
} finally {
|
loading.value = false;
|
}
|
};
|
|
const onClosed = () => {
|
detail.value = null;
|
problemImages.value = [];
|
maintainImages.value = [];
|
};
|
|
defineExpose({ open });
|
</script>
|
|
<style scoped>
|
.repair-detail {
|
min-height: 120px;
|
}
|
.phase-block {
|
margin-bottom: 20px;
|
}
|
.phase-title {
|
font-size: 15px;
|
font-weight: 600;
|
color: #303133;
|
margin-bottom: 10px;
|
padding-left: 8px;
|
border-left: 3px solid var(--el-color-primary);
|
}
|
.img-row-label {
|
font-size: 13px;
|
color: #606266;
|
margin: 14px 0 8px;
|
font-weight: 500;
|
}
|
.img-grid {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 8px;
|
}
|
.thumb {
|
width: 88px;
|
height: 88px;
|
border-radius: 6px;
|
border: 1px solid var(--el-border-color);
|
}
|
</style>
|