996 lines
24 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 class="left-panel">
<!-- 智能研判头部 -->
<SectionHeader>
<template #left>
<div class="filter-header">
<span>智能研判</span>
<img
class="filter-icon-ai"
src="../../assets/RiskWarning_img/AI1@2x.png"
alt=""
@click="handleAIClick"
/>
</div>
</template>
</SectionHeader>
<!-- 气象预警 -->
<div class="weather-warning-section">
<div class="section-title">气象预警</div>
<div class="warning-cards">
<div
v-for="(item, index) in weatherWarningData"
:key="index"
class="warning-card"
:class="item.class"
@click="handleWarningCardClick(item)"
>
<img
class="card-icon"
src="../../assets/RiskWarning_img/气象预警图标@2x.png"
alt=""
/>
<div class="card-info">
<div class="card-num mt_10">{{ item.value }}</div>
<div class="card-label mb_10">{{ item.label }}</div>
</div>
</div>
</div>
</div>
<!-- 影响点概况 -->
<div class="impact-section">
<div class="impact-header">
<div class="impact-title">影响点概况</div>
<div class="impact-detail clickable" @click="handleImpactDetailClick">
一键清单(影响点)
</div>
</div>
<div class="chart-container">
<div class="chart-y-label">数量</div>
<div class="bar-chart f1">
<div
v-for="(item, index) in impactData"
:key="index"
class="bar-item"
>
<div
class="bar"
:style="{ height: getBarHeight(item.value) + '%' }"
></div>
<div class="bar-value">{{ item.value }}</div>
<div class="bar-label">{{ item.label }}</div>
</div>
</div>
<div class="chart-x-label">类型</div>
</div>
</div>
<!-- 影响公路类型情况 -->
<div class="road-type-section">
<div class="section-title">影响公路类型情况</div>
<div class="road-type-cards">
<div class="road-card">
<span class="card-label">国省道</span>
<span class="card-value">100</span>
</div>
<div class="road-card">
<span class="card-label">农村公路</span>
<span class="card-value">300</span>
</div>
</div>
</div>
<!-- 区县统计表格 -->
<div class="district-table-section">
<el-table
:data="districtData"
style="width: 100%; background: transparent; font-size: 12px"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
size="small"
:height="tableHeight"
:scroll="{ y: true }"
>
<el-table-column prop="name" label="区县名称" :min-width="vw(80)" />
<el-table-column prop="road" label="路段" :min-width="vw(50)" />
<el-table-column prop="bridge" label="桥梁" :min-width="vw(50)" />
<el-table-column prop="tunnel" label="隧道" :min-width="vw(50)" />
<el-table-column prop="slope" label="边坡" :min-width="vw(50)" />
<el-table-column prop="project" label="项目" :min-width="vw(50)" />
</el-table>
</div>
<!-- 响应调度 -->
<div class="response-section">
<SectionHeader title="响应调度">
<template #right>
<div class="header-filters">
<span class="filter-item active">本轮</span>
<div class="date-range-wrapper">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
size="small"
popper-class="dark-date-picker"
:prefix-icon="Calendar"
/>
</div>
</div>
</template>
</SectionHeader>
<!-- 6个统计项 -->
<div class="stats-grid">
<div
v-for="(item, index) in responseStats"
:key="index"
class="stat-item"
:class="{
clickable:
item.label === '叫应总数' ||
item.label === '已回应数' ||
item.label === '调度区县数',
}"
@click="handleStatClick(item)"
>
<!-- <div class="stat-icon" :class="item.iconClass"></div> -->
<img class="stat-icon" :src="item.img" alt="" />
<div class="stat-info">
<div class="stat-num">{{ item.value }}</div>
<div class="stat-label">{{ item.label }}</div>
</div>
</div>
</div>
<!-- 3个调度清单卡片 -->
<div class="dispatch-cards">
<div
v-for="(item, index) in dispatchList"
:key="index"
class="dispatch-card"
:class="{
clickable:
item.label === '国省道调度清单' ||
item.label === '农村公路调度清单' ||
item.label === '建设工程调度清单',
}"
@click="handleDispatchCardClick(item)"
>
<div class="card-num">
{{ item.value }}<span class="unit"></span>
</div>
<div class="card-label">{{ item.label }}</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed } from "vue";
import SectionHeader from "./component/sectionHeader.vue";
const emit = defineEmits([
"openWarningInfo",
"openImpactPoint",
"openWarningSituation",
"openResponseStatus",
"openDispatchDistrict",
"openImpactDetail",
"showCenterCard",
]);
// 点击统计项
const handleStatClick = (item) => {
if (item.label === "叫应总数") {
emit("openWarningInfo");
} else if (item.label === "已回应数") {
emit("openResponseStatus");
} else if (item.label === "调度区县数") {
emit("openDispatchDistrict");
}
};
// 点击气象预警卡片
const handleWarningCardClick = (item) => {
emit("openWarningSituation", item);
};
// 点击影响点明细
const handleImpactDetailClick = () => {
emit("openImpactDetail");
};
// 点击调度清单卡片
const handleDispatchCardClick = (item) => {
if (item.label === "建设工程调度清单") {
emit("showCenterCard", {
type: "first",
value: item.value,
});
} else if (item.label === "国省道调度清单") {
emit("showCenterCard", {
type: "second",
value: item.value,
});
} else if (item.label === "农村公路调度清单") {
emit("showCenterCard", {
type: "third",
value: item.value,
});
}
};
// 导入图片资源
import imgCall from "../../assets/RiskWarning_img/回应icon@2x.png";
import imgReply from "../../assets/RiskWarning_img/已回应icon@2x.png";
import imgRate from "../../assets/RiskWarning_img/回应率icon@2x.png";
import imgDistrict from "../../assets/RiskWarning_img/区县icon@2x.png";
import imgHelp from "../../assets/RiskWarning_img/响应图标5@2x.png";
import imgCheck from "../../assets/RiskWarning_img/抽查人数icon@2x.png";
// 气象预警数据
const weatherWarningData = [
{ label: "红色预警", value: '8/13', class: "red" },
{ label: "橙色预警", value: '12/14', class: "orange" },
{ label: "黄色预警", value: '27/15', class: "yellow" },
{ label: "蓝色预警", value: '15/15', class: "blue" },
];
// 影响点数据
const impactData = [
{ label: "路段", value: 830 },
{ label: "桥梁", value: 312 },
{ label: "隧道", value: 405 },
{ label: "边坡", value: 634 },
{ label: "项目", value: 523 },
];
// 日期范围选择器
const dateRange = ref([]);
// 计算最大值用于动态计算高度
const maxValue = Math.max(...impactData.map((item) => item.value));
// 计算每个柱子的高度百分比
const getBarHeight = (value) => {
// 确保最小高度为20%最大高度为100%
const height = (value / maxValue) * 100;
return Math.min(100, Math.max(20, Math.round(height)));
};
const handleAIClick = () => {
emit("openAIResult");
};
// 响应式单位转换函数基于1920px设计稿
const vw = (px) => {
return Math.round((px / 1920) * window.innerWidth);
};
// 表格高度计算
const tableHeight = computed(() => {
return vw(100);
});
// 区县数据
const districtData = [
{ name: "江北区", road: 1, bridge: 1, tunnel: 1, slope: 8, project: 11 },
{ name: "江北区", road: 1, bridge: 1, tunnel: 1, slope: 8, project: 11 },
{ name: "南岸区", road: 1, bridge: 2, tunnel: 2, slope: 6, project: 12 },
{ name: "九龙坡区", road: 2, bridge: 1, tunnel: 1, slope: 9, project: 9 },
{ name: "九龙坡区", road: 2, bridge: 1, tunnel: 1, slope: 9, project: 9 },
{ name: "万州区", road: 1, bridge: 2, tunnel: 2, slope: 11, project: 7 },
];
// 响应调度统计数据
const responseStats = [
{
label: "叫应总数",
value: "15",
iconClass: "icon-call",
img: imgCall,
},
{
label: "已回应数",
value: "9",
iconClass: "icon-reply",
img: imgReply,
},
{
label: "回应率",
value: "100%",
iconClass: "icon-rate",
img: imgRate,
},
{
label: "调度区县数",
value: "21",
iconClass: "icon-district",
img: imgDistrict,
},
{
label: "线下帮扶数",
value: "12",
iconClass: "icon-help",
img: imgHelp,
},
{
label: "抽查人次",
value: "23",
iconClass: "icon-check",
img: imgCheck,
},
];
// 调度清单数据
const dispatchList = [
{ label: "国省道调度清单", value: "341" },
{ label: "农村公路调度清单", value: "210" },
{ label: "建设工程调度清单", value: "120" },
];
const headerCellStyle = () => ({
background: "rgba(64, 169, 255, 0.1)",
color: "rgba(255, 255, 255, 0.9)",
fontWeight: "normal",
borderBottom: "1px solid rgba(64, 169, 255, 0.2)",
padding: " 2px",
textAlign: "center",
lineHeight: "1.2",
});
const cellStyle = () => ({
background: "transparent",
color: "rgba(255, 255, 255, 0.8)",
borderBottom: "1px solid rgba(64, 169, 255, 0.1)",
padding: " 2px",
textAlign: "center",
lineHeight: "1.2",
});
</script>
<style lang="scss" scoped>
.filter-header {
margin-left: 35px;
display: flex;
align-items: center;
gap: vw(8);
color: #fff;
font-size: vw(24);
font-weight: bold;
}
.filter-icon-ai {
width: 30px;
height: 30px;
margin-left: 10px;
}
// 视频屏幕自适应 - 基于视口宽度动态调整
// 基准宽度 1920px使用 vw 单位实现自适应
@function vw($px) {
@return calc($px / 1920 * 100vw);
}
.left-panel {
width: 100%;
height: 100%;
padding: vw(20);
box-sizing: border-box;
overflow-y: auto;
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
// 小屏幕适配
@media (max-width: 1366px) {
padding: 12px;
}
@media (max-width: 1024px) {
padding: 8px;
}
.section-header {
height: vw(50);
min-height: 40px;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: vw(20);
background-image: url("../../assets/RiskWarning_img/标题bg@2x.png");
background-size: cover;
background-position: left;
.header-left {
display: flex;
align-items: center;
gap: vw(8);
margin-left: vw(35);
color: #fff;
font-size: vw(24);
font-weight: bold;
.icon-back {
width: vw(20);
height: vw(20);
min-width: 16px;
min-height: 16px;
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
&::before {
content: "←";
color: #fff;
font-size: vw(12);
}
}
.title {
font-size: vw(12);
font-weight: bold;
color: #fff;
}
}
.header-date {
font-size: vw(11);
color: rgba(255, 255, 255, 0.6);
}
}
// 气象预警
.weather-warning-section {
margin-bottom: vw(20);
.section-title {
font-size: vw(16);
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
margin-bottom: vw(12);
}
.warning-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: vw(10);
display: flex;
.warning-card {
display: flex;
align-items: center;
gap: vw(10);
flex: 1;
// padding: 12px;
background: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 6px;
.card-icon {
width: 100%;
max-width: vw(35);
height: auto;
display: flex;
align-items: center;
justify-content: center;
font-size: vw(24);
// &::before {
// content: "⛈️";
// }
}
.card-info {
flex: 1;
.card-num {
font-size: vw(20);
font-weight: bold;
margin-bottom: 2px;
}
.card-label {
font-size: vw(12);
color: rgba(255, 255, 255, 0.7);
}
}
&.red .card-num {
color: #ff4d4f;
}
&.orange .card-num {
color: #ff7a45;
}
&.yellow .card-num {
color: #ffc53d;
}
&.blue .card-num {
color: #40a9ff;
}
}
}
}
// 影响点概况
.impact-section {
margin-bottom: vw(40);
.impact-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: vw(20);
.impact-title {
font-size: vw(16);
color: rgba(255, 255, 255, 0.9);
}
.impact-detail {
font-size: vw(14);
color: #40a9ff;
cursor: pointer;
&.clickable {
transition: all 0.3s;
&:hover {
color: #69c0ff;
text-shadow: 0 0 8px rgba(105, 192, 255, 0.6);
}
}
}
}
.chart-container {
position: relative;
height: vw(120);
height: 70px;
display: flex;
// padding: 10px 0 30px 40px;
.chart-y-label {
position: absolute;
left: 0;
top: vw(-10);
font-size: vw(11);
color: rgba(255, 255, 255, 0.6);
}
.bar-chart {
display: flex;
justify-content: space-around;
align-items: flex-end;
height: 100%;
border-bottom: 1px solid rgba(64, 169, 255, 0.3);
position: relative;
// 背景网格线
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image:
linear-gradient(to right, transparent 0%, transparent 100%),
linear-gradient(
to bottom,
rgba(64, 169, 255, 0.1) 1px,
transparent 1px
);
background-size: 100% 25%;
pointer-events: none;
}
.bar-item {
height: 100%;
display: flex;
flex-direction: column-reverse;
align-items: center;
flex: 1;
cursor: pointer;
transition: all 0.3s ease;
padding: vw(5) 0;
border-radius: 4px;
&:hover {
background: rgba(64, 169, 255, 0.15);
.bar-value {
color: #40a9ff;
transform: scale(1.1);
}
.bar {
background: linear-gradient(
180deg,
#69c0ff 0%,
rgba(105, 192, 255, 0.5) 100%
);
box-shadow: 0 0 15px rgba(64, 169, 255, 0.6);
}
.bar-label {
color: #fff;
}
}
.bar-value {
font-size: vw(12);
font-weight: bold;
color: rgba(255, 255, 255, 0.9);
margin-bottom: vw(5);
transition: all 0.3s ease;
}
.bar {
width: vw(30);
min-width: 16px;
background: linear-gradient(
180deg,
#40a9ff 0%,
rgba(64, 169, 255, 0.3) 100%
);
border-radius: 2px 2px 0 0;
min-height: 20px;
transition: all 0.3s ease;
}
.bar-label {
bottom: vw(-20);
font-size: vw(10);
color: rgba(255, 255, 255, 0.7);
transition: all 0.3s ease;
position: absolute;
}
}
}
.chart-x-label {
position: absolute;
right: vw(-15);
bottom: 0;
font-size: vw(10);
color: rgba(255, 255, 255, 0.6);
}
}
}
// 影响公路类型情况
.road-type-section {
margin-bottom: vw(20);
.section-title {
font-size: vw(16);
font-weight: 600;
color: rgba(255, 255, 255, 0.9);
margin-bottom: vw(12);
}
.road-type-cards {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: vw(10);
.road-card {
display: flex;
align-items: center;
padding: vw(12) vw(16);
background: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 6px;
box-shadow: inset 0px 0px 8px 0px rgba(55, 155, 255, 0.2);
.card-label {
font-size: vw(14);
color: rgba(255, 255, 255, 0.7);
}
.card-value {
font-size: vw(24);
font-weight: bold;
color: #40a9ff;
text-shadow: 0 0 10px rgba(64, 169, 255, 0.5);
}
}
}
}
// 区县统计表格
.district-table-section {
height: vw(100);
overflow: hidden;
:deep(.el-table) {
background: transparent;
&::before {
display: none;
}
.el-table__inner-wrapper {
background: transparent;
}
.el-table__inner-wrapper:before {
display: none;
}
.el-table__header-wrapper {
background: transparent;
position: sticky;
top: 0;
z-index: 1;
}
.el-table__body-wrapper {
background: transparent;
overflow-y: auto;
max-height: calc(#{vw(100)} - #{vw(40)}); /* 减去表头高度 */
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
tr {
background: transparent;
&:hover {
background: rgba(64, 169, 255, 0.05) !important;
}
}
th.el-table__cell {
background: transparent;
}
td.el-table__cell {
background: transparent;
}
}
}
// 响应调度
.response-section {
padding: vw(15);
margin-top: vw(20);
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: vw(15);
background-image: url("../../assets/RiskWarning_img/标题bg@2x.png")
no-repeat;
background-size: cover;
background-position: left;
.header-left {
display: flex;
align-items: center;
gap: vw(8);
.icon-back {
width: vw(20);
height: vw(20);
min-width: 16px;
min-height: 16px;
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
&::before {
content: "←";
color: #fff;
font-size: vw(12);
}
}
.title {
font-size: vw(14);
font-weight: bold;
color: #fff;
}
}
.header-filters {
display: flex;
align-items: center;
gap: vw(6);
font-size: vw(10);
.filter-item {
color: rgba(255, 255, 255, 0.6);
padding: vw(3) vw(8);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
cursor: pointer;
&.active {
background: rgba(64, 169, 255, 0.2);
color: #40a9ff;
border-color: rgba(64, 169, 255, 0.5);
}
}
.filter-separator {
color: rgba(255, 255, 255, 0.4);
}
}
}
// 6个统计项网格
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: vw(10);
margin-bottom: vw(15);
.stat-item {
display: flex;
align-items: center;
gap: vw(8);
padding: vw(10);
background: rgba(64, 169, 255, 0.08);
border-radius: 6px;
.stat-icon {
width: 100%;
max-width: vw(40);
height: auto;
display: flex;
align-items: center;
justify-content: center;
font-size: vw(18);
&.icon-call::before {
content: "💬";
}
&.icon-reply::before {
content: "✉️";
}
&.icon-rate::before {
content: "⏱️";
}
&.icon-district::before {
content: "📍";
}
&.icon-help::before {
content: "🤝";
}
&.icon-check::before {
content: "👥";
}
}
.stat-info {
.stat-num {
font-size: vw(24);
font-weight: bold;
color: #40a9ff;
margin-bottom: 2px;
}
.stat-label {
font-size: vw(12);
color: #ffffff;
}
}
&.clickable {
cursor: pointer;
transition: all 0.3s;
&:hover {
background: rgba(64, 169, 255, 0.2);
transform: translateY(-2px);
.stat-num {
color: #69c0ff;
text-shadow: 0 0 10px rgba(105, 192, 255, 0.5);
}
}
}
}
}
// 3个调度清单卡片
.dispatch-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: vw(10);
.dispatch-card {
padding: vw(12);
background: rgba(64, 169, 255, 0.1);
border: 1px solid rgba(64, 169, 255, 0.2);
border-radius: 6px;
text-align: center;
&.clickable {
cursor: pointer;
transition: all 0.3s;
&:hover {
background: rgba(64, 169, 255, 0.2);
transform: translateY(-2px);
.card-num {
color: #69c0ff;
text-shadow: 0 0 10px rgba(105, 192, 255, 0.5);
}
}
}
.card-num {
font-size: vw(20);
font-weight: bold;
color: #40a9ff;
margin-bottom: vw(6);
.unit {
font-size: vw(12);
color: rgba(255, 255, 255, 0.7);
font-weight: normal;
margin-left: 2px;
}
}
.card-label {
font-size: vw(10);
color: rgba(255, 255, 255, 0.8);
}
}
}
}
}
.date-range-wrapper {
:deep(.el-date-editor) {
width: vw(200);
min-width: 140px;
background: transparent;
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
background: #183c67;
box-shadow: inset 0px 0px 8px 0px #4fecff;
.el-range-input {
background: transparent;
color: rgba(255, 255, 255, 0.8);
&::placeholder {
color: rgba(255, 255, 255, 0.5);
}
}
.el-range-separator {
color: rgba(255, 255, 255, 0.4);
}
.el-icon {
color: rgba(255, 255, 255, 0.5);
}
// &:hover {
// border-color: rgba(64, 169, 255, 0.5);
// }
}
}
</style>