<template>
|
<view class="repair-detail">
|
<PageHeader title="设备报修详情"
|
@back="goBack" />
|
<view v-if="detail"
|
class="detail-content">
|
<!-- 1. 报修登记 -->
|
<view class="section">
|
<view class="section-title">
|
<text class="section-num">1</text>
|
<text>报修登记</text>
|
</view>
|
<view class="info-grid">
|
<view class="info-item">
|
<text class="info-label">设备名称</text>
|
<text class="info-value">{{ detail.deviceName || '-' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="info-label">规格型号</text>
|
<text class="info-value">{{ detail.deviceModel || '-' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="info-label">报修日期</text>
|
<text class="info-value">{{ formatDate(detail.repairTime) || '-' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="info-label">报修人</text>
|
<text class="info-value">{{ detail.repairName || detail.maintenanceName || '-' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="info-label">验收人</text>
|
<text class="info-value">{{ detail.acceptanceName || '-' }}</text>
|
</view>
|
<view class="info-item"
|
v-if="Number(detail.status) !== 0">
|
<text class="info-label">维修人</text>
|
<text class="info-value">{{ detail.maintenancePerson || detail.maintenanceName || '-' }}</text>
|
</view>
|
<view class="info-item full">
|
<text class="info-label">故障现象</text>
|
<text class="info-value">{{ detail.remark || '-' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="info-label">当前状态</text>
|
<view class="info-value">
|
<u-tag :type="getStatusTagType(detail.status)"
|
size="small">{{ getStatusText(detail.status) }}</u-tag>
|
</view>
|
</view>
|
</view>
|
<view class="image-section">
|
<text class="image-title">设备问题图片</text>
|
<view v-if="repairImageList.length"
|
class="image-list">
|
<image v-for="(file, index) in repairImageList"
|
:key="file.id || index"
|
:src="getFileAccessUrl(file)"
|
mode="aspectFill"
|
class="repair-image"
|
@click="previewImage(index)" />
|
</view>
|
<view v-else
|
class="no-image">
|
<up-icon name="photo"
|
size="40"
|
color="#c0c4cc" />
|
<text>暂无设备问题图片</text>
|
</view>
|
</view>
|
</view>
|
<!-- 2. 维修处理 -->
|
<view class="section"
|
v-if="showMaintenanceSection">
|
<view class="section-title">
|
<text class="section-num">2</text>
|
<text>维修处理</text>
|
</view>
|
<view class="info-grid">
|
<view class="info-item">
|
<text class="info-label">维修人</text>
|
<text class="info-value">{{ detail.maintenancePerson || detail.maintenanceName || '-' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="info-label">维修时间</text>
|
<text class="info-value">{{ formatDateTime(detail.maintenanceTime) || '-' }}</text>
|
</view>
|
<view class="info-item full">
|
<text class="info-label">维修结果</text>
|
<text class="info-value">{{ detail.maintenanceResult || '-' }}</text>
|
</view>
|
</view>
|
</view>
|
<!-- 3. 验收 -->
|
<view class="section"
|
v-if="showAcceptanceSection">
|
<view class="section-title">
|
<text class="section-num">3</text>
|
<text>验收</text>
|
</view>
|
<view class="info-grid">
|
<view class="info-item">
|
<text class="info-label">验收人</text>
|
<text class="info-value">{{ detail.acceptanceName || '-' }}</text>
|
</view>
|
<view class="info-item">
|
<text class="info-label">验收时间</text>
|
<text class="info-value">{{ formatDateTime(detail.acceptanceTime) || '-' }}</text>
|
</view>
|
<view class="info-item full">
|
<text class="info-label">验收备注</text>
|
<text class="info-value">{{ detail.acceptanceRemark || '-' }}</text>
|
</view>
|
</view>
|
</view>
|
</view>
|
<view v-else
|
class="loading-wrap">
|
<text>加载中...</text>
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, computed, onMounted } from "vue";
|
import { onLoad } from "@dcloudio/uni-app";
|
import PageHeader from "@/components/PageHeader.vue";
|
import config from "@/config";
|
import {
|
getRepairById,
|
getRepairFileList,
|
} from "@/api/equipmentManagement/repair";
|
import dayjs from "dayjs";
|
|
defineOptions({ name: "设备报修详情" });
|
|
const repairId = ref("");
|
const detail = ref(null);
|
const repairImageList = ref([]);
|
|
const STATUS_MAP = {
|
0: "待维修",
|
3: "待验收",
|
1: "完成",
|
2: "维修失败",
|
};
|
|
const getStatusText = status => STATUS_MAP[Number(status)] || "-";
|
|
const getStatusTagType = status => {
|
const map = { 0: "error", 3: "warning", 1: "success", 2: "error" };
|
return map[Number(status)] || "info";
|
};
|
|
const showMaintenanceSection = computed(() => Number(detail.value?.status) !== 0);
|
|
const showAcceptanceSection = computed(() => Number(detail.value?.status) === 1);
|
|
const formatDate = dateStr => {
|
if (!dateStr) return "";
|
return dayjs(dateStr).format("YYYY-MM-DD");
|
};
|
|
const formatDateTime = dateStr => {
|
if (!dateStr) return "";
|
return dayjs(dateStr).format("YYYY-MM-DD HH:mm:ss");
|
};
|
|
const normalizeFileUrl = (rawUrl = "") => {
|
let fileUrl = rawUrl || "";
|
const javaApi = config.baseUrl;
|
const localPrefixes = ["wxfile://", "file://", "content://", "blob:", "data:"];
|
|
if (localPrefixes.some(prefix => fileUrl.startsWith(prefix))) {
|
return fileUrl;
|
}
|
|
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 && !fileUrl.startsWith("http")) {
|
if (!fileUrl.startsWith("/")) fileUrl = "/" + fileUrl;
|
fileUrl = javaApi + fileUrl;
|
}
|
return fileUrl;
|
};
|
|
const getFileAccessUrl = (file = {}) => {
|
if (file?.link) {
|
if (String(file.link).startsWith("http")) return file.link;
|
return normalizeFileUrl(file.link);
|
}
|
return normalizeFileUrl(file?.url || "");
|
};
|
|
const previewImage = index => {
|
const urls = repairImageList.value
|
.map(item => getFileAccessUrl(item))
|
.filter(Boolean);
|
if (!urls.length) return;
|
uni.previewImage({ urls, current: urls[index] || urls[0] });
|
};
|
|
const loadDetail = async () => {
|
if (!repairId.value) return;
|
try {
|
uni.showLoading({ title: "加载中...", mask: true });
|
const { code, data } = await getRepairById(repairId.value);
|
if (code === 200) {
|
detail.value = data;
|
} else {
|
uni.showToast({ title: "获取详情失败", icon: "none" });
|
}
|
const fileRes = await getRepairFileList(repairId.value);
|
if (fileRes?.code === 200) {
|
repairImageList.value = Array.isArray(fileRes.data) ? fileRes.data : [];
|
}
|
} catch (e) {
|
uni.showToast({ title: "获取详情失败", icon: "none" });
|
} finally {
|
uni.hideLoading();
|
}
|
};
|
|
const goBack = () => {
|
uni.navigateBack();
|
};
|
|
onLoad(options => {
|
repairId.value = options?.id || uni.getStorageSync("repairId") || "";
|
});
|
|
onMounted(() => {
|
loadDetail();
|
});
|
</script>
|
|
<style scoped lang="scss">
|
.repair-detail {
|
min-height: 100vh;
|
background: #f5f6f8;
|
padding-bottom: 24px;
|
}
|
|
.detail-content {
|
padding: 12px 16px;
|
}
|
|
.section {
|
background: #fff;
|
border-radius: 12px;
|
margin-bottom: 16px;
|
overflow: hidden;
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
}
|
|
.section-title {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
padding: 14px 16px;
|
font-size: 16px;
|
font-weight: 600;
|
color: #303133;
|
border-bottom: 1px solid #f0f0f0;
|
}
|
|
.section-num {
|
width: 22px;
|
height: 22px;
|
line-height: 22px;
|
text-align: center;
|
border-radius: 50%;
|
background: #2c7be5;
|
color: #fff;
|
font-size: 12px;
|
font-weight: 600;
|
}
|
|
.info-grid {
|
padding: 8px 16px 12px;
|
display: flex;
|
flex-wrap: wrap;
|
}
|
|
.info-item {
|
width: 50%;
|
padding: 10px 8px 10px 0;
|
box-sizing: border-box;
|
display: flex;
|
flex-direction: column;
|
gap: 4px;
|
|
&.full {
|
width: 100%;
|
}
|
}
|
|
.info-label {
|
font-size: 13px;
|
color: #909399;
|
}
|
|
.info-value {
|
font-size: 14px;
|
color: #303133;
|
word-break: break-all;
|
}
|
|
.image-section {
|
padding: 0 16px 16px;
|
border-top: 1px solid #f5f5f5;
|
margin-top: 4px;
|
padding-top: 12px;
|
}
|
|
.image-title {
|
font-size: 13px;
|
color: #909399;
|
display: block;
|
margin-bottom: 10px;
|
}
|
|
.image-list {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 10px;
|
}
|
|
.repair-image {
|
width: 80px;
|
height: 80px;
|
border-radius: 6px;
|
background: #f5f5f5;
|
}
|
|
.no-image {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
padding: 24px;
|
color: #c0c4cc;
|
font-size: 13px;
|
gap: 8px;
|
background: #fafafa;
|
border-radius: 8px;
|
}
|
|
.loading-wrap {
|
padding: 40px;
|
text-align: center;
|
color: #909399;
|
}
|
</style>
|