<template>
|
<div>
|
<el-dialog title="巡检详情"
|
v-model="dialogVisitable" width="700px" @close="cancel">
|
<div class="detail-container">
|
<!-- 基本信息 -->
|
<div class="info-section">
|
<div class="section-title">基本信息</div>
|
<el-descriptions :column="2" border>
|
<el-descriptions-item label="设备名称">{{ rowData.taskName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="巡检地点">{{ rowData.inspectionLocation || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="执行巡检人">{{ rowData.inspector || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="巡检时间">{{ rowData.dateStr || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="巡检状态">
|
<el-tag v-if="rowData.inspectionStatus === 1" type="success" size="small">正常</el-tag>
|
<el-tag v-else-if="rowData.inspectionStatus === 2" type="danger" size="small">异常</el-tag>
|
<el-tag v-else size="small">未巡检</el-tag>
|
</el-descriptions-item>
|
<el-descriptions-item label="登记人">{{ rowData.registrant || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="验收状态">
|
<el-tag v-if="rowData.acceptStatus === 1" type="success" size="small">已通过</el-tag>
|
<el-tag v-else-if="rowData.acceptStatus === 2" type="danger" size="small">已退回</el-tag>
|
<el-tag v-else-if="rowData.inspectionStatus > 0" type="warning" size="small">待验收</el-tag>
|
<span v-else>--</span>
|
</el-descriptions-item>
|
<el-descriptions-item label="验收人">{{ rowData.inspectionAcceptor || '-' }}</el-descriptions-item>
|
</el-descriptions>
|
</div>
|
|
<!-- 异常描述 -->
|
<div v-if="rowData.inspectionStatus === 2" class="info-section">
|
<div class="section-title">异常描述</div>
|
<div class="exception-content">
|
{{ rowData.inspectionRemark || '无' }}
|
</div>
|
</div>
|
|
<!-- 附件列表 -->
|
<div class="info-section">
|
<div class="section-title">附件 ({{ attachmentList.length }}个)</div>
|
<div v-if="attachmentList.length > 0" class="attachment-list">
|
<div v-for="(file, index) in attachmentList" :key="index" class="attachment-item">
|
<div class="attachment-preview" @click="previewFile(file, index)">
|
<img v-if="isImage(file)" :src="getFileUrl(file)" alt="附件" />
|
<video v-else-if="isVideo(file)" :src="getFileUrl(file)"></video>
|
<div v-else class="file-icon">
|
<i class="el-icon-document"></i>
|
</div>
|
</div>
|
<div class="attachment-info">
|
<span class="file-name">{{ file.originalFilename || file.name || '附件' }}</span>
|
</div>
|
</div>
|
</div>
|
<div v-else class="empty-attachment">
|
暂无附件
|
</div>
|
</div>
|
</div>
|
|
<template #footer>
|
<el-button @click="cancel">关闭</el-button>
|
</template>
|
</el-dialog>
|
|
<!-- 图片预览 -->
|
<vue-easy-lightbox
|
v-if="showLightbox"
|
:visible="showLightbox"
|
:imgs="previewImages"
|
:index="previewIndex"
|
@hide="closeLightbox"
|
></vue-easy-lightbox>
|
|
<!-- 视频预览 -->
|
<el-dialog v-if="showVideoDialog" v-model="showVideoDialog" title="视频预览" width="800px" append-to-body>
|
<video
|
ref="videoPlayer"
|
:src="videoUrl"
|
controls
|
autoplay
|
style="width: 100%; max-height: 60vh;"
|
></video>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import { ref, getCurrentInstance } from 'vue';
|
import VueEasyLightbox from 'vue-easy-lightbox';
|
|
const { proxy } = getCurrentInstance();
|
|
// 控制弹窗显示
|
const dialogVisitable = ref(false);
|
|
// 行数据
|
const rowData = ref({});
|
|
// 附件列表
|
const attachmentList = ref([]);
|
|
// 图片预览
|
const showLightbox = ref(false);
|
const previewImages = ref([]);
|
const previewIndex = ref(0);
|
|
// 视频预览
|
const showVideoDialog = ref(false);
|
const videoUrl = ref('');
|
|
const javaApi = proxy.javaApi;
|
|
// 处理文件URL
|
function processFileUrl(fileUrl) {
|
if (!fileUrl) return '';
|
|
if (fileUrl && fileUrl.indexOf('\\') > -1) {
|
const uploadsIndex = fileUrl.toLowerCase().indexOf('uploads');
|
if (uploadsIndex > -1) {
|
const relativePath = fileUrl.substring(uploadsIndex).replace(/\\/g, '/');
|
fileUrl = '/' + relativePath;
|
} else {
|
const parts = fileUrl.split('\\');
|
const fileName = parts[parts.length - 1];
|
fileUrl = '/uploads/' + fileName;
|
}
|
}
|
|
if (fileUrl && !fileUrl.startsWith('http')) {
|
if (!fileUrl.startsWith('/')) {
|
fileUrl = '/' + fileUrl;
|
}
|
fileUrl = javaApi + fileUrl;
|
}
|
|
return fileUrl;
|
}
|
|
// 获取文件访问URL
|
function getFileUrl(file) {
|
// 优先使用 link 字段
|
let url = file?.link;
|
if (!url) {
|
url = file?.url || file?.downloadUrl || file?.tempPath || '';
|
}
|
return processFileUrl(url);
|
}
|
|
// 判断是否为图片
|
function isImage(file) {
|
const name = file?.originalFilename || file?.bucketFilename || file?.name || '';
|
const ext = name.split('.').pop()?.toLowerCase();
|
return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext);
|
}
|
|
// 判断是否为视频
|
function isVideo(file) {
|
const name = file?.originalFilename || file?.bucketFilename || file?.name || '';
|
const ext = name.split('.').pop()?.toLowerCase();
|
return ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm'].includes(ext);
|
}
|
|
// 预览文件
|
function previewFile(file, index) {
|
if (isImage(file)) {
|
// 图片预览
|
previewImages.value = attachmentList.value
|
.filter(f => isImage(f))
|
.map(f => getFileUrl(f));
|
previewIndex.value = previewImages.value.indexOf(getFileUrl(file));
|
showLightbox.value = true;
|
} else if (isVideo(file)) {
|
// 视频预览
|
videoUrl.value = getFileUrl(file);
|
showVideoDialog.value = true;
|
}
|
}
|
|
// 关闭图片预览
|
function closeLightbox() {
|
showLightbox.value = false;
|
previewImages.value = [];
|
previewIndex.value = 0;
|
}
|
|
// 打开弹窗
|
const openDialog = (row) => {
|
rowData.value = { ...row };
|
|
// 收集所有附件
|
let files = [];
|
if (row?.commonFileList) {
|
files = files.concat(row.commonFileList);
|
}
|
if (row?.commonFileListBefore) {
|
files = files.concat(row.commonFileListBefore);
|
}
|
if (row?.commonFileListAfter) {
|
files = files.concat(row.commonFileListAfter);
|
}
|
attachmentList.value = files;
|
|
dialogVisitable.value = true;
|
};
|
|
// 关闭弹窗
|
const cancel = () => {
|
dialogVisitable.value = false;
|
showVideoDialog.value = false;
|
rowData.value = {};
|
attachmentList.value = [];
|
};
|
|
defineExpose({ openDialog });
|
</script>
|
|
<style scoped lang="scss">
|
.detail-container {
|
max-height: 60vh;
|
overflow-y: auto;
|
}
|
|
.info-section {
|
margin-bottom: 20px;
|
|
&:last-child {
|
margin-bottom: 0;
|
}
|
}
|
|
.section-title {
|
font-size: 14px;
|
color: #165dff;
|
font-weight: 600;
|
padding-left: 10px;
|
position: relative;
|
margin-bottom: 12px;
|
|
&::before {
|
content: "";
|
position: absolute;
|
left: 0;
|
top: 3px;
|
width: 4px;
|
height: 14px;
|
background-color: #165dff;
|
}
|
}
|
|
.exception-content {
|
padding: 12px;
|
background-color: #fff2f0;
|
border: 1px solid #ffccc7;
|
border-radius: 4px;
|
color: #ff4d4f;
|
font-size: 14px;
|
line-height: 1.6;
|
}
|
|
.attachment-list {
|
display: flex;
|
flex-wrap: wrap;
|
gap: 12px;
|
}
|
|
.attachment-item {
|
width: 100px;
|
text-align: center;
|
}
|
|
.attachment-preview {
|
width: 100px;
|
height: 100px;
|
border: 1px solid #dcdfe6;
|
border-radius: 4px;
|
overflow: hidden;
|
cursor: pointer;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
background-color: #f5f7fa;
|
transition: all 0.3s;
|
|
&:hover {
|
border-color: #409eff;
|
transform: scale(1.02);
|
}
|
|
img {
|
width: 100%;
|
height: 100%;
|
object-fit: cover;
|
}
|
|
video {
|
width: 100%;
|
height: 100%;
|
object-fit: cover;
|
}
|
|
.file-icon {
|
font-size: 32px;
|
color: #909399;
|
}
|
}
|
|
.attachment-info {
|
margin-top: 4px;
|
|
.file-name {
|
font-size: 12px;
|
color: #606266;
|
display: block;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap;
|
}
|
}
|
|
.empty-attachment {
|
padding: 30px;
|
text-align: center;
|
color: #909399;
|
background-color: #f5f7fa;
|
border-radius: 4px;
|
}
|
</style>
|