bxztApp/packages/screen/src/views/RiskWarning/component/impactPointDetailDialog.vue

568 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="visible" class="impact-detail-dialog-overlay" @click="handleOverlayClick">
<div class="impact-detail-dialog" @click.stop>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">影响点详情</div>
<div class="close-btn" @click="handleClose">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 隐患点基本信息 -->
<div class="section">
<div class="section-title">
<span class="title-icon"></span>
隐患点基本信息
</div>
<div class="basic-info">
<div class="info-row">
<div class="info-item">
<span class="info-dot"></span>
<span class="info-label">所属区县</span>
<span class="info-value">{{ basicInfo.district }}</span>
</div>
<div class="info-item">
<span class="info-dot"></span>
<span class="info-label">影响点等级</span>
<span class="info-value level-tag" :class="basicInfo.levelClass">{{ basicInfo.level }}</span>
</div>
<div class="info-item">
<span class="info-dot"></span>
<span class="info-label">公路编号</span>
<span class="info-value">{{ basicInfo.roadCode }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="info-dot"></span>
<span class="info-label">位置</span>
<span class="info-value">{{ basicInfo.location }}</span>
</div>
<div class="info-item">
<span class="info-dot"></span>
<span class="info-label">风险点描述</span>
<span class="info-value" :class="getStatusClass(basicInfo.riskDesc)">{{ basicInfo.riskDesc }}</span>
</div>
<div class="info-item">
<span class="info-dot"></span>
<span class="info-label">发现时间</span>
<span class="info-value">{{ basicInfo.discoverTime }}</span>
</div>
</div>
</div>
</div>
<!-- 照片 -->
<div class="section">
<div class="section-title">
<span class="title-icon"></span>
照片
</div>
<div class="photo-list">
<div
v-for="(photo, index) in photoList"
:key="index"
class="photo-item"
@click="previewImage(photo)"
>
<img :src="photo" alt="照片" />
</div>
</div>
</div>
<!-- 填报动态信息 -->
<div class="section">
<div class="section-title">
<span class="title-icon"></span>
填报动态信息
</div>
<div class="timeline-list">
<div v-for="(record, index) in dynamicRecords" :key="index" class="timeline-item">
<div class="timeline-marker">{{ index + 1 }}</div>
<div class="timeline-content">
<div class="timeline-header">
<span class="timeline-type">{{ record.type }}</span>
</div>
<div class="timeline-detail">
<div class="detail-row">
<span class="detail-label">巡查时间</span>
<span class="detail-value">{{ record.patrolTime }}</span>
</div>
<div class="detail-row">
<span class="detail-label">巡查人</span>
<span class="detail-value">{{ record.patrolPerson }}</span>
</div>
<div class="detail-row">
<span class="detail-label">现场情况描述</span>
<span class="detail-value">{{ record.description }}</span>
<div v-if="record.image" class="detail-image" @click="previewImage(record.image)">
<img :src="record.image" alt="现场照片" />
</div>
</div>
<div class="detail-row">
<span class="detail-label">是否发现问题</span>
<span class="detail-value" :class="record.hasProblem ? 'status-yes' : 'status-no'">{{ record.hasProblem ? '是' : '否' }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 图片预览弹窗 -->
<div v-if="previewVisible" class="image-preview-overlay" @click="closePreview">
<div class="image-preview-container" @click.stop>
<img :src="previewImageUrl" alt="预览" />
<div class="close-preview-btn" @click="closePreview">
<el-icon><Close /></el-icon>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
pointData: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(["update:visible", "close"]);
// 基本信息
const basicInfo = ref({
district: "合川区",
level: "一般隐患",
levelClass: "level-normal",
roadCode: "G348",
location: "丁吴路(K116+656至K116+739)",
riskDesc: "未回应",
discoverTime: "立即启动防汛Ⅰ级应急响应,立即转移危险区群众,医疗机构做好应急准备",
});
// 照片列表
const photoList = ref([
"https://via.placeholder.com/120x80/40a9ff/ffffff?text=照片",
]);
// 动态记录
const dynamicRecords = ref([
{
type: "隐患",
patrolTime: "2026-03-28 14:30:00",
patrolPerson: "刘伟",
description: "设置警示标识,半幅通行",
hasProblem: true,
image: "https://via.placeholder.com/80x60/40a9ff/ffffff?text=现场",
},
{
type: "隐患",
patrolTime: "2026-03-28 14:30:00",
patrolPerson: "刘伟",
description: "设置警示标识,半幅通行",
hasProblem: false,
image: null,
},
]);
// 状态样式
const getStatusClass = (status) => {
if (status === "未回应") return "status-unresponse";
if (status === "已回应") return "status-response";
return "";
};
// 图片预览
const previewVisible = ref(false);
const previewImageUrl = ref("");
const previewImage = (url) => {
previewImageUrl.value = url;
previewVisible.value = true;
};
const closePreview = () => {
previewVisible.value = false;
previewImageUrl.value = "";
};
// 关闭对话框
const handleClose = () => {
emit("update:visible", false);
emit("close");
};
// 点击遮罩关闭
const handleOverlayClick = () => {
handleClose();
};
// 监听visible变化
watch(
() => props.visible,
(newVal) => {
if (newVal && props.pointData) {
Object.assign(basicInfo.value, props.pointData);
}
}
);
</script>
<style lang="scss" scoped>
.impact-detail-dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 2500;
}
.impact-detail-dialog {
width: 650px;
max-height: 90vh;
overflow-y: auto;
background: linear-gradient(135deg, rgba(20, 50, 90, 0.95) 0%, rgba(10, 30, 60, 0.98) 100%);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 12px;
padding: 24px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
}
// 标题栏
.dialog-header {
display: flex;
align-items: center;
justify-content: center;
position: relative;
margin-bottom: 24px;
.header-title {
font-size: 20px;
font-weight: 600;
color: #fff;
padding: 8px 40px;
background: linear-gradient(90deg, transparent 0%, rgba(64, 169, 255, 0.2) 20%, rgba(64, 169, 255, 0.2) 80%, transparent 100%);
border-bottom: 2px solid #40a9ff;
}
.close-btn {
position: absolute;
right: 0;
top: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;
font-size: 20px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
}
// 区块
.section {
margin-bottom: 20px;
&:last-child {
margin-bottom: 0;
}
}
// 区块标题
.section-title {
font-size: 15px;
font-weight: 600;
color: #fff;
margin-bottom: 12px;
display: flex;
align-items: center;
.title-icon {
color: #40a9ff;
margin-right: 6px;
}
}
// 基本信息
.basic-info {
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
padding: 16px 20px;
}
.info-row {
display: flex;
margin-bottom: 12px;
&:last-child {
margin-bottom: 0;
}
}
.info-item {
flex: 1;
display: flex;
align-items: flex-start;
gap: 6px;
}
.info-dot {
width: 6px;
height: 6px;
background-color: rgba(255, 255, 255, 0.4);
border-radius: 50%;
margin-top: 6px;
flex-shrink: 0;
}
.info-label {
font-size: 12px;
color: rgba(255, 255, 255, 0.5);
white-space: nowrap;
min-width: 70px;
}
.info-value {
font-size: 12px;
color: rgba(255, 255, 255, 0.9);
line-height: 1.4;
flex: 1;
&.level-tag {
display: inline-block;
padding: 2px 10px;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
width: fit-content;
&.level-normal {
background-color: rgba(250, 219, 95, 0.2);
color: #fadb5f;
border: 1px solid rgba(250, 219, 95, 0.4);
}
&.level-serious {
background-color: rgba(255, 77, 79, 0.2);
color: #ff4d4f;
border: 1px solid rgba(255, 77, 79, 0.4);
}
}
&.status-unresponse {
color: #ff7a45;
}
&.status-response {
color: #52c41a;
}
}
// 照片列表
.photo-list {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.photo-item {
width: 120px;
height: 80px;
border-radius: 6px;
overflow: hidden;
cursor: pointer;
border: 1px solid rgba(64, 169, 255, 0.3);
transition: all 0.3s;
&:hover {
border-color: #40a9ff;
transform: scale(1.05);
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
// 时间轴列表
.timeline-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.timeline-item {
display: flex;
gap: 12px;
position: relative;
}
.timeline-marker {
width: 24px;
height: 24px;
border-radius: 50%;
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 600;
color: #fff;
flex-shrink: 0;
margin-top: 2px;
}
.timeline-content {
flex: 1;
background-color: rgba(30, 70, 120, 0.3);
border-radius: 8px;
padding: 12px 16px;
}
.timeline-header {
margin-bottom: 10px;
}
.timeline-type {
font-size: 14px;
color: #40a9ff;
font-weight: 600;
}
// 详情内容
.timeline-detail {
display: flex;
flex-direction: column;
gap: 8px;
}
.detail-row {
display: flex;
align-items: flex-start;
gap: 8px;
}
.detail-label {
font-size: 12px;
color: rgba(255, 255, 255, 0.5);
white-space: nowrap;
min-width: 90px;
}
.detail-value {
font-size: 12px;
color: rgba(255, 255, 255, 0.85);
flex: 1;
line-height: 1.5;
&.status-yes {
color: #52c41a;
}
&.status-no {
color: #ff7a45;
}
}
.detail-image {
width: 80px;
height: 60px;
border-radius: 4px;
overflow: hidden;
cursor: pointer;
border: 1px solid rgba(64, 169, 255, 0.3);
margin-left: 12px;
flex-shrink: 0;
&:hover {
border-color: #40a9ff;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
}
}
// 图片预览
.image-preview-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.85);
display: flex;
align-items: center;
justify-content: center;
z-index: 2600;
}
.image-preview-container {
position: relative;
max-width: 80%;
max-height: 80%;
img {
max-width: 100%;
max-height: 80vh;
border-radius: 8px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}
}
.close-preview-btn {
position: absolute;
top: -40px;
right: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.8);
cursor: pointer;
font-size: 24px;
transition: color 0.3s;
&:hover {
color: #fff;
}
}
// 隐藏滚动条
.impact-detail-dialog {
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
</style>