This commit is contained in:
给我
2026-04-16 14:21:25 +08:00
parent f8e99b7c77
commit c3b2a20ad0
14 changed files with 1026 additions and 572 deletions

View File

@ -7,170 +7,173 @@
<div class="nav-placeholder"></div>
</div>
<!-- 预警信息卡片 -->
<div class="detail-card">
<!-- 头部信息 -->
<div class="card-header">
<div class="header-left">
<span class="level-tag" :class="alertDetail.levelClass">
{{ alertDetail.level }}
<!-- 加载状态 -->
<van-loading v-if="loading" class="loading-state" color="#2563eb">加载中...</van-loading>
<template v-else>
<!-- 预警信息卡片 -->
<div class="detail-card">
<!-- 头部信息 -->
<div class="card-header">
<div class="header-left">
<span class="level-tag" :class="getLevelClass(alertDetail.eventLevel)">
{{ levelMap[alertDetail.eventLevel] }}
</span>
<span class="alert-title">{{ alertDetail.eventType }}</span>
</div>
<span class="status-tag" :class="getStatusClass(allDetail.taskStatus)">
{{ statusMap[allDetail.taskStatus] }}
</span>
<span class="alert-title">{{ alertDetail.title }}</span>
</div>
<span class="status-tag" :class="alertDetail.statusClass">
{{ alertDetail.status }}
</span>
<!-- 图片 -->
<div class="card-image" v-if="alertDetail.image">
<van-image
:src="alertDetail.imgUrl"
fit="cover"
class="alert-img"
/>
</div>
<!-- 详细信息 -->
<div class="card-details">
<div class="detail-item">
<van-icon name="clock" class="detail-icon" />
<span class="label">检测时间</span>
<span class="value">{{ alertDetail.eventTime }}</span>
</div>
<div class="detail-item">
<van-icon name="location" class="detail-icon" />
<span class="label">检测地点</span>
<span class="value">{{ alertDetail.siteName }}</span>
</div>
<div class="detail-item" v-if="alertDetail.tasksVo">
<van-icon name="phone-o" class="detail-icon" />
<span class="label">联系电话</span>
<span class="value">{{ alertDetail.phoneNumber }}</span>
</div>
<!-- 视频展示按钮 -->
<div class="video-btn-wrapper" v-if="alertDetail.videoUrls && alertDetail.videoUrls.length > 0">
<van-button block round class="video-btn" @click="openVideo(alertDetail.videoUrls[0])">
<van-icon name="video-o" class="video-icon" />
视频展示
</van-button>
</div>
</div>
</div>
<!-- -->
<div class="card-image" v-if="alertDetail.image">
<van-image
:src="alertDetail.image"
fit="cover"
class="alert-img"
/>
</div>
<!-- 执行记录卡 -->
<div class="record-card">
<div class="card-title">执行记录</div>
<!-- 详细信息 -->
<div class="card-details">
<div class="detail-item">
<van-icon name="clock" class="detail-icon" />
<span class="label">检测时间</span>
<span class="value">{{ alertDetail.time }}</span>
</div>
<div class="detail-item">
<van-icon name="location" class="detail-icon" />
<span class="label">检测地点</span>
<span class="value">{{ alertDetail.location }}</span>
</div>
<!-- 蓝色边框卡片 -->
<div class="execution-box">
<!-- 绿色边框内层卡片 -->
<div class="info-box" v-if="taskDetail">
<div class="info-row">
<span class="info-label">任务地点</span>
<span class="info-value">{{ taskDetail.clickAddress }}</span>
</div>
<div class="info-row" v-if="taskDetail.deptName">
<span class="info-text">{{ taskDetail.deptName }}</span>
<span class="info-divider" v-if="taskDetail.phoneNumber">|</span>
<span class="info-text" v-if="taskDetail.phoneNumber">联系电话{{ taskDetail.phoneNumber }}</span>
</div>
</div>
<!-- 视频展示按钮 -->
<div class="video-btn-wrapper">
<van-button block round class="video-btn">
<van-icon name="video-o" class="video-icon" />
视频展示
</van-button>
<!-- 任务描述 -->
<div class="task-desc" v-if="taskDetail">
{{ taskDetail.taskContent }}
</div>
<!-- 打卡情况 -->
<div v-if="checkinRecords.length > 0" class="checkin-box">
<div v-for="(record, index) in checkinRecords" :key="index" class="checkin-row-wrapper">
<div class="checkin-row">
<span class="checkin-time">{{ record.date }}</span>
<span class="checkin-type">{{ record.type }}</span>
</div>
<div class="checkin-row">
<span class="checkin-label">打卡账号</span>
<span class="checkin-value">{{ record.account }}</span>
</div>
<div class="checkin-row checkin-location" v-if="record.location">
<van-icon name="location" class="location-icon" />
<span>{{ record.location }}</span>
</div>
</div>
</div>
<!-- 待派发状态 - 显示定位打卡按钮 -->
<div v-if="showCheckInBtn" class="action-buttons">
<van-button block round type="primary" class="action-btn" @click="handleCheckIn">
定位打卡
</van-button>
</div>
<!-- 执行中/已完成状态 - 显示反馈按钮 -->
<div v-if="showFeedbackBtns" class="feedback-buttons">
<van-button
v-for="(feedback, index) in allDetail.feebacks"
:key="index"
block
round
class="feedback-btn success"
@click="showFeedback(index + 1)"
>
{{ index+1 }}次反馈结果
</van-button>
<van-button
v-if="allDetail.taskStatus == '1'"
block
round
type="primary"
class="action-btn"
@click="handleFeedback"
>
立即反馈
</van-button>
</div>
</div>
</div>
</div>
<!-- 执行记录卡片 -->
<div class="record-card">
<div class="card-title">执行记录</div>
<!-- 蓝色边框卡片 -->
<div class="execution-box">
<!-- 绿色边框内层卡片 -->
<div class="info-box">
<div class="info-row">
<span class="info-label">执行时间</span>
<span class="info-value">03/29 09:30</span>
</div>
<div class="info-row">
<span class="info-text">交通指挥中心</span>
<span class="info-divider">|</span>
<span class="info-text">联系电话027-8888-8888</span>
</div>
<div class="info-row">
<span class="info-label">任务地点</span>
<span class="info-value">{{ alertDetail.location }}</span>
</div>
</div>
<!-- 任务描述 -->
<div class="task-desc">
协助疏导交通确保道路畅通维持现场秩序
</div>
<!-- 打卡情况 - 执行中和已完成状态显示 -->
<div v-if="status === '执行中' || status === '已完成'" class="checkin-box">
<div class="checkin-row">
<span class="checkin-date">11-16</span>
<span class="checkin-time">16:20:15</span>
<span class="checkin-type">签到打卡</span>
</div>
<div class="checkin-row">
<span class="checkin-label">打卡账号</span>
<span class="checkin-value">21515800</span>
</div>
<div class="checkin-row checkin-location">
<van-icon name="location" class="location-icon" />
<span>四川省德阳市旌阳区嘉明街道光兴街2号</span>
</div>
</div>
<!-- 按钮区域 -->
<!-- 已完成状态 - 显示三次反馈按钮 -->
<div v-if="status === '已完成'" class="feedback-buttons">
<van-button block round class="feedback-btn" @click="showFeedback(1)">
第一次反馈结果
</van-button>
<van-button block round class="feedback-btn" @click="showFeedback(2)">
第二次反馈结果
</van-button>
<van-button block round class="feedback-btn" @click="showFeedback(3)">
第三次反馈结果
</van-button>
</div>
<!-- 未执行状态 - 显示定位打卡按钮 -->
<div v-else-if="status === '未执行'" class="action-buttons">
<van-button block round type="primary" class="action-btn" @click="handleCheckIn">
定位打卡
</van-button>
</div>
<!-- 执行中状态 - 显示反馈按钮 -->
<div v-else-if="status === '执行中'" class="feedback-buttons">
<van-button block round class="feedback-btn success" @click="showFeedback(1)">
第一次反馈结果
</van-button>
<van-button block round class="feedback-btn success" @click="showFeedback(2)">
第二次反馈结果
</van-button>
<van-button block round type="primary" class="action-btn" @click="handleFeedback">
立即反馈
</van-button>
</div>
</div>
</div>
</template>
<!-- 反馈结果弹窗 -->
<van-popup v-model:show="showResultDialog" round position="center" class="feedback-popup">
<div class="popup-content">
<div class="popup-content" v-if="currentFeedback.title">
<h2 class="popup-title">{{ currentFeedback.title }}</h2>
<div class="popup-info">
<p>反馈时间{{ currentFeedback.time }}</p>
<p>反馈时间{{ currentFeedback.feedbackTime }}</p>
</div>
<!-- 现场照片 -->
<div class="popup-section">
<div class="popup-section" v-if="currentFeedback.images && currentFeedback.images.length > 0">
<p class="section-label">现场照片</p>
<div class="image-grid">
<van-image
v-for="(img, idx) in currentFeedback.images"
:key="idx"
@click.stop="ImagePreview([img])"
:src="img"
fit="cover"
class="feedback-img"
/>
</div>
</div>
<!-- 场地视频 -->
<div class="popup-section">
<!-- 现场照片 -->
<div class="popup-section" v-if="currentFeedback.video && currentFeedback.video.length > 0">
<p class="section-label">场地视频</p>
<div class="video-item">
<van-icon name="video-o" class="video-icon" />
<span>视频 1</span>
<div class="image-grid">
<span v-for="(img, idx) in currentFeedback.video"
:key="idx">视频{{ idx+1 }}</span>
</div>
</div>
<!-- 反馈内容 -->
<div class="popup-section">
<div class="popup-section" v-if="currentFeedback.feedback">
<h3 class="content-title">反馈内容</h3>
<p class="content-text">{{ currentFeedback.content }}</p>
<p class="content-text">{{ currentFeedback.feedback }}</p>
</div>
<!-- 关闭按钮 -->
@ -179,75 +182,141 @@
</van-button>
</div>
</van-popup>
<!-- 视频播放弹窗 -->
<van-popup v-model:show="showVideoDialog" round position="center" class="video-popup" :close-on-click-overlay="true">
<div class="video-content">
<video
v-if="currentVideoUrl"
:src="currentVideoUrl"
controls
autoplay
class="video-player"
/>
<p v-if="!currentVideoUrl" class="video-tip">暂无视频</p>
<van-icon name="cross" class="video-close" @click="showVideoDialog = false" />
</div>
</van-popup>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
import { ref, computed, onMounted } from "vue";
import { useRouter, useRoute } from "vue-router";
import { getTrafficEventDetail } from "@/api/traffic";
import { ImagePreview } from 'vant';
const router = useRouter();
const route = useRoute();
// 获取URL参数
const alertId = route.query.id || "1";
const status = route.query.status || "未执行";
const taskId = route.query.id || "";
const statusText = route.query.status || "";
// 加载状态
const loading = ref(false);
// 预警详情数据
const alertDetail = ref({});
const allDetail=ref({})
const taskDetail=ref({})
// 打卡记录
const checkinRecords = ref([]);
// 反馈数据
const feedbackList = ref([]);
// 反馈弹窗状态
const showResultDialog = ref(false);
const selectedFeedbackIndex = ref(1);
const currentFeedback = ref({});
// 预警详情数据
const alertDetail = ref({
id: alertId,
level: "一级",
levelClass: "level-red",
title: "交通事故",
status: status,
statusClass: computed(() => {
if (status === "执行中") return "status-blue";
if (status === "已完成") return "status-green";
return "status-orange";
}).value,
image: "https://images.unsplash.com/photo-1772962622823-77778ff7b192?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx0cmFmZmljJTIwYWNjaWRlbnQlMjBlbWVyZ2VuY3l8ZW58MXx8fHwxNzc0NjA1NzE3fDA&ixlib=rb-4.1.0&q=80&w=1080",
location: "解放大道光谷广场",
time: "03/29 08:00",
issuer: "110指挥中心",
phone: "027-110"
});
// 视频弹窗状态
const showVideoDialog = ref(false);
const currentVideoUrl = ref("");
// 反馈数据
const feedbackData = {
1: {
title: "第一次反馈结果",
time: "03/29 10:15",
content: "已到达现场,正在疏导交通。现场车流量较大,已设置临时路障引导车辆分流。",
images: [
"https://images.unsplash.com/photo-1449824913935-59a10b8d2000?w=800&q=80",
"https://images.unsplash.com/photo-1486006920555-c77dcf18193c?w=800&q=80"
]
},
2: {
title: "第二次反馈结果",
time: "03/29 10:45",
content: "交通疏导进行中拥堵情况有所缓解。已联系拖车处理事故车辆预计15分钟后完全恢复通行。",
images: [
"https://images.unsplash.com/photo-1485833077593-4278bba3f11f?w=800&q=80",
"https://images.unsplash.com/photo-1502444330042-d1a1ddf9bb5b?w=800&q=80"
]
},
3: {
title: "第三次反馈结果",
time: "03/29 11:10",
content: "任务已完成,交通已恢复正常。事故车辆已清离现场,临时路障已撤除,道路完全畅通。",
images: [
"https://images.unsplash.com/photo-1464037866556-6812c9d1c72e?w=800&q=80",
"https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=800&q=80"
]
}
// 状态文本映射
const statusMap = {
0: "待执行",
1: "执行中",
2: "已完成",
};
const currentFeedback = computed(() => feedbackData[selectedFeedbackIndex.value] || feedbackData[1]);
// 等级映射
const levelMap = {
0: "一级",
1: "二级",
2: "三级",
3: "四级"
};
// 等级样式
function getLevelClass(level) {
const map = {
1: "level-red",
2: "level-orange",
3: "level-blue"
};
return map[level] || "level-blue";
}
// 状态样式
function getStatusClass(status) {
const map = {
0: "status-gray",
1: "status-blue",
2: "status-green",
};
return map[status] || "status-gray";
}
// 时间格式化
function formatTime(timestamp) {
if (!timestamp) return "";
const date = new Date(timestamp);
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${month}/${day} ${hours}:${minutes}`;
}
// 获取详情数据
const fetchDetail = async () => {
if (!taskId) return;
loading.value = true;
try {
const res = await getTrafficEventDetail(taskId);
console.log(res, '详情res');
if (res) {
const data = res.data || res;
// 处理详情数据
alertDetail.value = data.eventDetailVO;
taskDetail.value = data.tasksVo;
allDetail.value = data
// 处理打卡记录
checkinRecords.value = (data.checkinRecords || data.signList || []).map(item => ({
date: formatTime(item.signTime || item.createTime),
type: item.type === 1 ? "【签到打卡】" : "【签退打卡】",
account: item.userName || item.account || "",
location: item.address || item.location || ""
}));
// 处理反馈列表
feedbackList.value = (data.feedbackList || data.reportList || []).map((item, index) => ({
title: `${index + 1}反馈结果`,
time: formatTime(item.reportTime || item.createTime),
content: item.reportContent || item.content || "",
images: item.imageUrls || item.images || []
}));
}
} catch (error) {
console.error("获取详情失败:", error);
} finally {
loading.value = false;
}
};
// 返回
function goBack() {
@ -258,7 +327,7 @@ function goBack() {
function handleCheckIn() {
router.push({
path: "/checkInPage",
query: { id: alertId }
query: { id: taskId }
});
}
@ -266,20 +335,51 @@ function handleCheckIn() {
function handleFeedback() {
router.push({
path: "/alert-handle",
query: { id: alertId, status: status }
query: { id: taskId, status: alertDetail.value.status }
});
}
// 显示反馈弹窗
function showFeedback(index) {
selectedFeedbackIndex.value = index;
showResultDialog.value = true;
if (allDetail.value.feebacks[index - 1]) {
currentFeedback.value = JSON.parse(JSON.stringify(allDetail.value.feebacks[index - 1]));
if(typeof currentFeedback.value.images=='string' && currentFeedback.value.images){
currentFeedback.value.images = currentFeedback.value.images.split(',')
}
if(typeof currentFeedback.value.video=='string' && currentFeedback.value.video){
currentFeedback.value.video = currentFeedback.value.video.split(',')
}
currentFeedback.value.title=`${index}次反馈结果`
showResultDialog.value = true;
}
}
//预览图片
function onClickImg(url) {
ImagePreview([url]);
}
// 打开视频弹窗
function openVideo(url) {
currentVideoUrl.value = url;
showVideoDialog.value = true;
}
// 关闭弹窗
function closePopup() {
showResultDialog.value = false;
}
// 判断按钮显示
const showCheckInBtn = computed(() => {
return allDetail.value.taskStatus == "0";
});
const showFeedbackBtns = computed(() => {
return allDetail.value.taskStatus == "1" || allDetail.value.taskStatus == "2";
});
// 页面初始化
onMounted(() => {
fetchDetail();
});
</script>
<style lang="scss" scoped>
@ -372,6 +472,11 @@ function closePopup() {
padding: 4px 12px;
border-radius: 20px;
font-size: 12px;
&.status-gray {
background: #f1f5f9;
color: #64748b;
border: 1px solid #e2e8f0;
}
&.status-orange {
background: #fff7ed;
@ -597,6 +702,7 @@ function closePopup() {
.popup-content {
padding: 24px;
width: 70vw;
}
.popup-title {
@ -680,4 +786,35 @@ function closePopup() {
background: #d1d5db;
}
}
.video-content {
position: relative;
width: 90vw;
.video-player {
width: 100%;
display: block;
max-height: 70vh;
}
.video-tip {
text-align: center;
color: #999;
padding: 40px 0;
margin: 0;
}
.video-close {
position: absolute;
top: 8px;
right: 8px;
font-size: 20px;
color: #fff;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
padding: 4px;
z-index: 1;
cursor: pointer;
}
}
</style>