bxztApp/packages/screen/src/views/RiskWarning/Dialog/aiWarningResultDialog.vue

955 lines
28 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>
<base-dialog
v-model:visible="props.visible"
title="AI预警处理结果"
:table-data="[]"
:table-columns="[]"
:table-height="0"
:total="0"
:current-page="1"
:page-size="10"
:z-index="2200"
:max-width="1200"
:tableShow="false"
@close="handleClose"
>
<!-- 标题栏下方自定义插槽 -->
<template #header>
<!-- 内容区域 -->
<div class="content-wrapper">
<!-- 左侧气象预警信息 -->
<div class="panel left-panel">
<div class="panel-title">气象预警信息</div>
<div class="panel-content">
<div class="warning-content-container">{{ warningData.warningContent }}</div>
</div>
</div>
<!-- 中间AI层级结构 -->
<div class="ai-hierarchy">
<!-- AI图标 -->
<div class="ai-icon-wrapper">
<img class="ai-icon-img" src="../../../assets/RiskWarning_img/AI.png" alt="AI" />
<div class="level-arrow">
<el-icon><DArrowRight /></el-icon>
</div>
</div>
<!-- 层级结构主体 -->
<div class="hierarchy-main">
<!-- 左侧层级节点 -->
<div class="hierarchy-left">
<div v-for="(level, index) in hierarchyLevels" :key="level.key" class="level-item">
<!-- 层级节点 -->
<div
class="level-node"
:class="[level.key, { active: activeLevel === level.key }]"
@click="activeLevel = level.key"
>
<img :src="level.icon" class="level-icon" alt="" />
<span class="level-name">{{ level.name }}</span>
</div>
<div class="level-arrow">
<el-icon><DArrowRight /></el-icon>
</div>
</div>
</div>
</div>
<!-- 右侧选项卡 -->
<div class="hierarchy-right">
<!-- 市级分组 -->
<div class="tab-group">
<div
v-for="tab in hierarchyTabs"
:key="tab.key"
class="hierarchy-tab"
:class="{ active: activeHierarchyTab === tab.key }"
@click="
activeHierarchyTab = tab.key;
if (tab.key == 'city-dept') {
activeImpactTab = 'point';
} else {
activeImpactTab = 'aiiType';
}
"
>
{{ tab.label }}
</div>
</div>
<!-- 区县级分组 -->
<div class="tab-group">
<div
v-for="tab in districtTabsMap"
:key="tab.key"
class="hierarchy-tab"
:class="{ active: activeHierarchyTab === tab.key }"
@click="
activeHierarchyTab = tab.key;
activeImpactTab = 'point';
"
>
{{ tab.label }}
</div>
</div>
<!-- 一线分组 -->
<div class="tab-group">
<div
v-for="tab in frontlineTabsMap"
:key="tab.key"
class="hierarchy-tab"
:class="{ active: activeHierarchyTab === tab.key }"
@click="
activeHierarchyTab = tab.key;
if (tab.key == 'frontline-whistle' || tab.key == 'frontline-guard') {
activeImpactTab = 'project';
}
"
>
{{ tab.label }}
</div>
</div>
</div>
</div>
<!-- 右侧AI处理后预警 -->
<div class="panel right-panel">
<div class="panel-title">AI处理后预警</div>
<div class="panel-content">
<div class="warning-content-container">
<!-- 预警信息标签页 -->
<div class="section warning-info-section">
<div class="section-label">预警信息</div>
<div class="tab-content">
<div class="info-text">{{ currentWarningInfo.content }}</div>
</div>
</div>
<!-- 工作建议 -->
<div class="section suggestion-section">
<div class="section-label">工作建议</div>
<div class="suggestion-content" v-if="'city-leader' == activeHierarchyTab">
{{ currentWarningInfo.leader }}
</div>
<div class="suggestion-content" v-else-if="'city-dept' == activeHierarchyTab">
{{ currentWarningInfo.dept }}
</div>
<div class="suggestion-content" v-else-if="'city-emergency' == activeHierarchyTab">
{{ currentWarningInfo.emergency }}
</div>
<div class="suggestion-content" v-else-if="'district-leader' == activeHierarchyTab">
{{ currentWarningInfo.leader }}
</div>
<div class="suggestion-content" v-else-if="'district-person' == activeHierarchyTab">
{{ currentWarningInfo.person }}
</div>
<div class="suggestion-content" v-else-if="'frontline-guard' == activeHierarchyTab">
{{ currentWarningInfo.guard }}
</div>
<div
class="suggestion-content"
v-else-if="'frontline-whistle' == activeHierarchyTab"
>
{{ currentWarningInfo.whistle }}
</div>
<div
class="suggestion-content"
v-else-if="'frontline-contractor' == activeHierarchyTab"
>
{{ currentWarningInfo.contractor }}
</div>
<div
class="suggestion-content"
v-else-if="'frontline-person' == activeHierarchyTab"
>
{{ currentWarningInfo.person }}
</div>
</div>
<!-- 影响范围 -->
<div
class="section impact-section"
v-if="
'city-leader' == activeHierarchyTab ||
'city-dept' == activeHierarchyTab ||
'city-emergency' == activeHierarchyTab ||
'district-leader' == activeHierarchyTab ||
'district-person' == activeHierarchyTab ||
'frontline-whistle' == activeHierarchyTab ||
'frontline-contractor' == activeHierarchyTab
"
>
<div class="display jc_sb ai_center">
<div class="section-label">影响范围</div>
<div
class="impact-tabs"
v-if="
'district-leader' == activeHierarchyTab ||
'district-person' == activeHierarchyTab
"
>
<div
v-for="tab in impactTabs"
:key="tab.key"
class="impact-tab-item"
:class="{ active: activeImpactTab === tab.key }"
@click="activeImpactTab = tab.key"
>
{{ tab.label }}
</div>
</div>
</div>
</div>
<!-- 影响范围表格 -->
<div
class="impact-table-wrapper"
v-if="
'city-leader' == activeHierarchyTab ||
'city-dept' == activeHierarchyTab ||
'city-emergency' == activeHierarchyTab ||
'district-leader' == activeHierarchyTab ||
'district-person' == activeHierarchyTab ||
'frontline-whistle' == activeHierarchyTab ||
'frontline-contractor' == activeHierarchyTab
"
>
<el-table
v-if="activeImpactTab === 'aiiType'"
:data="aiiTypeArr"
style="background: transparent; width: 368px"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
size="small"
height="150"
>
<el-table-column prop="label" label="类型" width="" align="center" />
<el-table-column prop="number" label="数量" width="" align="center" />
</el-table>
<el-table
v-if="activeImpactTab === 'point'"
:data="currentImpactData"
style="background: transparent"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
size="small"
height="150"
>
<el-table-column type="index" label="序号" width="" align="center" />
<el-table-column prop="area" label="影响区域" width="" align="center" />
<el-table-column prop="roadCode" label="线路编号" width="" align="center" />
<el-table-column prop="type" label="类型" width="" align="center" />
<el-table-column
prop="stake"
label="桩号"
width=""
align="center"
show-overflow-tooltip
/>
</el-table>
<el-table
v-if="activeImpactTab === 'project'"
:data="impactProjectDataTable"
style="background: transparent"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
size="small"
height="150"
>
<el-table-column type="index" label="序号" width="" align="center" />
<el-table-column prop="area" label="影响区域" width="" align="center" />
<el-table-column prop="projectName" label="项目名称" width="70" align="center" />
<el-table-column
prop="projectAddress"
label="项目地址"
width=""
align="center"
/>
<el-table-column
prop="lineNumber"
label="线路编号"
width=""
align="center"
show-overflow-tooltip
/>
</el-table>
</div>
</div>
</div>
</div>
</div>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { DArrowRight, Folder } from '@element-plus/icons-vue';
import baseDialog from '../component/baseDialog.vue';
import cityIcon from '../../../assets/RiskWarning_img/市级icon.png';
import districtIcon from '../../../assets/RiskWarning_img/区县icon.png';
import frontlineIcon from '../../../assets/RiskWarning_img/一线icon.png';
import AIIcon from '../../../assets/RiskWarning_img/AI.png';
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
aiData: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(['update:visible', 'close']);
// ==================== 数据定义 ====================
// 气象预警信息数据
const warningData = ref({
publishOrg: '',
warningContent:
'黔江区气象台2026年4月16日01时00分发布“暴雨红色预警信号”过去1小时双槐雨量已达90毫米预计6日1:00-7:00濯水镇街强降水仍将持续未来6小时累计雨量将达90160毫米最大小时雨强将达80100毫米局地伴有雷电、阵性大风地质灾害、中小河流洪水、山洪、城乡积涝等灾害风险高请注意防范。',
effectiveTime: '',
expireTime: '',
});
// 层级结构数据
const hierarchyLevels = ref([
{
key: 'city',
name: '市级',
icon: cityIcon,
groupCount: 71,
percent: 7.31,
},
{
key: 'district',
name: '区县级',
icon: districtIcon,
percent: 2.03,
},
{
key: 'frontline',
name: '一线',
icon: frontlineIcon,
},
]);
// 右侧选项卡数据
const hierarchyTabs = ref([
{ key: 'city-leader', label: '中心领导', parentKey: 'city' },
{ key: 'city-dept', label: '处室负责人', parentKey: 'city' },
{ key: 'city-emergency', label: '中心应急办', parentKey: 'city' },
]);
const districtTabsMap = ref([
{ key: 'district-leader', label: '公路交通部门领导', parentKey: 'district' },
{ key: 'district-person', label: '养护站长/路长办', parentKey: 'district' },
]);
const frontlineTabsMap = ref([
{ key: 'frontline-guard', label: '护路员', parentKey: 'frontline' },
{ key: 'frontline-whistle', label: '建设单位包保责任人', parentKey: 'frontline' },
{ key: 'frontline-contractor', label: '施工单位包保责任人', parentKey: 'frontline' },
{ key: 'frontline-person', label: '吹哨人', parentKey: 'frontline' },
]);
const activeHierarchyTab = ref('city-leader');
const activeLevel = ref('city');
// 预警信息标签页
const warningTabs = ref([
{
key: 'leader',
label: '公路中心领导',
content:
'黔江区气象台2026年4月16日01时00分发布"暴雨红色预警信号"过去1小时双槐雨量已达90毫米预计6日1:00-7:00濯水镇街强降水仍将持续未来6小时累计雨量将达90~160毫米最大小时雨强将达80~100毫米局地伴有雷电、阵性大风地质灾害、中小河流洪水、山洪、城乡积涝等灾害风险高请注意防范。',
leader:
'按照相关指南启动I级防御响应请领导在岗值班主持召开或参加部署调度会及时跟踪、研究、部署灾害防范和应急保通工作安排开展督导帮扶。',
dept: '按照相关指南启动I级防御响应请处室负责人和相关人员在岗值班参加部署调度会分析研判影响区县、重点路段和项目部署本领域调度核查工作督促本领域落实5个关口防御应对措施参加工作组开展指导帮扶及时收集分析、研究部署本领域防范应对、受灾和抢险救援保通工作。',
emergency:
'按照相关指南启动I级防御响应请相关人员在岗值班准备并参加部署调度会开展预警叫应组织分析研判影响区县、重点路段和项目等风险要素部署市级公路应急中心全员做好应急抢险准备及时跟踪分析、研究部署防范应对、受灾和抢险救援保通工作。',
leader:
'按照相关指南,请根据暴雨预警级别启动相应等级防御响应,视情召开部署调度会,请及时关注地质、水文等风险提示信息,做好会商研判,严格值班值守,落实预警叫应、部署调度、督导帮扶、力量预置、巡查排危、告警阻拦、交通管控和“关停撤转”等措施,及时跟踪分析、研究部署防范应对、受灾和抢险救援保通工作。',
person:
'按照相关指南,请根据暴雨预警级别启动相应等级防御响应,视情召开部署调度会,请及时关注地质、水文等风险提示信息,做好会商研判,严格值班值守,落实预警叫应、部署调度、督导帮扶、力量预置、巡查排危、告警阻拦、交通管控和“关停撤转”等措施,及时跟踪分析、研究部署防范应对、受灾和抢险救援保通工作。',
guard:
'按照相关要求启动I级防御响应并请及时关注地质、水文等风险提示信息落实主动封闭管控措施请立即按照2小时一次频率对你管养的重点路段进行巡查重点巡查较高及以上风险路段、涉灾隐患点、地质条件复杂路段、临河临崖路段重点关注涉水桥梁基础及墩台、不良地质隧道、隧道洞口边仰坡及侧切结构、高陡边坡支挡防护以及防排水设施发现异常情况立即向上报告采取紧急排危、告警阻拦、交通管控等措施并及时报送工作开展情况。',
whistle:
'按照相关指南,请根据暴雨预警级别启动相应等级防御响应,请及时关注地质、水文等风险提示信息,做好会商研判,严格值班值守,落实预警叫应、部署调度、督导帮扶、力量预置、巡查排危和“关停撤转”等措施,及时跟踪分析、研究部署防范应对、受灾和抢险救援保通工作。',
contractor:
'按照相关指南,请根据暴雨预警级别启动相应等级防御响应,请及时关注地质、水文等风险提示信息,做好会商研判,严格值班值守,落实预警叫应、部署调度、督导帮扶、力量预置、巡查排危和“关停撤转”等措施,及时跟踪分析、研究部署防范应对、受灾和抢险救援保通工作。',
},
{ key: 'dept', label: '处室负责人', content: '', suggestion: '' },
{ key: 'emergency', label: '中心应急办', content: '', suggestion: '' },
]);
const activeWarningTab = ref('leader');
// 当前选中的预警信息
const currentWarningInfo = computed(() => {
const tab = warningTabs.value.find(t => t.key === activeWarningTab.value);
return tab || warningTabs.value[0];
});
// 影响范围标签页
const impactTabs = ref([
{ key: 'point', label: '影响点' },
{ key: 'project', label: '影响驻地' },
]);
const activeImpactTab = ref('project');
// 影响点数据
const impactPointData = ref([]);
// 影响项目数据
const impactProjectData = ref([]);
const aiiTypeArr = ref([
{ number: '2', label: '影响区县' },
{ number: '21', label: '风险路段' },
{ number: '21', label: '桥梁' },
{ number: '21', label: '隧道' },
{ number: '21', label: '驻地' },
]);
// 影响项目
const impactProjectDataTable = ref([
{
index: '1',
area: '巫溪区',
projectName: '',
projectAddress: '',
lineNumber: 'G242',
},
{
index: '2',
area: '万州区',
projectName: '',
projectAddress: '',
lineNumber: 'G318',
},
]);
// 当前影响范围数据
const currentImpactData = computed(() => {
return [
{
index: '1',
area: '巫溪区',
roadCode: 'G242',
type: '桥梁',
stake: '336.800-336.850',
},
{
index: '2',
area: '万州区',
roadCode: 'G318',
type: '隧道',
stake: '336.850-336.900',
},
];
});
// 表格样式
const headerCellStyle = () => ({
background: '#2373B8',
color: '#18f2f9',
fontSize: '12px',
fontWeight: 'bold',
borderBottom: '1px solid rgba(24, 242, 249, 0.2)',
});
const cellStyle = () => ({
background: '#1C659E',
color: '#fff',
fontSize: '12px',
borderBottom: '1px solid #175E91',
textAlign: 'center',
});
// ==================== 方法 ====================
// 关闭对话框
const handleClose = () => {
emit('update:visible', false);
emit('close');
};
// 初始化数据
const initData = data => {
if (!data) return;
// 气象预警信息
if (data.warning) {
warningData.value = {
publishOrg: data.warning.publishOrg || '',
publishContent: data.warning.publishContent || '',
effectiveTime: data.warning.effectiveTime || '',
expireTime: data.warning.expireTime || '',
};
}
// 层级结构
if (data.hierarchyLevels) {
hierarchyLevels.value = data.hierarchyLevels;
}
// 右侧选项卡
if (data.hierarchyTabs) {
hierarchyTabs.value = data.hierarchyTabs;
}
if (data.districtTabsMap) {
districtTabsMap.value = data.districtTabsMap;
}
if (data.frontlineTabsMap) {
frontlineTabsMap.value = data.frontlineTabsMap;
}
if (data.activeHierarchyTab) {
activeHierarchyTab.value = data.activeHierarchyTab;
}
if (data.activeLevel) {
activeLevel.value = data.activeLevel;
}
// 预警信息标签页
if (data.warningTabs) {
warningTabs.value = data.warningTabs;
}
// 影响范围数据
if (data.impactPointData) {
impactPointData.value = data.impactPointData;
}
if (data.impactProjectData) {
impactProjectData.value = data.impactProjectData;
}
};
// ==================== 监听 ====================
watch(
() => props.visible,
newVal => {
if (newVal && props.aiData) {
initData(props.aiData);
}
},
{ immediate: true }
);
</script>
<style lang="scss" scoped>
// 内容区域
.content-wrapper {
display: flex;
gap: 10px;
}
// 面板
.panel {
// flex: 1;
display: flex;
flex-direction: column;
.panel-title {
font-size: 16px;
font-weight: 600;
color: #18f2f9;
text-align: center;
padding: 12px 0;
background-image: url('../../../assets/RiskWarning_img/矩形@2x.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.panel-content {
background: url('../../../assets/RiskWarning_img/AI背景@2x.png');
background-size: 100% 100%;
background-position: center;
background-repeat: no-repeat;
padding: 30px;
overflow-y: auto;
color: #fff;
font-size: 12px;
.warning-content-container {
overflow-y: auto;
height: 360px;
// 自定义滚动条样式
-ms-overflow-style: none; /* IE/Edge */
scrollbar-width: none; /* Firefox */
}
}
}
// 左侧面板
.left-panel {
.info-container {
height: 100%;
overflow-y: auto;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: rgba(24, 242, 249, 0.3);
border-radius: 2px;
}
}
.info-item {
margin-bottom: 16px;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
font-size: 13px;
color: rgba(255, 255, 255, 0.6);
margin-bottom: 4px;
display: block;
}
.info-value {
font-size: 13px;
color: rgba(255, 255, 255, 0.9);
line-height: 1.6;
&.content-text {
text-align: justify;
display: block;
}
}
}
// 中间AI层级结构
.ai-hierarchy {
width: 500px;
display: flex;
// flex-direction: column;
align-items: center;
justify-content: center;
// gap: 20px;
.ai-icon-wrapper {
display: flex;
align-items: center;
justify-content: center;
// margin-top: -40px;
gap: 8px;
.ai-icon-img {
width: 80px;
height: 80px;
}
.level-arrow {
color: #18f2f9;
font-size: 24px;
margin-top: 8px;
:deep(.el-icon) {
font-size: 24px;
}
}
}
.hierarchy-main {
display: flex;
gap: 20px;
align-items: center;
margin-left: 20px;
// margin-top: -40px;
margin-right: 10px;
.hierarchy-left {
flex: 1;
display: flex;
flex-direction: column;
gap: 80px;
.level-item {
display: flex;
justify-content: center;
align-items: center;
position: relative;
gap: 10px;
.group-tag {
position: absolute;
left: -10px;
top: -20px;
display: flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
background: #409eff;
border-radius: 3px;
font-size: 11px;
color: #fff;
:deep(.el-icon) {
font-size: 12px;
}
}
.level-node {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 5px 10px;
border: 1px solid rgba(24, 242, 249, 0.3);
background: rgba(55, 155, 255, 0.08);
border-radius: 4px;
min-width: 110px;
.level-icon {
width: 28px;
height: 28px;
}
.level-name {
font-size: 14px;
color: #18f2f9;
font-weight: 500;
}
&.active {
background: #2a75bf !important;
border-color: #18f2f9 !important;
transform: scale(1.05);
}
&:hover {
cursor: pointer;
opacity: 0.9;
}
}
.percent-tag {
position: absolute;
right: -60px;
top: 50%;
transform: translateY(-50%);
padding: 4px 10px;
background: #ff4d4f;
border-radius: 4px;
font-size: 12px;
color: #fff;
font-weight: 600;
&::before {
content: '';
position: absolute;
left: -6px;
top: 50%;
transform: translateY(-50%);
border: 6px solid transparent;
border-right-color: #ff4d4f;
}
}
.level-arrow {
color: #18f2f9;
font-size: 24px;
margin-top: 8px;
:deep(.el-icon) {
font-size: 24px;
}
}
}
}
}
}
.hierarchy-right {
width: 180px;
display: flex;
flex-direction: column;
gap: 12px;
.tab-group {
display: flex;
flex-direction: column;
padding: 12px;
background-size: 100% 100%;
background-position: center;
border-radius: 4px;
border: 2px solid rgba(55, 155, 255, 0.31);
.hierarchy-tab {
padding: 10px 12px;
text-align: center;
font-size: 14px;
background-image: url('@/assets/RiskWarning_img/矩形@2x.png');
background-repeat: no-repeat;
background-size: cover;
background-position: center;
color: #fff;
cursor: pointer;
&.active {
color: #18f2f9;
}
}
}
}
// 右侧面板
.right-panel {
.panel-content {
display: flex;
flex-direction: column;
gap: 12px;
padding: 30px;
width: 400px;
}
.section {
.section-label {
flex: 1;
font-size: 13px;
padding: 5px 5px;
font-weight: 500;
background-image: url('@/assets/RiskWarning_img/矩形@2x.png');
background-repeat: no-repeat;
background-size: contain;
background-position: -5% 0%;
}
}
// 预警信息标签页
.warning-info-section {
.tab-header {
display: flex;
gap: 8px;
margin-bottom: 8px;
flex-wrap: wrap;
.tab-item {
padding: 4px 10px;
font-size: 11px;
color: rgba(255, 255, 255, 0.8);
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 3px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: rgba(24, 242, 249, 0.1);
border-color: rgba(24, 242, 249, 0.3);
}
&.active {
background: #18f2f9;
border-color: #18f2f9;
color: #0a1a2a;
font-weight: 500;
}
}
}
.tab-content {
.info-text {
font-size: 12px;
color: rgba(255, 255, 255, 0.85);
line-height: 1.6;
}
}
}
// 工作建议
.suggestion-section {
.suggestion-content {
font-size: 12px;
color: rgba(255, 255, 255, 0.85);
line-height: 1.6;
}
}
// 影响范围
.impact-section {
flex: 1;
display: flex;
flex-direction: column;
.impact-tabs {
display: flex;
// gap: 8px;
border-radius: 4px;
margin-bottom: 8px;
overflow: hidden;
.impact-tab-item {
padding: 6px 16px;
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
background: linear-gradient(
180deg,
rgba(102, 105, 255, 0.26) 0%,
rgba(31, 201, 255, 0.23) 100%
);
cursor: pointer;
transition: all 0.3s;
&:hover {
background: rgba(24, 242, 249, 0.2);
}
&.active {
background: #18f2f9;
border-color: #18f2f9;
color: #0a1a2a;
font-weight: 500;
}
}
}
.impact-table-wrapper {
overflow: hidden;
:deep(.el-table) {
background: transparent;
&::before {
display: none;
}
.el-table__body-wrapper {
background: transparent;
}
}
}
}
}
// 滚动条样式
::-webkit-scrollbar {
width: 4px;
height: 4px;
}
::-webkit-scrollbar-thumb {
background: rgba(24, 242, 249, 0.3);
border-radius: 2px;
}
::-webkit-scrollbar-track {
background: transparent;
}
:deep(.el-table--small .cell) {
padding: 0px;
}
:deep(.el-table .cell) {
text-align: center;
}
</style>