This commit is contained in:
niedongsheng 2026-04-17 18:16:36 +08:00
commit 285bc95b91
29 changed files with 3547 additions and 1690 deletions

10
.prettierrc Normal file
View File

@ -0,0 +1,10 @@
{
"printWidth": 100,
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"arrowParens": "avoid",
"htmlWhitespaceSensitivity": "ignore",
"vueIndentScriptAndStyle": false
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -190,6 +190,17 @@ const routes = [
parentRoute: 'warningManagement3'
}
},
{
path: '/messageManagement',
name: 'messageManagement',
component: () => import('../views/WarningManagement/law/messageManagement/index.vue'),
meta: {
title: '消息推送设置',
breadcrumb: true,
parentRoute: 'warningManagement3'
}
},
// 项目管理 - 区县
{

View File

@ -16,7 +16,7 @@
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">影响区域</span>
<span class="filter-label">行政区域</span>
<el-select
:teleported="false"
v-model="filterForm.district"
@ -86,9 +86,9 @@
<!-- 管控措施列插槽 -->
<template #roadConditionType="{ row }">
<span :class="['control-tag', getControlClass(row.roadConditionType)]">{{
row.roadConditionType
}}</span>
<span :class="['control-tag', getControlClass(row.roadConditionType)]">
{{ row.roadConditionType }}
</span>
</template>
<!-- 操作列插槽 -->
@ -99,15 +99,11 @@
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close } from "@element-plus/icons-vue";
import {
regionOptions,
typeOptions,
controlMeasureOptions,
} from "../component/index.js";
import BaseDialog from "../component/baseDialog.vue";
import { request } from "@/utils/request";
import { ref, computed, watch } from 'vue';
import { Close } from '@element-plus/icons-vue';
import { regionOptions, typeOptions, controlMeasureOptions } from '../component/index.js';
import BaseDialog from '../component/baseDialog.vue';
import { request } from '@/utils/request';
const props = defineProps({
visible: {
@ -116,13 +112,13 @@ const props = defineProps({
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
const emit = defineEmits(['update:visible', 'close', 'detail']);
//
const filterForm = ref({
district: "",
type: "",
roadConditionType: "",
district: '',
type: '',
roadConditionType: '',
});
//
@ -139,21 +135,21 @@ const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: "id", label: "序号", width: "" },
{ prop: "district", label: "影响区域", width: "" },
{ prop: "routeNo", label: "线路编号", width: "" },
{ prop: "stakeNo", label: "起止桩号", width: "", slot: "stakeNo" },
{ prop: "location", label: "路况位置", width: "", slot: "location" },
{ prop: "occurrenceTime", label: "发生时间", width: "" },
{ prop: 'id', label: '序号', width: '' },
{ prop: 'district', label: '行政区域', width: '' },
{ prop: 'routeNo', label: '线路编号', width: '' },
{ prop: 'stakeNo', label: '起止桩号', width: '', slot: 'stakeNo' },
{ prop: 'location', label: '路况位置', width: '', slot: 'location' },
{ prop: 'occurrenceTime', label: '发生时间', width: '' },
// { prop: "routeNo2", label: "线", width: "" },
{ prop: "type", label: "类型", width: "" },
{ prop: 'type', label: '类型', width: '' },
{
prop: "roadConditionType",
label: "管控措施",
width: "",
slot: "roadConditionType",
prop: 'roadConditionType',
label: '管控措施',
width: '',
slot: 'roadConditionType',
},
{ prop: "operation", label: "操作", width: "", slot: "operation" },
{ prop: 'operation', label: '操作', width: '', slot: 'operation' },
]);
//
@ -183,34 +179,34 @@ const visiblePages = computed(() => {
});
//
const getControlClass = (measure) => {
const getControlClass = measure => {
const classMap = {
全幅封闭: "control-close",
半幅封闭: "control-half",
正常通行: "control-normal",
限制通行: "control-limit",
全幅封闭: 'control-close',
半幅封闭: 'control-half',
正常通行: 'control-normal',
限制通行: 'control-limit',
};
return classMap[measure] || "";
return classMap[measure] || '';
};
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
emit('update:visible', false);
emit('close');
};
//
const handleDetail = (item) => {
emit("detail", item);
const handleDetail = item => {
emit('detail', item);
};
//
const handleSizeChange = (val) => {
const handleSizeChange = val => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
const handleCurrentChange = val => {
currentPage.value = val;
fetchData();
};
@ -224,19 +220,19 @@ const fetchData = async () => {
try {
//
let measureType = 0;
if (filterForm.value.roadConditionType === "全幅封闭") {
if (filterForm.value.roadConditionType === '全幅封闭') {
measureType = 1;
} else if (filterForm.value.roadConditionType === "半幅封闭") {
} else if (filterForm.value.roadConditionType === '半幅封闭') {
measureType = 2;
} else if (filterForm.value.roadConditionType === "限速") {
} else if (filterForm.value.roadConditionType === '限速') {
measureType = 3;
} else if (filterForm.value.roadConditionType === "告警阻拦") {
} else if (filterForm.value.roadConditionType === '告警阻拦') {
measureType = 4;
}
const res = await request({
url: "/snow-ops-platform/sm-event/dashboard/control-list",
method: "GET",
url: '/snow-ops-platform/sm-event/dashboard/control-list',
method: 'GET',
params: {
pageNum: currentPage.value,
pageSize: pageSize.value,
@ -244,40 +240,40 @@ const fetchData = async () => {
},
});
if (res.code === "00000" && res.data) {
if (res.code === '00000' && res.data) {
const data = res.data;
//
tableData.value = data.records.map((item) => {
tableData.value = data.records.map((item, index) => {
return {
id: item.id,
district: item.district || "-",
routeNo: item.routeNo || "-",
stakeNo: `${item.startStakeNo}-${item.endStakeNo}` || "-",
location: item.occurLocation || "-",
occurrenceTime: item.occurTime || "-",
id: currentPage.value * pageSize.value - (pageSize.value - index - 1),
district: item.affectedArea || '-',
routeNo: item.routeNo || '-',
stakeNo: `${item.startStakeNo}-${item.endStakeNo}` || '-',
location: item.occurLocation || '-',
occurrenceTime: item.occurTime || '-',
// routeNo2: item.routeNo || "-",
type: item.roadConditionType || "-",
roadConditionType: item.roadConditionType || "-",
expectRecoverTime: item.expectRecoverTime || "-",
eventStatus: item.eventStatus || "-",
type: item.roadConditionType || '-',
roadConditionType: item.processingMeasure || '-',
expectRecoverTime: item.expectRecoverTime || '-',
eventStatus: item.eventStatus || '-',
};
});
total.value = data.total;
}
} catch (error) {
console.error("获取抢通情况数据失败:", error);
console.error('获取抢通情况数据失败:', error);
}
};
// visible
watch(
() => props.visible,
(newVal) => {
newVal => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
},
}
);
</script>

View File

@ -17,7 +17,7 @@
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">影响区域</span>
<span class="filter-label">行政区域</span>
<el-select :teleported="false" v-model="filterForm.region" placeholder="请选择" class="filter-select" clearable>
<el-option
v-for="item in regionOptions"
@ -83,7 +83,7 @@ const filterForm = ref({
riskLevel: "",
});
//
//
// index.js
//
@ -95,7 +95,7 @@ const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: 'id', label: '序号', width: '60' },
{ prop: 'region', label: '影响区域', width: '100' },
{ prop: 'region', label: '行政区域', width: '100' },
{ prop: 'stationName', label: '驻地名称', width: '200', slot: 'stationName' },
{ prop: 'project', label: '所属项目', width: '200', slot: 'project' },
{ prop: 'peopleCount', label: '驻地人数', width: '80' },

View File

@ -17,7 +17,7 @@
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">影响区域</span>
<span class="filter-label">行政区域</span>
<el-select :teleported="false"
v-model="filterForm.region"
placeholder="请选择"
@ -76,7 +76,7 @@ const filterForm = ref({
type: "",
});
//
//
// index.js
//

View File

@ -17,7 +17,7 @@
<template #filter>
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">影响区域</span>
<span class="filter-label">行政区域</span>
<el-select :teleported="false"
v-model="filterForm.region"
placeholder="请选择"
@ -68,7 +68,7 @@ const filterForm = ref({
region: "",
});
//
//
// index.js
//
@ -77,7 +77,7 @@ const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: "id", label: "序号", width: "" },
{ prop: "region", label: "影响区域", width: "" },
{ prop: "region", label: "行政区域", width: "" },
{ prop: "dispatchCount", label: "调度数", width: "", slot: "dispatchCount" },
{ prop: "lastDispatchTime", label: "最近调度时间", width: "" },
]);

View File

@ -0,0 +1,451 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="涉灾隐患点情况"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="450"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:z-index="2000"
:max-width="900"
:show-filter="false"
:table-show="false"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 自定义内容区域 -->
<template #header>
<div class="hazard-info-panel">
<!-- 基本信息 -->
<div class="info-section">
<div class="info-row">
<div class="info-item">
<span class="info-label">区县名称</span>
<span class="info-value">{{ hazardData.district }}</span>
</div>
<div class="info-item">
<span class="info-label">风险等级</span>
<span class="info-value">{{ hazardData.riskLevel }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="info-label">公路编号</span>
<span class="info-value">{{ hazardData.roadCode }}</span>
</div>
<div class="info-item">
<span class="info-label">位置</span>
<span class="info-value">{{ hazardData.location }}</span>
</div>
</div>
</div>
<!-- 风险描述 -->
<div class="info-block">
<div class="block-title">风险描述</div>
<div class="block-content">{{ hazardData.riskDescription }}</div>
</div>
<!-- 采取措施 -->
<div class="info-block">
<div class="block-title">采取措施</div>
<div class="block-content">{{ hazardData.measures }}</div>
</div>
<!-- 三级包保责任人 -->
<div class="info-block">
<div class="block-title">三级包保责任人</div>
<div class="responsibility-list">
<div class="responsibility-item">
<span class="responsibility-label">交通主管部门责任人</span>
<span class="responsibility-name">{{ hazardData.trafficDept.name }}</span>
<span class="responsibility-phone">{{ hazardData.trafficDept.phone }}</span>
<span class="responsibility-frequency">{{ hazardData.trafficDept.frequency }}</span>
</div>
<div class="responsibility-item">
<span class="responsibility-label">公路机构责任人</span>
<span class="responsibility-name">{{ hazardData.roadOrg.name }}</span>
<span class="responsibility-phone">{{ hazardData.roadOrg.phone }}</span>
<span class="responsibility-frequency">{{ hazardData.roadOrg.frequency }}</span>
</div>
<div class="responsibility-item">
<span class="responsibility-label">养护站责任人</span>
<span class="responsibility-name">{{ hazardData.maintenance.name }}</span>
<span class="responsibility-phone">{{ hazardData.maintenance.phone }}</span>
<span class="responsibility-frequency">{{ hazardData.maintenance.frequency }}</span>
</div>
</div>
</div>
<!-- 护路员 -->
<div class="info-row simple-row">
<span class="row-label">护路员</span>
<span class="row-value name">{{ hazardData.roadKeeper.name }}</span>
<span class="row-value phone">{{ hazardData.roadKeeper.phone }}</span>
<span class="row-value frequency">{{ hazardData.roadKeeper.frequency }}</span>
</div>
<!-- 预警预报关 -->
<div class="info-row simple-row">
<span class="row-label">预警预报关</span>
<span class="row-value">{{ hazardData.earlyWarning }}</span>
</div>
<!-- 线下巡查关 -->
<div class="info-row simple-row">
<span class="row-label">线下巡查关</span>
<span class="row-value">{{ hazardData.offlinePatrol }}</span>
</div>
<!-- 交通管控关 -->
<div class="info-row simple-row">
<span class="row-label">交通管控关</span>
<span class="row-value">{{ hazardData.trafficControl }}</span>
</div>
<!-- 力量预置关 -->
<div class="info-row simple-row">
<span class="row-label">力量预置关</span>
<span class="row-value">{{ hazardData.forcePreposition }}</span>
<el-icon class="location-icon"><Location /></el-icon>
</div>
<!-- 告警阻拦关 -->
<div class="info-row simple-row">
<span class="row-label">告警阻拦关</span>
<span class="row-value">{{ hazardData.alarmBlocking }}</span>
</div>
</div>
</template>
</base-dialog>
</template>
<script setup>
import { ref, watch } from 'vue';
import { Location } from '@element-plus/icons-vue';
import baseDialog from '../component/baseDialog.vue';
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
data: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(['update:visible', 'close']);
// 使
const tableColumns = ref([]);
const tableData = ref([]);
const total = ref(0);
const currentPage = ref(1);
const pageSize = ref(10);
//
const hazardData = ref({
//
district: '', // GL1_QXMC
riskLevel: '', // GL1_FXDJ
roadCode: '', // GL1_GLBH 线
roadName: '', // GL1_GLMC 线
location: '', // GL1_QDZH + GL1_ZDZH
riskDescription: '', // GL1_FXMS
riskType: '', // GL1_FXLX
measures: '', // GL1_CQCS
isWithinRedLine: '', // GL1_SFHXN 线
//
trafficDept: {
name: '', // GL1_JTXM
phone: '', // GL1_JTDH
frequency: '',
},
roadOrg: {
name: '', // GL1_JGXM
phone: '', // GL1_JGDH
frequency: '',
},
maintenance: {
name: '', // GL1_YHXM
phone: '', // GL1_YHDH
frequency: '',
},
roadKeeper: {
name: '', // GL1_HLXM
phone: '', // GL1_HLDH
frequency: '',
},
//
earlyWarning: '', // GL1_YJDJ
offlinePatrol: '', // GL1_SFJCD
trafficControl: '', // GL1_SFZCQS
forcePreposition: '', // GL1_ZT
alarmBlocking: '', // GL1_SFZZWC
//
isMeasureTaken: '', // GL1_SFCQCS
completeDeadline: '', // GL1_WCSX
expertOpinion: '', // GL1_ZJYJ
isTransferred: '', // GL1_SFGZYJ
auditStatus: '', // GL1_SHZT
auditUnit: '', // GL1_SHDW
reporter: '', // GL1_SBR
reportTime: '', // GL1_SBSJ
remark: '', // GL1_BZ
//
longitude: '', // GL1_LON
latitude: '', // GL1_LAT
});
//
const handleClose = () => {
emit('update:visible', false);
emit('close');
};
//
const handleSizeChange = val => {
pageSize.value = val;
};
const handleCurrentChange = val => {
currentPage.value = val;
};
// visible
watch(
() => props.visible,
newVal => {
if (newVal && props.data) {
//
const data = props.data;
hazardData.value = {
//
district: data.GL1_QXMC || data.district || '',
riskLevel: data.GL1_FXDJ || data.riskLevel || '',
roadCode: data.GL1_GLBH || data.roadCode || '',
roadName: data.GL1_GLMC || data.roadName || '',
location:
data.GL1_QDZH && data.GL1_ZDZH
? `${data.GL1_QDZH}${data.GL1_ZDZH}`
: data.location || '',
riskDescription: data.GL1_FXMS || data.riskDescription || '',
riskType: data.GL1_FXLX || data.riskType || '',
measures: data.GL1_CQCS || data.measures || '',
isWithinRedLine: data.GL1_SFHXN || data.isWithinRedLine || '',
//
trafficDept: {
name: data.GL1_JTXM || data.trafficDept?.name || '',
phone: data.GL1_JTDH || data.trafficDept?.phone || '',
frequency: data.trafficDept?.frequency || '',
},
roadOrg: {
name: data.GL1_JGXM || data.roadOrg?.name || '',
phone: data.GL1_JGDH || data.roadOrg?.phone || '',
frequency: data.roadOrg?.frequency || '',
},
maintenance: {
name: data.GL1_YHXM || data.maintenance?.name || '',
phone: data.GL1_YHDH || data.maintenance?.phone || '',
frequency: data.maintenance?.frequency || '',
},
roadKeeper: {
name: data.GL1_HLXM || data.roadKeeper?.name || '',
phone: data.GL1_HLDH || data.roadKeeper?.phone || '',
frequency: data.roadKeeper?.frequency || '',
},
//
earlyWarning: data.GL1_YJDJ || data.earlyWarning || '',
offlinePatrol: data.GL1_SFJCD || data.offlinePatrol || '',
trafficControl: data.GL1_SFZCQS || data.trafficControl || '',
forcePreposition: data.GL1_ZT || data.forcePreposition || '',
alarmBlocking: data.GL1_SFZZWC || data.alarmBlocking || '',
//
isMeasureTaken: data.GL1_SFCQCS || data.isMeasureTaken || '',
completeDeadline: data.GL1_WCSX || data.completeDeadline || '',
expertOpinion: data.GL1_ZJYJ || data.expertOpinion || '',
isTransferred: data.GL1_SFGZYJ || data.isTransferred || '',
auditStatus: data.GL1_SHZT || data.auditStatus || '',
auditUnit: data.GL1_SHDW || data.auditUnit || '',
reporter: data.GL1_SBR || data.reporter || '',
reportTime: data.GL1_SBSJ || data.reportTime || '',
remark: data.GL1_BZ || data.remark || '',
//
longitude: data.GL1_LON || data.longitude || '',
latitude: data.GL1_LAT || data.latitude || '',
};
}
},
{ immediate: true }
);
</script>
<style lang="scss" scoped>
.hazard-info-panel {
height: 500px;
color: rgba(255, 255, 255, 0.9);
overflow-y: auto;
//
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: rgba(20, 46, 73, 0.3);
border-radius: 3px;
}
&::-webkit-scrollbar-thumb {
background: #142e49;
border-radius: 3px;
&:hover {
background: #1a3a5c;
}
}
// Firefox
scrollbar-width: thin;
scrollbar-color: #142e49 rgba(20, 46, 73, 0.3);
.info-section {
margin-bottom: 16px;
}
.info-row {
display: flex;
margin-bottom: 12px;
&.simple-row {
align-items: center;
padding: 8px 0;
border-bottom: 1px solid rgba(64, 169, 255, 0.1);
.row-label {
width: 100px;
color: rgba(255, 255, 255, 0.6);
font-size: 14px;
}
.row-value {
flex: 1;
color: rgba(255, 255, 255, 0.9);
font-size: 14px;
&.name {
color: #40a9ff;
width: auto;
flex: none;
margin-right: 8px;
}
&.phone {
color: #40a9ff;
width: auto;
flex: none;
margin-right: 16px;
}
&.frequency {
color: rgba(255, 255, 255, 0.6);
width: auto;
flex: none;
}
}
.location-icon {
color: #40a9ff;
font-size: 16px;
cursor: pointer;
margin-left: 8px;
}
}
}
.info-item {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
.info-label {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
}
.info-value {
color: rgba(255, 255, 255, 0.9);
font-size: 14px;
font-weight: 500;
}
}
.info-block {
margin-bottom: 16px;
padding: 12px;
background: rgba(64, 169, 255, 0.05);
border-radius: 4px;
.block-title {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
margin-bottom: 8px;
}
.block-content {
color: rgba(255, 255, 255, 0.9);
font-size: 14px;
line-height: 1.6;
}
}
.responsibility-list {
display: flex;
flex-direction: column;
gap: 8px;
.responsibility-item {
display: flex;
align-items: center;
gap: 8px;
.responsibility-label {
width: 140px;
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
}
.responsibility-name {
color: #40a9ff;
font-size: 14px;
width: 60px;
}
.responsibility-phone {
color: #40a9ff;
font-size: 14px;
width: 120px;
}
.responsibility-frequency {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
flex: 1;
}
}
}
}
</style>

View File

@ -79,7 +79,7 @@
:teleported="false"
v-model="filterForm.region"
size="small"
placeholder="影响区域"
placeholder="行政区域"
class="filter-select"
>
<el-option
@ -260,7 +260,7 @@ const filterForm = ref({
// 使
const unifiedColumns = [
{ prop: "id", label: "序号", width: "" },
{ prop: "region", label: "影响区域", width: "" },
{ prop: "region", label: "行政区域", width: "" },
{ prop: "pointType", label: "影响点类型", width: "" },
{ prop: "pointLocation", label: "影响点位置", width: "" },
{ prop: "pointLevel", label: "影响点等级", width: "", slot: "pointLevel" },
@ -296,7 +296,7 @@ const unifiedColumns = [
//
const projectColumns = [
{ prop: "id", label: "序号", width: "60" },
{ prop: "region", label: "影响区域", width: "" },
{ prop: "region", label: "行政区域", width: "" },
{ prop: "pointType", label: "影响点类型", width: "" },
{ prop: "siteName", label: "驻地名称", width: "" },
{
@ -535,7 +535,7 @@ const processUnifiedData = (item, type) => {
//
const baseData = {
id: item.id,
//
//
region:
item.GL1_QXMC ||
item.COUNTY ||
@ -600,7 +600,7 @@ const processUnifiedData = (item, type) => {
if (cardTypeVal.value === "桥梁") {
return {
...baseData,
// - 使
// - 使
region: item.GL1_QXMC || "-",
// - 使
pointLocation: item.GL1_QLMC || "-",
@ -641,7 +641,7 @@ const processUnifiedData = (item, type) => {
if (cardTypeVal.value === "隧道") {
return {
...baseData,
// - 使
// - 使
region: item.GL1_QXMC || item.GL1_QXBM || "-",
// - 使
pointLocation: item.GL1_SDMC || "-",
@ -682,8 +682,8 @@ const processUnifiedData = (item, type) => {
if (cardTypeVal.value === "路段") {
return {
...baseData,
// - 使
region: item.GL1_QXMC || "-",
// - 使
region: item.GL1_ZDMC || "-",
// - 使线+
pointLocation: `${item.GL1_GLMC || item.GL1_GLBH || "-"} (${item.GL1_QDZH || "-"} - ${item.GL1_ZDZH || "-"})`,
// - 使
@ -721,7 +721,7 @@ const processUnifiedData = (item, type) => {
if (cardTypeVal.value === "项目") {
return {
...baseData,
// - 使COUNTY
// - 使COUNTY
region: item.COUNTY || item.county || item.county_name || "-",
// - 使
pointLocation: item.PROJECT_NAME || item.projectName || item.name || "-",

View File

@ -19,78 +19,20 @@
<!-- 统计卡片 -->
<div class="stats-cards">
<div
@click="handleClick('0')"
v-for="(item, index) in statsCardsData"
:key="index"
@click="handleClick(item.type)"
class="stat-card"
:style="{
backgroundImage: `url(${cardType === '0' ? selectedIcon : unselectedIcon})`,
backgroundImage: `url(${cardType === item.type ? selectedIcon : unselectedIcon})`,
backgroundSize: '100% 100%',
backgroundPosition: 'center',
}"
>
<div class="stat-icon"><img :src="Icon0" alt="" /></div>
<div class="stat-icon"><img :src="item.icon" alt="" /></div>
<div class="stat-content">
<span class="stat-label">影响桥梁</span>
<span class="stat-value">(1430)</span>
</div>
</div>
<div
@click="handleClick('1')"
class="stat-card"
:style="{
backgroundImage: `url(${cardType === '1' ? selectedIcon : unselectedIcon})`,
backgroundSize: '100% 100%',
backgroundPosition: 'center',
}"
>
<div class="stat-icon"><img :src="Icon1" alt="" /></div>
<div class="stat-content">
<span class="stat-label">影响边坡</span>
<span class="stat-value">(933)</span>
</div>
</div>
<div
@click="handleClick('2')"
class="stat-card"
:style="{
backgroundImage: `url(${cardType === '2' ? selectedIcon : unselectedIcon})`,
backgroundSize: '100% 100%',
backgroundPosition: 'center',
}"
>
<div class="stat-icon"><img :src="Icon2" alt="" /></div>
<div class="stat-content">
<span class="stat-label">影响隧道</span>
<span class="stat-value">(1033)</span>
</div>
</div>
<div
@click="handleClick('3')"
class="stat-card"
:style="{
backgroundImage: `url(${cardType === '3' ? selectedIcon : unselectedIcon})`,
backgroundSize: '100% 100%',
backgroundPosition: 'center',
}"
>
<div class="stat-icon"><img :src="Icon3" alt="" /></div>
<div class="stat-content">
<span class="stat-label">影响项目</span>
<span class="stat-value">(832)</span>
</div>
</div>
<div
@click="handleClick('4')"
class="stat-card"
:style="{
backgroundImage: `url(${cardType === '4' ? selectedIcon : unselectedIcon})`,
backgroundSize: '100% 100%',
backgroundPosition: 'center',
}"
>
<div class="stat-icon"><img :src="Icon4" alt="" /></div>
<div class="stat-content">
<span class="stat-label">影响路段</span>
<span class="stat-value">(832)</span>
<span class="stat-label">{{ item.label }}</span>
<span class="stat-value">{{ item.value }}</span>
</div>
</div>
</div>
@ -115,7 +57,8 @@
</div> -->
<div class="filter-item">
<span class="filter-label">影响点等级</span>
<el-select :teleported="false"
<el-select
:teleported="false"
v-model="filterForm.pointLevel"
placeholder="影响点等级"
class="filter-select"
@ -130,7 +73,8 @@
</div>
<div class="filter-item">
<span class="filter-label">是否回应</span>
<el-select :teleported="false"
<el-select
:teleported="false"
v-model="filterForm.isResponded"
placeholder="是否回应"
class="filter-select"
@ -148,9 +92,7 @@
<!-- 影响点等级列插槽 -->
<template #pointLevel="{ row }">
<span class="level-tag" :class="row.levelClass">{{
row.pointLevel
}}</span>
<span class="level-tag" :class="row.levelClass">{{ row.pointLevel }}</span>
</template>
<!-- 交通主管部门负责人列插槽 -->
@ -160,11 +102,7 @@
<span style="margin-right: 5px">{{ row.trafficDept.name }}</span>
<img
class="response-icon"
:src="
row.trafficDept.isResponded
? row.trafficDept.img
: row.trafficDept.img
"
:src="row.trafficDept.isResponded ? row.trafficDept.img : row.trafficDept.img"
alt
/>
</div>
@ -197,11 +135,11 @@
</template>
<!-- 回应状态列插槽 -->
<template #responseStatus="{ row }">
<!-- <template #responseStatus="{ row }">
<span class="response-status" :class="row.responseClass">{{
row.responseStatus
}}</span>
</template>
</template> -->
<!-- 最新催告时间列插槽 -->
<template #urgeTime="{ row }">
@ -219,25 +157,22 @@
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { Close, ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
import {
pointTypeOptions,
pointLevelOptions,
isRespondedOptions,
} from "../component/index.js";
import baseDialog from "../component/baseDialog.vue";
import { ref, computed, watch, onMounted } from 'vue';
import { Close, ArrowLeft, ArrowRight } from '@element-plus/icons-vue';
import { pointTypeOptions, pointLevelOptions, isRespondedOptions } from '../component/index.js';
import baseDialog from '../component/baseDialog.vue';
import { request } from '@/utils/request.js';
import respondedIcon from "../../../assets/xiangying/有回应@2x.png";
import notRespondedIcon from "../../../assets/xiangying/无回应@2x.png";
import selectedIcon from "../../../assets/xiangying/选中bg@2x.png";
import unselectedIcon from "../../../assets/xiangying/未选中bg@2x.png";
import respondedIcon from '../../../assets/xiangying/有回应@2x.png';
import notRespondedIcon from '../../../assets/xiangying/无回应@2x.png';
import selectedIcon from '../../../assets/xiangying/选中bg@2x.png';
import unselectedIcon from '../../../assets/xiangying/未选中bg@2x.png';
import Icon0 from "../../../assets/xiangying/选中@2x.png";
import Icon1 from "../../../assets/xiangying/未选中1@2x.png";
import Icon2 from "../../../assets/xiangying/未选中2@2x.png";
import Icon3 from "../../../assets/xiangying/未选中3@2x.png";
import Icon4 from "../../../assets/xiangying/未选中4@2x.png";
import Icon0 from '../../../assets/xiangying/选中@2x.png';
import Icon1 from '../../../assets/xiangying/未选中1@2x.png';
import Icon2 from '../../../assets/xiangying/未选中2@2x.png';
import Icon3 from '../../../assets/xiangying/未选中3@2x.png';
import Icon4 from '../../../assets/xiangying/未选中4@2x.png';
const props = defineProps({
visible: {
@ -246,108 +181,94 @@ const props = defineProps({
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
const emit = defineEmits(['update:visible', 'close', 'detail']);
//
const filterForm = ref({
pointType: "",
pointLevel: "",
isResponded: "",
pointType: '',
pointLevel: '',
isResponded: '',
});
const cardType = ref("0");
const cardType = ref('0');
//
const statsCardsData = ref([
{ type: '桥梁', label: '影响桥梁', value: 0, icon: Icon0 },
{ type: '路段', label: '影响路段', value: 0, icon: Icon4 },
{ type: '隧道', label: '影响隧道', value: 0, icon: Icon2 },
{ type: '边坡', label: '影响边坡', value: 0, icon: Icon1 },
{ type: '项目', label: '影响项目', value: 0, icon: Icon3 },
]);
//
const tableColumns = ref([
{ prop: "id", label: "序号", width: "50px" },
{ prop: "pointType", label: "影响点类型", width: "80px" },
{ prop: "pointLocation", label: "影响点位置", width: "150px" },
{
prop: "pointLevel",
label: "影响点等级",
width: "90px",
slot: "pointLevel",
},
{ prop: "checkCount", label: "查次数", width: "60px" },
{
prop: "trafficDept",
label: "交通主管部门负责人",
width: "120px",
slot: "trafficDept",
},
{ prop: "roadOrg", label: "公路机构责任人", width: "110px", slot: "roadOrg" },
{
prop: "maintenance",
label: "养护站负责人",
width: "110px",
slot: "maintenance",
},
{ prop: "roadKeeper", label: "护路员", width: "80px", slot: "roadKeeper" },
{
prop: "responseStatus",
label: "回应状态",
width: "70px",
slot: "responseStatus",
},
{ prop: "urgeTime", label: "最新催告时间", width: "110px", slot: "urgeTime" },
{ prop: "operation", label: "操作", width: "50px", slot: "operation" },
{ prop: 'id', label: '序号', width: '' },
{ prop: 'pointType', label: '影响点类型', width: '' },
{ prop: 'pointLocation', label: '影响点位置', width: '' },
{ prop: 'pointLevel', label: '影响点等级', width: '', slot: 'pointLevel' },
{ prop: 'checkCount', label: '查次数', width: '' },
{ prop: 'trafficDept', label: '交通主管部门负责人', width: '', slot: 'trafficDept' },
{ prop: 'roadOrg', label: '公路机构责任人', width: '', slot: 'roadOrg' },
{ prop: 'maintenance', label: '养护站负责人', width: '', slot: 'maintenance' },
{ prop: 'roadKeeper', label: '护路员', width: '', slot: 'roadKeeper' },
{ prop: 'urgeTime', label: '最新催告时间', width: '', slot: 'urgeTime' },
{ prop: 'operation', label: '操作', width: '', slot: 'operation' },
]);
//
const tableData = ref([
{
id: 1,
pointType: "边坡",
pointLocation: "武汉-大理(K1452+951至K1462+209)",
pointLevel: "一般隐患",
levelClass: "level-normal",
pointType: '边坡',
pointLocation: '武汉-大理(K1452+951至K1462+209)',
pointLevel: '一般隐患',
levelClass: 'level-normal',
checkCount: 2,
trafficDept: {
name: "罗宸",
phone: "17623865172",
name: '罗宸',
phone: '17623865172',
img: respondedIcon,
isResponded: true,
},
roadOrg: {
name: "李海平",
phone: "1372386532",
name: '李海平',
phone: '1372386532',
img: notRespondedIcon,
isResponded: false,
},
maintenance: {
name: "苏祖兵",
phone: "13594331090",
name: '苏祖兵',
phone: '13594331090',
img: notRespondedIcon,
isResponded: false,
},
roadKeeper: {
name: "凌承礼",
phone: "1592393704",
name: '凌承礼',
phone: '1592393704',
img: respondedIcon,
isResponded: true,
},
responseStatus: "已回应",
responseClass: "status-responded",
urgeTime: { date: "2026-03-28", time: "12:30:00" },
responseStatus: '已回应',
responseClass: 'status-responded',
urgeTime: { date: '2026-03-28', time: '12:30:00' },
},
{
id: 12,
pointType: "边坡",
pointLocation: "武汉-大理(K1452+951至K1462+209)",
pointLevel: "一般隐患",
levelClass: "level-normal",
pointType: '边坡',
pointLocation: '武汉-大理(K1452+951至K1462+209)',
pointLevel: '一般隐患',
levelClass: 'level-normal',
checkCount: 2,
trafficDept: { name: "罗宸", phone: "17623865172" },
roadOrg: { name: "李海平", phone: "1372386532" },
maintenance: { name: "苏祖兵", phone: "13594331090" },
roadKeeper: { name: "凌承礼", phone: "1592393704" },
responseStatus: "已回应",
responseClass: "status-responded",
urgeTime: { date: "2026-03-28", time: "12:30:00" },
trafficDept: { name: '罗宸', phone: '17623865172' },
roadOrg: { name: '李海平', phone: '1372386532' },
maintenance: { name: '苏祖兵', phone: '13594331090' },
roadKeeper: { name: '凌承礼', phone: '1592393704' },
responseStatus: '已回应',
responseClass: 'status-responded',
urgeTime: { date: '2026-03-28', time: '12:30:00' },
},
]);
tableData.value.push(...tableData.value);
tableData.value.push(...tableData.value);
//
const currentPage = ref(1);
@ -356,49 +277,90 @@ const total = ref(36);
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
emit('update:visible', false);
emit('close');
};
//
const handleClick = (type) => {
const handleClick = type => {
cardType.value = type;
};
//
const loadBarChartData = async () => {
try {
const res = await request({
url: '/snow-ops-platform/weather-warning/affected-count',
method: 'GET',
});
if (res.code == '00000') {
const data = res.data;
if (data && Array.isArray(data)) {
//
const nameMap = {
'road-section': '路段',
bridge: '桥梁',
tunnel: '隧道',
slope: '边坡',
project: '项目',
Road: '路段',
Bridge: '桥梁',
Tunnel: '隧道',
Slope: '边坡',
Project: '项目',
};
statsCardsData.value.forEach(item => {
data.forEach(stat => {
if (stat.extension == item.type) {
item.value = stat.count || 0;
}
});
});
}
}
} catch (error) {
console.error('加载柱状图数据失败:', error);
}
};
// base-dialog
//
const handleDetail = (item) => {
emit("detail", item);
const handleDetail = item => {
emit('detail', item);
};
//
const handleSizeChange = (val) => {
const handleSizeChange = val => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
const handleCurrentChange = val => {
currentPage.value = val;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
console.log('获取第', currentPage.value, '页数据');
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
newVal => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
},
}
);
onMounted(() => {
//
loadBarChartData();
});
</script>
<style lang="scss" scoped>

View File

@ -1,7 +1,7 @@
<template>
<base-dialog
v-model:visible="props.visible"
title="潼南基本信息表"
:title="props.allCountyData.name + '基本信息表'"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="320"
@ -32,70 +32,83 @@
<!-- 驻地名称列插槽 -->
<template #stationName="{ row }">
<el-tooltip :content="row.stationName" placement="top" :show-after="500">
<span class="station-name-text" @click="handleStationNameClick(row)">{{ row.stationName }}</span>
<span class="station-name-text" @click="handleStationNameClick(row)">
{{ row.stationName }}
</span>
</el-tooltip>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch } from "vue";
import { VideoCamera, Microphone, Phone } from "@element-plus/icons-vue";
import baseDialog from "../component/baseDialog.vue";
import { ref, computed, watch } from 'vue';
import { VideoCamera, Microphone, Phone } from '@element-plus/icons-vue';
import baseDialog from '../component/baseDialog.vue';
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
allCountyData: {
type: Object,
default: () => {},
},
tongnanInfoItemData: {
type: Object,
default: () => ({
name: '',
}),
},
});
const emit = defineEmits(["update:visible", "close", "video", "voice", "call", "stationNameClick"]);
const emit = defineEmits(['update:visible', 'close', 'video', 'voice', 'call', 'stationNameClick']);
//
const tableColumns = ref([
{ prop: "id", label: "序号", width: "60px" },
{ prop: "region", label: "区县/镇街", width: "140px" },
{ prop: "name", label: "姓名", width: "100px" },
{ prop: "phone", label: "电话", width: "120px" },
{ prop: "stationName", label: "驻地名称", width: "180px", slot: "stationName" },
{ prop: "type", label: "类型", width: "auto" },
{ prop: "operation", label: "调度", width: "140px", slot: "operation" },
{ prop: 'id', label: '序号', width: '60px' },
{ prop: 'region', label: '区县/镇街', width: '140px' },
{ prop: 'name', label: '姓名', width: '100px' },
{ prop: 'phone', label: '电话', width: '120px' },
{ prop: 'stationName', label: '驻地名称', width: '180px', slot: 'stationName' },
{ prop: 'type', label: '类型', width: 'auto' },
{ prop: 'operation', label: '调度', width: '140px', slot: 'operation' },
]);
//
const tableData = ref([
{
id: 1,
region: "沙坪坝区",
name: "赵海浪",
phone: "18623520688",
stationName: "沙坪坝区S545茅山峡公路桥新建工程渝黔铁路扩能改造工程项目经理部",
type: "交通主管部门",
region: '沙坪坝区',
name: '赵海浪',
phone: '18623520688',
stationName: '沙坪坝区S545茅山峡公路桥新建工程渝黔铁路扩能改造工程项目经理部',
type: '交通主管部门',
},
{
id: 2,
region: "沙坪坝区",
name: "府效能",
phone: "18623520688",
stationName: "沙坪坝区S545茅山峡公路桥新建工程渝黔铁路扩能改造工程项目经理部",
type: "公路机构",
region: '沙坪坝区',
name: '府效能',
phone: '18623520688',
stationName: '沙坪坝区S545茅山峡公路桥新建工程渝黔铁路扩能改造工程项目经理部',
type: '公路机构',
},
{
id: 3,
region: "万州区柏梓镇",
name: "王鑫",
phone: "18623520688",
stationName: "万州区项目经理部",
type: "公路机构",
region: '万州区柏梓镇',
name: '王鑫',
phone: '18623520688',
stationName: '万州区项目经理部',
type: '公路机构',
},
{
id: 4,
region: "万州区柏梓镇",
name: "王鑫",
phone: "18623520688",
stationName: "万州区项目经理部",
type: "公路机构",
region: '万州区柏梓镇',
name: '王鑫',
phone: '18623520688',
stationName: '万州区项目经理部',
type: '公路机构',
},
]);
@ -124,52 +137,52 @@ const visiblePages = computed(() => {
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
emit('update:visible', false);
emit('close');
};
// base-dialog
//
const handleVideo = (item) => {
emit("video", item);
const handleVideo = item => {
emit('video', item);
};
const handleVoice = (item) => {
emit("voice", item);
const handleVoice = item => {
emit('voice', item);
};
const handleCall = (item) => {
emit("call", item);
const handleCall = item => {
emit('call', item);
};
//
const handleStationNameClick = (item) => {
emit("stationNameClick", item);
const handleStationNameClick = item => {
emit('stationNameClick', item);
};
//
const handleSizeChange = (size) => {
const handleSizeChange = size => {
pageSize.value = size;
currentPage.value = 1;
fetchData();
};
const handleCurrentChange = (page) => {
const handleCurrentChange = page => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
console.log('获取第', currentPage.value, '页数据');
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
newVal => {
if (newVal) {
currentPage.value = 1;
fetchData();
@ -189,7 +202,7 @@ watch(
width: 28px;
height: 28px;
display: flex;
align-items: center;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.7);
cursor: pointer;

View File

@ -0,0 +1,238 @@
<template>
<base-dialog
v-model:visible="props.visible"
:title="`${props.allCountyData.name}建设项目责任人详情`"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="350"
:total="total"
:current-page="currentPage"
:page-size="pageSize"
:z-index="1000"
:max-width="1100"
:show-filter="false"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
>
<!-- 调度列插槽 -->
<template #dispatch="{ row }">
<div class="dispatch-actions">
<div class="action-btns">
<div class="action-btn" @click="handleView(row)">
<el-icon><VideoCamera /></el-icon>
</div>
<div class="action-btn" @click="handleVoice(row)">
<el-icon><Microphone /></el-icon>
</div>
<div class="action-btn" @click="handleCall(row)">
<el-icon><Phone /></el-icon>
</div>
</div>
</div>
</template>
</base-dialog>
</template>
<script setup>
import { ref, watch } from 'vue';
import baseDialog from '../component/baseDialog.vue';
import {
Close,
VideoCamera,
Microphone,
Phone,
ArrowLeft,
ArrowRight,
User,
OfficeBuilding,
MapLocation,
} from '@element-plus/icons-vue';
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
allCountyData: {
type: Object,
default: () => {},
},
});
const emit = defineEmits(['update:visible', 'close']);
//
const tableColumns = ref([
{ prop: 'id', label: '序号', width: '' },
{ prop: 'district', label: '区县/镇街', width: '' },
{ prop: 'totalCount', label: '总人数', width: '' },
{ prop: 'whistleblower', label: '吹哨人', width: '' },
{ prop: 'constructionUnit', label: '建设单位包保责任人', width: '' },
{ prop: 'constructionDept', label: '施工单位包保责任人', width: '' },
{ prop: 'stationUnit', label: '驻地包保责任人', width: '' },
{ prop: 'districtUnit', label: '区县级包保责任人', width: '' },
{ prop: 'cityUnit', label: '市级包保责任人', width: '' },
{ prop: 'dispatch', label: '调度', width: '', slot: 'dispatch' },
]);
//
const tableData = ref([
{
id: 1,
district: '万州区',
totalCount: 6,
whistleblower: 2,
constructionUnit: '罗宸\n17623865172',
constructionDept: '李海平\n1372386532',
stationUnit: '苏祖兵\n13594331090',
districtUnit: '凌承礼\n1592393704',
cityUnit: '周刚\n1892395467',
},
{
id: 2,
district: '涪陵区',
totalCount: 8,
whistleblower: 3,
constructionUnit: '张三\n13800138000',
constructionDept: '李四\n13900139000',
stationUnit: '王五\n13700137000',
districtUnit: '赵六\n13600136000',
cityUnit: '孙七\n13500135000',
},
{
id: 3,
district: '渝中区',
totalCount: 5,
whistleblower: 2,
constructionUnit: '周八\n13400134000',
constructionDept: '吴九\n13300133000',
stationUnit: '郑十\n13200132000',
districtUnit: '钱十一\n13100131000',
cityUnit: '陈十二\n13000130000',
},
{
id: 4,
district: '江北区',
totalCount: 7,
whistleblower: 3,
constructionUnit: '刘一\n12900129000',
constructionDept: '黄二\n12800128000',
stationUnit: '林三\n12700127000',
districtUnit: '杨四\n12600126000',
cityUnit: '何五\n12500125000',
},
{
id: 5,
district: '沙坪坝区',
totalCount: 6,
whistleblower: 2,
constructionUnit: '高六\n12400124000',
constructionDept: '马七\n12300123000',
stationUnit: '罗八\n12200122000',
districtUnit: '梁九\n12100121000',
cityUnit: '宋十\n12000120000',
},
]);
const total = ref(36);
const currentPage = ref(1);
const pageSize = ref(10);
//
const handleClose = () => {
emit('update:visible', false);
emit('close');
};
//
const handleSizeChange = val => {
pageSize.value = val;
fetchData();
};
const handleCurrentChange = val => {
currentPage.value = val;
fetchData();
};
//
const fetchData = () => {
console.log('获取第', currentPage.value, '页数据');
// API
};
//
const handleView = item => {
console.log('查看视频:', item);
};
const handleVoice = item => {
console.log('语音通话:', item);
};
const handleCall = item => {
console.log('拨打电话:', item);
};
// visible
watch(
() => props.visible,
newVal => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
}
);
</script>
<style lang="scss" scoped>
.dispatch-actions {
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
.dispatch-icon {
width: 24px;
height: 24px;
cursor: pointer;
transition: transform 0.2s;
&:hover {
transform: scale(1.1);
}
}
}
:deep(.el-table .cell) {
white-space: pre-line;
line-height: 1.5;
}
//
.action-btns {
display: flex;
justify-content: center;
gap: 8px;
.action-btn {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(64, 169, 255, 0.15);
border-radius: 2px;
color: #40a9ff;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.3);
transform: scale(1.1);
}
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<base-dialog
v-model:visible="props.visible"
:title="props.allCountyData.name + '建设责任人明细'"
:title="props.allCountyData.name + '建设项目责任人明细'"
:table-data="tableData"
:table-columns="tableColumns"
:table-height="400"
@ -22,8 +22,8 @@
</template>
<script setup>
import { ref, computed, watch } from "vue";
import baseDialog from "../component/baseDialog.vue";
import { ref, computed, watch } from 'vue';
import baseDialog from '../component/baseDialog.vue';
const props = defineProps({
visible: {
@ -33,32 +33,38 @@ const props = defineProps({
allCountyData: {
type: Object,
default: () => ({
name: "",
name: '',
}),
},
tongnanInfoItemData: {
type: Object,
default: () => ({
name: '',
}),
},
});
const emit = defineEmits(["update:visible", "close", "detail"]);
const emit = defineEmits(['update:visible', 'close', 'detail']);
//
const tableColumns = ref([
{ prop: "id", label: "序号", width: "" },
{ prop: "region", label: "区县/镇街", width: "" },
{ prop: "totalCount", label: "总人数", width: "" },
{ prop: "whistleblower", label: "吹哨人", width: "" },
{ prop: "constructionUnit", label: "建设单位包保责任人", width: "" },
{ prop: "contractorUnit", label: "施工单位包保责任人", width: "" },
{ prop: "stationed", label: "驻地包保责任人", width: "" },
{ prop: "districtLevel", label: "区县级包保责任人", width: "" },
{ prop: "cityLevel", label: "市级包保责任人", width: "" },
{ prop: "operation", label: "操作", width: "60px", slot: "operation" },
{ prop: 'id', label: '序号', width: '' },
{ prop: 'region', label: '区县/镇街', width: '' },
{ prop: 'totalCount', label: '总人数', width: '' },
{ prop: 'whistleblower', label: '吹哨人', width: '' },
{ prop: 'constructionUnit', label: '建设单位包保责任人', width: '' },
{ prop: 'contractorUnit', label: '施工单位包保责任人', width: '' },
{ prop: 'stationed', label: '驻地包保责任人', width: '' },
{ prop: 'districtLevel', label: '区县级包保责任人', width: '' },
{ prop: 'cityLevel', label: '市级包保责任人', width: '' },
{ prop: 'operation', label: '操作', width: '60px', slot: 'operation' },
]);
//
const tableData = ref([
{
id: 1,
region: "万州区",
region: '万州区',
totalCount: 6,
whistleblower: 2,
constructionUnit: 2,
@ -69,7 +75,7 @@ const tableData = ref([
},
{
id: 2,
region: "柏梓镇",
region: '柏梓镇',
totalCount: 6,
whistleblower: 2,
constructionUnit: 2,
@ -105,44 +111,44 @@ const visiblePages = computed(() => {
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
emit('update:visible', false);
emit('close');
};
// base-dialog
//
const handleDetail = (item) => {
emit("detail", item);
const handleDetail = item => {
emit('detail', item);
};
//
const handleSizeChange = (size) => {
const handleSizeChange = size => {
pageSize.value = size;
currentPage.value = 1;
fetchData();
};
const handleCurrentChange = (page) => {
const handleCurrentChange = page => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
console.log('获取第', currentPage.value, '页数据');
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
newVal => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
},
}
);
</script>

View File

@ -22,8 +22,8 @@
</template>
<script setup>
import { ref, computed, watch } from "vue";
import baseDialog from "../component/baseDialog.vue";
import { ref, computed, watch } from 'vue';
import baseDialog from '../component/baseDialog.vue';
const props = defineProps({
visible: {
@ -33,30 +33,30 @@ const props = defineProps({
allCountyData: {
type: Object,
default: () => ({
name: "",
name: '',
}),
},
});
const emit = defineEmits(["update:visible", "close", "view"]);
const emit = defineEmits(['update:visible', 'close', 'view', 'itemdata']);
//
const tableColumns = ref([
{ prop: "id", label: "序号", width: "60px" },
{ prop: "district", label: "区县", width: "100px" },
{ prop: "totalCount", label: "总人数", width: "80px" },
{ prop: "trafficDept", label: "交通主管部门责任人", width: "140px" },
{ prop: "roadOrg", label: "公路机构责任人", width: "120px" },
{ prop: "maintenance", label: "养护站道班责任人", width: "140px" },
{ prop: "roadKeeper", label: "护路员", width: "80px" },
{ prop: "operation", label: "操作", width: "auto", slot: "operation" },
{ prop: 'id', label: '序号', width: '60px' },
{ prop: 'district', label: '区县', width: '100px' },
{ prop: 'totalCount', label: '总人数', width: '80px' },
{ prop: 'trafficDept', label: '交通主管部门责任人', width: '140px' },
{ prop: 'roadOrg', label: '公路机构责任人', width: '120px' },
{ prop: 'maintenance', label: '养护站道班责任人', width: '140px' },
{ prop: 'roadKeeper', label: '护路员', width: '80px' },
{ prop: 'operation', label: '操作', width: 'auto', slot: 'operation' },
]);
//
const tableData = ref([
{
id: 1,
district: "潼南",
district: '潼南',
totalCount: 128,
trafficDept: 2,
roadOrg: 3,
@ -65,7 +65,7 @@ const tableData = ref([
},
{
id: 2,
district: "万州",
district: '万州',
totalCount: 96,
trafficDept: 1,
roadOrg: 2,
@ -74,7 +74,7 @@ const tableData = ref([
},
{
id: 3,
district: "沙坪坝",
district: '沙坪坝',
totalCount: 156,
trafficDept: 3,
roadOrg: 4,
@ -83,7 +83,7 @@ const tableData = ref([
},
{
id: 4,
district: "渝中",
district: '渝中',
totalCount: 64,
trafficDept: 1,
roadOrg: 2,
@ -117,44 +117,48 @@ const visiblePages = computed(() => {
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
emit('update:visible', false);
emit('close');
};
// base-dialog
//
const handleView = (item) => {
emit("view", item);
const handleView = item => {
emit('view');
emit('itemdata', {
...item,
allCountyData: props.allCountyData,
});
};
//
const handleSizeChange = (size) => {
const handleSizeChange = size => {
pageSize.value = size;
currentPage.value = 1;
fetchData();
};
const handleCurrentChange = (page) => {
const handleCurrentChange = page => {
currentPage.value = page;
fetchData();
};
//
const fetchData = () => {
console.log("获取第", currentPage.value, "页数据");
console.log('获取第', currentPage.value, '页数据');
// API
};
// visible
watch(
() => props.visible,
(newVal) => {
newVal => {
if (newVal) {
currentPage.value = 1;
fetchData();
}
},
}
);
</script>

View File

@ -34,7 +34,7 @@
<div class="filter-item">
<el-select :teleported="false"
v-model="filterForm.region"
placeholder="影响区域"
placeholder="行政区域"
class="filter-select"
>
<el-option
@ -128,7 +128,7 @@ const tableColumns = ref([
{ prop: "id", label: "序号", width: "" },
{ prop: "weatherSource", label: "气象来源", width: "" },
{ prop: "warningLevel", label: "预警等级", width: "", slot: "warningLevel" },
{ prop: "region", label: "影响区域", width: "" },
{ prop: "region", label: "行政区域", width: "" },
{ prop: "warningTime", label: "预警时间", width: "" },
{ prop: "endTime", label: "结束时间", width: "" },
{ prop: "impactPoints", label: "影响点数量", width: "" },

View File

@ -34,7 +34,7 @@
</el-select>
</div>
<div class="filter-item">
<span class="filter-label">影响区域</span>
<span class="filter-label">行政区域</span>
<el-select
v-model="filterForm.countyName"
placeholder="请选择"
@ -90,33 +90,25 @@
<!-- 预警等级列插槽 -->
<template #riskLeve="{ row }">
<span :class="['warning-level-tag', getWarningClass(row.riskLeve)]">{{
row.riskLeve
}}</span>
<span :class="['warning-level-tag', getWarningClass(row.riskLeve)]">{{ row.riskLeve }}</span>
</template>
<!-- 影响点数量列插槽 -->
<template #impactCount="{ row }">
<span class="impact-count" @click="handleImpactClick(row)">{{
row.impactCount
}}</span>
<span class="impact-count" @click="handleImpactClick(row)">{{ row.impactCount }}</span>
</template>
</base-dialog>
</template>
<script setup>
import { ref, computed, watch, onMounted, inject } from "vue";
import { Close, Calendar } from "@element-plus/icons-vue";
import {
warningLevelOptions,
regionOptions,
isEndedOptions,
} from "../component/index.js";
import baseDialog from "../component/baseDialog.vue";
import { request } from "@/utils/request";
import { ref, computed, watch, onMounted, inject } from 'vue';
import { Close, Calendar } from '@element-plus/icons-vue';
import { warningLevelOptions, regionOptions, isEndedOptions } from '../component/index.js';
import baseDialog from '../component/baseDialog.vue';
import { request } from '@/utils/request';
//
const getdateRange = inject("getdateRange", ref([]));
const getdateRange = inject('getdateRange', ref([]));
const props = defineProps({
visible: {
@ -129,7 +121,7 @@ const props = defineProps({
},
});
const emit = defineEmits(["update:visible", "close", "impactClick"]);
const emit = defineEmits(['update:visible', 'close', 'impactClick']);
//
const dateRange = ref([]);
@ -143,39 +135,39 @@ onMounted(() => {
// warningitem
watch(
() => props.warningitem,
(newVal) => {
console.log("warningitem 变化:", newVal);
newVal => {
console.log('warningitem 变化:', newVal);
if (newVal && Object.keys(newVal).length > 0) {
filterForm.value.riskLeve = newVal.label || "";
filterForm.value.riskLeve = newVal.label || '';
currentPage.value = 1;
}
},
{ deep: true },
{ deep: true }
);
//
watch(
() => getdateRange.value,
(newVal) => {
console.log("warningSituationDialog.vue 日期范围变化:", newVal);
newVal => {
console.log('warningSituationDialog.vue 日期范围变化:', newVal);
if (newVal && newVal.length === 2) {
dateRange.value = newVal;
filterForm.value.dateRange = newVal;
}
},
{ deep: true },
{ deep: true }
);
//
const filterForm = ref({
riskLeve: "",
countyName: "",
isEnded: "",
riskLeve: '',
countyName: '',
isEnded: '',
dateRange: dateRange.value,
});
//
const handleDateChange = (val) => {
const handleDateChange = val => {
filterForm.value.dateRange = val;
};
@ -193,28 +185,14 @@ const tableHeight = ref(300);
//
const tableColumns = ref([
{ prop: "index", label: "序号", width: "" },
{
prop: "riskLeve",
label: "预警等级",
width: "",
slot: "riskLeve",
},
{
label: "来源",
prop: "source",
width: "",
},
{ prop: "weatherType", label: "气象类型", width: "" },
{ prop: "countyName", label: "影响区域", width: "" },
{ prop: "warningTime", label: "生效时间", width: "" },
{ prop: "endTime", label: "失效时间", width: "" },
{
prop: "impactCount",
label: "影响点数量",
width: "",
slot: "impactCount",
},
{ prop: 'index', label: '序号', width: '' },
{ prop: 'riskLeve', label: '预警等级', width: '', slot: 'riskLeve' },
{ label: '来源', prop: 'source', width: '' },
{ prop: 'weatherType', label: '气象类型', width: '' },
{ prop: 'countyName', label: '影响区域', width: '' },
{ prop: 'warningTime', label: '生效时间', width: '' },
{ prop: 'endTime', label: '失效时间', width: '' },
{ prop: 'impactCount', label: '影响点数量', width: '', slot: 'impactCount' },
]);
//
@ -235,12 +213,12 @@ const fetchWarningData = async () => {
const params = {
offset: (currentPage.value - 1) * pageSize.value,
limit: pageSize.value,
start: "",
end: "",
riskLevel: "",
weatherType: "",
isExpire: "",
countyName: "",
start: '',
end: '',
riskLevel: '',
weatherType: '',
isExpire: '',
countyName: '',
};
//
@ -250,10 +228,7 @@ const fetchWarningData = async () => {
if (filterForm.value.countyName) {
params.countyName = filterForm.value.countyName;
}
if (
filterForm.value.isEnded !== undefined &&
filterForm.value.isEnded !== ""
) {
if (filterForm.value.isEnded !== undefined && filterForm.value.isEnded !== '') {
params.isExpire = filterForm.value.isEnded;
}
if (filterForm.value.dateRange && filterForm.value.dateRange.length === 2) {
@ -262,24 +237,24 @@ const fetchWarningData = async () => {
}
const res = await request({
url: "/snow-ops-platform/weather-warning/affected-count/_by_weather",
method: "GET",
url: '/snow-ops-platform/weather-warning/affected-count/_by_weather',
method: 'GET',
params,
});
if (res.code === "00000" && res.data) {
if (res.code === '00000' && res.data) {
//
const list = res.data.data || res.data.records || [];
total.value = res.data.total || 0;
tableData.value = list.map((item, index) => ({
index: index + 1,
riskLeve: item.riskLeve || "-",
weatherType: item.weatherType || "-",
countyName: item.countyName || "-",
source: item.source || "-",
warningTime: item.startTime || "-",
endTime: item.endTime || "-",
riskLeve: item.riskLeve || '-',
weatherType: item.weatherType || '-',
countyName: item.countyName || '-',
source: item.source || '-',
warningTime: item.startTime || '-',
endTime: item.endTime || '-',
impactCount: item.affectedCount || 0,
}));
} else {
@ -287,7 +262,7 @@ const fetchWarningData = async () => {
total.value = 0;
}
} catch (error) {
console.error("获取预警数据失败:", error);
console.error('获取预警数据失败:', error);
tableData.value = [];
total.value = 0;
} finally {
@ -296,55 +271,55 @@ const fetchWarningData = async () => {
};
//
const formatDateTime = (date) => {
if (!date) return "";
const formatDateTime = date => {
if (!date) return '';
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
const hours = String(d.getHours()).padStart(2, "0");
const minutes = String(d.getMinutes()).padStart(2, "0");
const seconds = String(d.getSeconds()).padStart(2, "0");
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
const hours = String(d.getHours()).padStart(2, '0');
const minutes = String(d.getMinutes()).padStart(2, '0');
const seconds = String(d.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
//
const getWarningClass = (level) => {
const getWarningClass = level => {
const classMap = {
红色预警: "warning-red",
橙色预警: "warning-orange",
黄色预警: "warning-yellow",
蓝色预警: "warning-blue",
红色预警: 'warning-red',
橙色预警: 'warning-orange',
黄色预警: 'warning-yellow',
蓝色预警: 'warning-blue',
};
return classMap[level] || "";
return classMap[level] || '';
};
//
const handleClose = () => {
emit("update:visible", false);
emit("close");
emit('update:visible', false);
emit('close');
};
//
const handleImpactClick = (item) => {
emit("impactClick", item);
emit("impactClickItem", {
const handleImpactClick = item => {
emit('impactClick', item);
emit('impactClickItem', {
...item,
dateRange: filterForm.value.dateRange || [],
riskLeve: filterForm.value.riskLeve || "",
riskLeve: filterForm.value.riskLeve || '',
});
};
//
const handleSizeChange = (val) => {
const handleSizeChange = val => {
pageSize.value = val;
console.log("分页大小变化:", val);
console.log('分页大小变化:', val);
currentPage.value = 1;
fetchWarningData();
};
const handleCurrentChange = (val) => {
console.log("当前页码变化:", val);
const handleCurrentChange = val => {
console.log('当前页码变化:', val);
currentPage.value = val;
fetchWarningData();
};
@ -359,23 +334,23 @@ const initDialogData = () => {
// warningitem
if (props.warningitem && Object.keys(props.warningitem).length > 0) {
filterForm.value = {
riskLeve: props.warningitem.label || "",
countyName: props.warningitem.countyName || "",
isEnded: "",
riskLeve: props.warningitem.label || '',
countyName: props.warningitem.countyName || '',
isEnded: '',
dateRange: dateRange.value,
};
} else {
filterForm.value = {
riskLeve: "",
countyName: "",
isEnded: "",
riskLeve: '',
countyName: '',
isEnded: '',
dateRange: dateRange.value,
};
}
//
currentPage.value = 1;
console.log("初始化筛选条件:", filterForm.value);
console.log('初始化筛选条件:', filterForm.value);
fetchWarningData();
};
@ -387,9 +362,9 @@ const resetData = () => {
currentPage.value = 1;
//
filterForm.value = {
riskLeve: "",
countyName: "",
isEnded: "",
riskLeve: '',
countyName: '',
isEnded: '',
dateRange: [],
};
//
@ -399,7 +374,7 @@ const resetData = () => {
// visible
watch(
() => props.visible,
(newVal) => {
newVal => {
if (newVal) {
//
initDialogData();
@ -407,38 +382,38 @@ watch(
//
resetData();
}
},
}
);
//
watch(
() => filterForm.value,
(newVal, oldVal) => {
console.log("筛选条件变化:===========", newVal, oldVal);
console.log('筛选条件变化:===========', newVal, oldVal);
//
if (oldVal && Object.keys(oldVal).length > 0) {
currentPage.value = 1;
fetchWarningData();
}
},
{ deep: true },
{ deep: true }
);
// dateRange filterForm.dateRange
watch(
() => dateRange.value,
(newVal) => {
newVal => {
filterForm.value.dateRange = newVal;
},
{ deep: true },
{ deep: true }
);
watch(
() => filterForm.value.dateRange,
(newVal) => {
newVal => {
dateRange.value = newVal;
},
{ deep: true },
{ deep: true }
);
</script>

View File

@ -8,13 +8,15 @@
:class="{ active: activeIndex === index }"
@click="handleClick(item, index)"
>
<div class="nav-icon-box" :ref="(el) => setNavIconRef(el, index)">
<div class="nav-icon-box" :ref="el => setNavIconRef(el, index)">
<!-- <i :class="item.icon"></i> -->
<img :src="activeIndex === index ? item.icon1 : item.icon" alt="" />
</div>
<div class="nav-label">{{ item.label }}</div>
</div>
</div>
<!-- 气象预警监测表格组件 -->
<WeatherWarningTable ref="weatherWarningTableRef" @clearFilters="handleClearFilters" />
<!-- 涉灾隐患点图片弹窗 -->
<div v-if="showHazardPopup" class="hazard-popup" :style="popupStyle">
<div class="hazard-popup-content">
@ -30,11 +32,7 @@
</div>
</div>
<!-- 路段图片弹窗 -->
<div
v-if="showRoadPopup"
class="hazard-popup road-popup"
:style="roadPopupStyle"
>
<div v-if="showRoadPopup" class="hazard-popup road-popup" :style="roadPopupStyle">
<div class="hazard-popup-content">
<div
v-for="(item, index) in roadItems"
@ -47,172 +45,98 @@
</div>
</div>
</div>
<!-- 气象预警监测表格 -->
<div class="weather-warning-wrapper">
<div class="weather-warning-panel">
<img
class="clear-icon"
src="../../assets/RiskWarning_img/清除icon@2x.png"
alt=""
@click="clearFilters"
/>
<div class="panel-header">
<div class="header-title">气象预警监测</div>
<div class="filter-tags">
<label class="tag">
<input type="checkbox" v-model="filters.red" />
<span class="">红色预警</span>
</label>
<label class="tag">
<input type="checkbox" v-model="filters.blue" />
<span class="">蓝色预警</span>
</label>
<label class="tag">
<input type="checkbox" v-model="filters.orange" />
<span class="">橙色预警</span>
</label>
<label class="tag">
<input type="checkbox" v-model="filters.yellow" />
<span class="">黄色预警</span>
</label>
</div>
</div>
<div class="table-container">
<el-table
:data="filteredData"
height="100%"
style="width: 100%; background: transparent"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
:row-class-name="rowClassName"
>
<el-table-column prop="time" label="预警时间" min-width="vw(140)" />
<el-table-column
prop="type"
label="类型"
min-width="vw(80)"
align="center"
/>
<el-table-column
label="预警等级"
min-width="vw(100)"
align="center"
>
<template #default="{ row }">
<div class="warning-level">
<img
:src="
row.levelClass === 'red'
? redIcon
: row.levelClass === 'blue'
? blueIcon
: row.levelClass === 'orange'
? orangeIcon
: yellowIcon
"
alt=""
/>
<span class="ml_10">{{ row.level }}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="district"
label="预警区县"
min-width="vw(80)"
/>
</el-table>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue";
import { ElTable } from "element-plus";
import orangeIcon from "../../assets/RiskWarning_img/橙色@2x.png";
import yellowIcon from "../../assets/RiskWarning_img/黄色@2x.png";
import redIcon from "../../assets/RiskWarning_img//红色@2x.png";
import blueIcon from "../../assets/RiskWarning_img/蓝色@2x.png";
import { ref, computed, nextTick } from 'vue';
import WeatherWarningTable from './component/WeatherWarningTable.vue';
import warningIconIcon from "../../assets/RiskWarning_img/风险预警icon@2x.png";
import tunnelIconIcon from "../../assets/RiskWarning_img/隧道icon@2x.png";
import slopeIconIcon from "../../assets/RiskWarning_img/边坡icon@2x.png";
import bridgeIconIcon from "../../assets/RiskWarning_img/桥梁icon@2x.png";
import roadIconIcon from "../../assets/RiskWarning_img/线路路段icon@2x.png";
import teamIconIcon from "../../assets/RiskWarning_img/队伍icon@2x.png";
import hazardIconIconIcon from "../../assets/RiskWarning_img/隐患点icon@2x.png";
import warningIconIcon from '../../assets/RiskWarning_img/风险预警icon@2x.png';
import tunnelIconIcon from '../../assets/RiskWarning_img/隧道icon@2x.png';
import slopeIconIcon from '../../assets/RiskWarning_img/边坡icon@2x.png';
import bridgeIconIcon from '../../assets/RiskWarning_img/桥梁icon@2x.png';
import roadIconIcon from '../../assets/RiskWarning_img/线路路段icon@2x.png';
import teamIconIcon from '../../assets/RiskWarning_img/队伍icon@2x.png';
import hazardIconIconIcon from '../../assets/RiskWarning_img/隐患点icon@2x.png';
import warningIconIcon1 from "../../assets/RiskWarning_img/风险预警icon1@2x.png";
import tunnelIconIcon1 from "../../assets/RiskWarning_img/隧道icon1@2x.png";
import slopeIconIcon1 from "../../assets/RiskWarning_img/边坡icon1@2x.png";
import bridgeIconIcon1 from "../../assets/RiskWarning_img/桥梁icon1@2x.png";
import roadIconIcon1 from "../../assets/RiskWarning_img/线路路段icon1@2x.png";
import teamIconIcon1 from "../../assets/RiskWarning_img/队伍icon1@2x.png";
import hazardIconIcon from "../../assets/RiskWarning_img/隐患点icon1@2x.png";
import warningIconIcon1 from '../../assets/RiskWarning_img/风险预警icon1@2x.png';
import tunnelIconIcon1 from '../../assets/RiskWarning_img/隧道icon1@2x.png';
import slopeIconIcon1 from '../../assets/RiskWarning_img/边坡icon1@2x.png';
import bridgeIconIcon1 from '../../assets/RiskWarning_img/桥梁icon1@2x.png';
import roadIconIcon1 from '../../assets/RiskWarning_img/线路路段icon1@2x.png';
import teamIconIcon1 from '../../assets/RiskWarning_img/队伍icon1@2x.png';
import hazardIconIcon from '../../assets/RiskWarning_img/隐患点icon1@2x.png';
import hazardIconIcon1 from "../../assets/MaMap_img/一般路内隐患点@2x.png";
import hazardIconIcon2 from "../../assets/MaMap_img/一般路外隐患点@2x.png";
import hazardIconIcon3 from "../../assets/MaMap_img/较大路内隐患点@2x.png";
import hazardIconIcon4 from "../../assets/MaMap_img/较大路外隐患点@2x.png";
import hazardIconIcon5 from "../../assets/MaMap_img/重大路内隐患点@2x.png";
import hazardIconIcon6 from "../../assets/MaMap_img/重大路外隐患点@2x.png";
import hazardIconIcon1 from '../../assets/MaMap_img/一般路内隐患点@2x.png';
import hazardIconIcon2 from '../../assets/MaMap_img/一般路外隐患点@2x.png';
import hazardIconIcon3 from '../../assets/MaMap_img/较大路内隐患点@2x.png';
import hazardIconIcon4 from '../../assets/MaMap_img/较大路外隐患点@2x.png';
import hazardIconIcon5 from '../../assets/MaMap_img/重大路内隐患点@2x.png';
import hazardIconIcon6 from '../../assets/MaMap_img/重大路外隐患点@2x.png';
import tunnelLineIcon3 from "../../assets/MaMap_img/高风险路段@2x.png";
import tunnelLineIcon2 from "../../assets/MaMap_img/较高风险路段@2x.png";
import tunnelLineIcon1 from "../../assets/MaMap_img/中风险路段@2x.png";
import tunnelLineIcon from "../../assets/MaMap_img/线路icon定位@2x.png";
import tunnelLineIcon3 from '../../assets/MaMap_img/高风险路段@2x.png';
import tunnelLineIcon2 from '../../assets/MaMap_img/较高风险路段@2x.png';
import tunnelLineIcon1 from '../../assets/MaMap_img/中风险路段@2x.png';
import tunnelLineIcon from '../../assets/MaMap_img/线路icon定位@2x.png';
const emit = defineEmits([
'changeActiveIndex',
'clearMapMarkers',
'hazardItemClick',
'roadItemClick',
'showHazardPopupfn',
]);
const activeIndex = ref(-1);
const menuItems = [
{
label: "涉灾隐患点",
icon: "icon-hazard",
iconClass: "hazard",
label: '涉灾隐患点',
icon: 'icon-hazard',
iconClass: 'hazard',
icon: hazardIconIconIcon,
icon1: hazardIconIcon,
},
{
label: "路段",
icon: "icon-road",
iconClass: "road",
label: '路段',
icon: 'icon-road',
iconClass: 'road',
icon: roadIconIcon,
icon1: roadIconIcon1,
},
{
label: "项目",
icon: "icon-warning",
iconClass: "warning",
label: '项目',
icon: 'icon-warning',
iconClass: 'warning',
icon: warningIconIcon,
icon1: warningIconIcon1,
},
{
label: "隧道",
icon: "icon-tunnel",
iconClass: "tunnel",
label: '隧道',
icon: 'icon-tunnel',
iconClass: 'tunnel',
icon: tunnelIconIcon,
icon1: tunnelIconIcon1,
},
{
label: "边坡",
icon: "icon-slope",
iconClass: "slope",
label: '边坡',
icon: 'icon-slope',
iconClass: 'slope',
icon: slopeIconIcon,
icon1: slopeIconIcon1,
},
{
label: "桥梁",
icon: "icon-bridge",
iconClass: "bridge",
label: '桥梁',
icon: 'icon-bridge',
iconClass: 'bridge',
icon: bridgeIconIcon,
icon1: bridgeIconIcon1,
},
{
label: "队伍",
icon: "icon-team",
iconClass: "team",
label: '队伍',
icon: 'icon-team',
iconClass: 'team',
icon: teamIconIcon,
icon1: teamIconIcon1,
},
@ -225,12 +149,12 @@ const navIconRefs = ref([]);
//
const hazardItems = ref([
{ icon: hazardIconIcon5, label: "重大路内隐患点" },
{ icon: hazardIconIcon6, label: "重大路外隐患点" },
{ icon: hazardIconIcon3, label: "较大路内隐患点" },
{ icon: hazardIconIcon4, label: "较大路外隐患点" },
{ icon: hazardIconIcon1, label: "一般路内隐患点" },
{ icon: hazardIconIcon2, label: "一般路外隐患点" },
{ icon: hazardIconIcon5, label: '重大路内隐患点', isWithinRedLine: '是' },
{ icon: hazardIconIcon6, label: '重大路外隐患点', isWithinRedLine: '否' },
{ icon: hazardIconIcon3, label: '较大路内隐患点', isWithinRedLine: '是' },
{ icon: hazardIconIcon4, label: '较大路外隐患点', isWithinRedLine: '否' },
{ icon: hazardIconIcon1, label: '一般路内隐患点', isWithinRedLine: '是' },
{ icon: hazardIconIcon2, label: '一般路外隐患点', isWithinRedLine: '否' },
]);
//
@ -239,10 +163,10 @@ const roadPopupStyle = ref({});
//
const roadItems = ref([
{ icon: tunnelLineIcon3, label: "高风险路段" },
{ icon: tunnelLineIcon2, label: "较高风险路段" },
{ icon: tunnelLineIcon1, label: "中风险路段" },
{ icon: tunnelLineIcon, label: "低风险路段" },
{ icon: tunnelLineIcon3, label: '高风险路段' },
{ icon: tunnelLineIcon2, label: '较高风险路段' },
{ icon: tunnelLineIcon1, label: '中风险路段' },
{ icon: tunnelLineIcon, label: '低风险路段' },
]);
// nav-icon-boxref
@ -251,94 +175,12 @@ const setNavIconRef = (el, index) => {
navIconRefs.value[index] = el;
}
};
const weatherWarningTableRef = ref(null);
//
const filters = ref({
red: false,
blue: false,
orange: false,
yellow: false,
});
//
const warningData = ref([
{
time: "2025-11-18 09:24:81",
type: "暴雨",
level: "蓝色预警",
levelClass: "blue",
district: "合川区",
},
{
time: "2025-11-12 09:24:81",
type: "冰雪",
level: "红色预警",
levelClass: "red",
district: "万州区",
},
{
time: "2025-11-12 09:24:81",
type: "大雾",
level: "橙色预警",
levelClass: "orange",
district: "涪陵区",
},
{
time: "2025-11-12 09:24:81",
type: "大风",
level: "黄色预警",
levelClass: "yellow",
district: "城口县",
},
]);
//
const filteredData = computed(() => {
const hasFilter =
filters.value.red ||
filters.value.blue ||
filters.value.orange ||
filters.value.yellow;
if (!hasFilter) return warningData.value;
return warningData.value.filter((item) => {
if (filters.value.red && item.levelClass === "red") return true;
if (filters.value.blue && item.levelClass === "blue") return true;
if (filters.value.orange && item.levelClass === "orange") return true;
if (filters.value.yellow && item.levelClass === "yellow") return true;
return false;
});
});
// el-table
const headerCellStyle = () => ({
background: "#17466F",
color: "rgba(255, 255, 255, 0.6)",
fontWeight: "normal",
borderBottom: "1px solid rgba(64, 169, 255, 0.2)",
padding: "5px 20px",
});
const cellStyle = () => ({
background: "#142E49",
color: "rgba(255, 255, 255, 0.9)",
borderBottom: "1px solid rgba(64, 169, 255, 0.1)",
padding: "5px 5px",
});
const rowClassName = ({ rowIndex }) => {
return rowIndex % 2 === 0 ? "even-row" : "odd-row";
};
const emit = defineEmits([
"changeActiveIndex",
"clearMapMarkers",
"hazardItemClick",
"roadItemClick",
]);
//
const handleClick = (item, index) => {
//
if (item.label === "涉灾隐患点") {
if (item.label === '涉灾隐患点') {
//
showHazardPopup.value = !showHazardPopup.value;
if (showHazardPopup.value) {
@ -350,14 +192,14 @@ const handleClick = (item, index) => {
if (navIconBox) {
const rect = navIconBox.getBoundingClientRect();
popupStyle.value = {
position: "fixed",
left: rect.right + 10 + "px",
top: rect.top + "px",
position: 'fixed',
left: rect.right + 10 + 'px',
top: rect.top + 'px',
zIndex: 2,
};
}
}
} else if (item.label === "路段") {
} else if (item.label === '路段') {
//
//
@ -372,165 +214,59 @@ const handleClick = (item, index) => {
if (navIconBox) {
const rect = navIconBox.getBoundingClientRect();
roadPopupStyle.value = {
position: "fixed",
left: rect.right + 10 + "px",
top: rect.top + "px",
zIndex: 3,
position: 'fixed',
left: rect.right + 10 + 'px',
top: rect.top + 'px',
zIndex: 2,
};
}
}
emit('showHazardPopupfn', false);
} else {
showHazardPopup.value = false;
showRoadPopup.value = false;
emit('showHazardPopupfn', false);
//
emit('hideRoadStats');
}
activeIndex.value = index;
emit("changeActiveIndex", {
emit('changeActiveIndex', {
...item,
});
};
//
const handleHazardItemClick = (item) => {
console.log("点击隐患点:", item);
emit("hazardItemClick", item);
const handleHazardItemClick = item => {
console.log('点击隐患点:', item);
emit('hazardItemClick', item);
//
// showHazardPopup.value = false;
emit('showHazardPopupfn', true);
};
const roadItem = ref({});
//
const handleRoadItemClick = (item) => {
console.log("点击路段:", item);
const handleRoadItemClick = item => {
console.log('点击路段:', item);
roadItem.value = item;
emit("roadItemClick", item);
emit('roadItemClick', item);
//
// showRoadPopup.value = false;
};
//
const clearFilters = () => {
filters.value = {
red: false,
blue: false,
orange: false,
yellow: false,
};
//
const handleClearFilters = () => {
console.log('清除筛选条件');
//
activeIndex.value = -1;
//
showHazardPopup.value = false;
showRoadPopup.value = false;
//
emit("clearMapMarkers");
emit('clearMapMarkers');
emit('showHazardPopupfn', false);
};
//
const tableRef = ref(null);
const isScrolling = ref(true);
const scrollTimer = ref(null);
const scrollSpeed = 50; //
const scrollStep = 1; //
//
const hasFilter = computed(() => {
return (
filters.value.red ||
filters.value.blue ||
filters.value.orange ||
filters.value.yellow
);
});
//
const startAutoScroll = () => {
if (scrollTimer.value) {
clearInterval(scrollTimer.value);
}
if (!isScrolling.value || hasFilter.value) return;
const tableBody = document.querySelector(
".weather-warning-panel .el-table__body-wrapper .el-scrollbar__wrap",
);
if (!tableBody) return;
scrollTimer.value = setInterval(() => {
if (tableBody) {
tableBody.scrollTop += scrollStep;
// 使 >=
const maxScroll = tableBody.scrollHeight - tableBody.clientHeight;
if (tableBody.scrollTop >= maxScroll - 2) {
tableBody.scrollTop = 0;
}
}
}, scrollSpeed);
};
//
const stopAutoScroll = () => {
if (scrollTimer.value) {
clearInterval(scrollTimer.value);
scrollTimer.value = null;
}
};
//
const resetScrollToTop = () => {
const tableBody = document.querySelector(
".weather-warning-panel .el-table__body-wrapper .el-scrollbar__wrap",
);
if (tableBody) {
tableBody.scrollTop = 0;
}
};
//
const handleMouseEnter = () => {
stopAutoScroll();
};
//
const handleMouseLeave = () => {
if (!hasFilter.value) {
isScrolling.value = true;
startAutoScroll();
}
};
//
watch(hasFilter, (newVal) => {
if (newVal) {
//
stopAutoScroll();
resetScrollToTop();
} else {
//
isScrolling.value = true;
nextTick(() => {
startAutoScroll();
});
}
});
onMounted(() => {
nextTick(() => {
startAutoScroll();
//
const tableContainer = document.querySelector(
".weather-warning-panel .table-container",
);
if (tableContainer) {
tableContainer.addEventListener("mouseenter", handleMouseEnter);
tableContainer.addEventListener("mouseleave", handleMouseLeave);
}
});
});
onUnmounted(() => {
stopAutoScroll();
const tableContainer = document.querySelector(
".weather-warning-panel .table-container",
);
if (tableContainer) {
tableContainer.removeEventListener("mouseenter", handleMouseEnter);
tableContainer.removeEventListener("mouseleave", handleMouseLeave);
}
});
</script>
<style lang="scss" scoped>
@ -653,27 +389,27 @@ onUnmounted(() => {
// 使 emoji
&.warning::before {
content: "📍";
content: '📍';
font-size: vw(24);
}
&.tunnel::before {
content: "🚇";
content: '🚇';
font-size: vw(24);
}
&.slope::before {
content: "⛰️";
content: '⛰️';
font-size: vw(24);
}
&.bridge::before {
content: "🌉";
content: '🌉';
font-size: vw(24);
}
&.road::before {
content: "🛣️";
content: '🛣️';
font-size: vw(24);
}
}
@ -688,198 +424,4 @@ onUnmounted(() => {
}
}
}
//
.weather-warning-wrapper {
display: flex;
flex-direction: column;
justify-content: flex-end;
width: 92%;
height: 50%;
position: absolute;
z-index: 2;
right: 0px;
bottom: 0px;
}
//
.weather-warning-panel {
// border-radius: vw(8);
display: flex;
flex-direction: column;
height: 100%;
.clear-icon {
position: absolute;
top: vw(-50);
right: 0px;
width: vw(40) !important;
height: vw(40) !important;
min-width: vw(28);
min-height: vw(28);
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
background-image: url("../../assets/RiskWarning_img/二级标题栏bg1@2x.png");
background-size: 100% 100%;
background-position: left top;
padding: vw(15);
.header-title {
font-size: vw(18);
font-weight: 500;
color: #fff;
position: relative;
}
.filter-tags {
display: flex;
gap: vw(15);
.tag {
display: flex;
align-items: center;
gap: vw(5);
cursor: pointer;
input {
width: vw(14);
height: vw(14);
min-width: vw(12);
min-height: vw(12);
accent-color: #40a9ff;
}
span {
font-size: vw(14);
color: rgba(255, 255, 255, 0.8);
&.tag-red {
color: #ff4d4f;
}
&.tag-blue {
color: #40a9ff;
}
&.tag-orange {
color: #ff7a45;
}
&.tag-yellow {
color: #ffc53d;
}
}
}
}
}
.table-container {
background: rgb(20, 46, 74, 0.95);
flex: 1;
overflow: hidden;
:deep(.el-table__body-wrapper) {
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
}
// el-table
:deep(.el-table) {
background: transparent;
&::before {
display: none;
}
.el-table__inner-wrapper {
background: transparent;
}
.el-table__inner-wrapper:before {
width: 0%;
}
.el-table__header-wrapper {
background: transparent;
th.el-table__cell {
background: transparent;
font-size: vw(14);
color: rgba(255, 255, 255, 0.6);
padding: vw(10) vw(8);
}
}
.el-table__body-wrapper {
background: transparent;
td.el-table__cell {
background: transparent;
font-size: vw(14);
color: rgba(255, 255, 255, 0.9);
padding: vw(5) vw(5);
}
&::-webkit-scrollbar {
display: none !important;
}
}
tr {
background: transparent;
&:hover {
background: rgba(64, 169, 255, 0.05) !important;
}
}
}
.warning-level {
display: flex;
align-items: center;
justify-content: center;
img {
width: vw(15);
height: vw(15);
min-width: vw(10);
min-height: vw(10);
}
// .level-icon {
// width: 0;
// height: 0;
// border-left: 6px solid transparent;
// border-right: 6px solid transparent;
// border-bottom: 10px solid currentColor;
// }
&.red {
color: #ff4d4f;
background: rgba(255, 77, 79, 0.1);
}
&.blue {
color: #40a9ff;
background: rgba(64, 169, 255, 0.1);
}
&.orange {
color: #ff7a45;
background: rgba(255, 122, 69, 0.1);
}
&.yellow {
color: #ffc53d;
background: rgba(255, 197, 61, 0.1);
}
}
}
</style>

View File

@ -0,0 +1,448 @@
<template>
<!-- 气象预警监测表格 -->
<div class="weather-warning-wrapper">
<div class="weather-warning-panel">
<img
class="clear-icon"
src="../../../assets/RiskWarning_img/清除icon@2x.png"
alt=""
@click="clearFilters"
/>
<div class="panel-header">
<div class="header-title">气象预警监测</div>
<div class="filter-tags">
<label class="tag">
<input type="checkbox" v-model="filters.red" />
<span class="">红色预警</span>
</label>
<label class="tag">
<input type="checkbox" v-model="filters.blue" />
<span class="">蓝色预警</span>
</label>
<label class="tag">
<input type="checkbox" v-model="filters.orange" />
<span class="">橙色预警</span>
</label>
<label class="tag">
<input type="checkbox" v-model="filters.yellow" />
<span class="">黄色预警</span>
</label>
</div>
</div>
<div class="table-container">
<el-table
v-loading="loading"
element-loading-text="加载中..."
element-loading-background="#1A3E5E"
:data="filteredData"
height="100%"
style="width: 100%; background: transparent"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
:row-class-name="rowClassName"
>
<el-table-column prop="time" label="预警时间" min-width="vw(140)" />
<el-table-column prop="type" label="类型" min-width="vw(80)" align="center" />
<el-table-column label="预警等级" min-width="vw(100)" align="center">
<template #default="{ row }">
<div class="warning-level">
<img
:src="
row.levelClass === 'red'
? redIcon
: row.levelClass === 'blue'
? blueIcon
: row.levelClass === 'orange'
? orangeIcon
: yellowIcon
"
alt=""
/>
<span class="ml_10">{{ row.level }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="district" label="预警区县" min-width="vw(80)" />
</el-table>
</div>
</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue';
import { request } from '@/utils/request';
import orangeIcon from '../../../assets/RiskWarning_img/橙色@2x.png';
import yellowIcon from '../../../assets/RiskWarning_img/黄色@2x.png';
import redIcon from '../../../assets/RiskWarning_img//红色@2x.png';
import blueIcon from '../../../assets/RiskWarning_img/蓝色@2x.png';
const emit = defineEmits(['clearFilters']);
//
const filters = ref({
red: false,
blue: false,
orange: false,
yellow: false,
});
//
const warningData = ref([]);
//
const tableRef = ref(null);
const isScrolling = ref(true);
const scrollTimer = ref(null);
const scrollSpeed = 50; //
const scrollStep = 1; //
//
const hasFilter = computed(() => {
return filters.value.red || filters.value.blue || filters.value.orange || filters.value.yellow;
});
//
const startAutoScroll = () => {
if (scrollTimer.value) {
clearInterval(scrollTimer.value);
}
if (!isScrolling.value || hasFilter.value) return;
const tableBody = document.querySelector(
'.weather-warning-panel .el-table__body-wrapper .el-scrollbar__wrap'
);
if (!tableBody) return;
scrollTimer.value = setInterval(() => {
if (tableBody) {
tableBody.scrollTop += scrollStep;
// 使 >=
const maxScroll = tableBody.scrollHeight - tableBody.clientHeight;
if (tableBody.scrollTop >= maxScroll - 2) {
tableBody.scrollTop = 0;
}
}
}, scrollSpeed);
};
//
const stopAutoScroll = () => {
if (scrollTimer.value) {
clearInterval(scrollTimer.value);
scrollTimer.value = null;
}
};
//
const resetScrollToTop = () => {
const tableBody = document.querySelector(
'.weather-warning-panel .el-table__body-wrapper .el-scrollbar__wrap'
);
if (tableBody) {
tableBody.scrollTop = 0;
}
};
//
const handleMouseEnter = () => {
stopAutoScroll();
};
//
const handleMouseLeave = () => {
if (!hasFilter.value) {
isScrolling.value = true;
startAutoScroll();
}
};
//
watch(hasFilter, newVal => {
if (newVal) {
//
stopAutoScroll();
resetScrollToTop();
} else {
//
isScrolling.value = true;
nextTick(() => {
startAutoScroll();
});
}
});
//
const loading = ref(false);
//
const fetchWeatherWarningData = async () => {
loading.value = true;
try {
const res = await request({
url: '/snow-ops-platform/weather-warning/affected-count/_by_weather',
method: 'GET',
params: {
offset: 0,
limit: 100000,
},
});
console.log('气象预警数据:', res);
if (res.code === '00000' && res.data) {
//
warningData.value = res.data.data.map(item => ({
time: item.startTime || '',
type: item.weatherType || '',
level: item.riskLeve || '',
levelClass: getLevelClass(item.riskLeve || ''),
district: item.countyName || '',
}));
} else {
warningData.value = [];
}
loading.value = false;
nextTick(() => {
startAutoScroll();
//
const tableContainer = document.querySelector('.weather-warning-panel .table-container');
if (tableContainer) {
tableContainer.addEventListener('mouseenter', handleMouseEnter);
tableContainer.addEventListener('mouseleave', handleMouseLeave);
}
});
} catch (error) {
console.error('获取气象预警数据失败:', error);
warningData.value = [];
loading.value = false;
}
};
// class
const getLevelClass = level => {
if (level.includes('红')) return 'red';
if (level.includes('橙')) return 'orange';
if (level.includes('黄')) return 'yellow';
if (level.includes('蓝')) return 'blue';
return '';
};
//
onMounted(() => {
//
console.log('获取气象预警数据');
fetchWeatherWarningData();
});
onUnmounted(() => {
stopAutoScroll();
const tableContainer = document.querySelector('.weather-warning-panel .table-container');
if (tableContainer) {
tableContainer.removeEventListener('mouseenter', handleMouseEnter);
tableContainer.removeEventListener('mouseleave', handleMouseLeave);
}
});
//
const filteredData = computed(() => {
const hasFilter =
filters.value.red || filters.value.blue || filters.value.orange || filters.value.yellow;
if (!hasFilter) return warningData.value;
return warningData.value.filter(item => {
if (filters.value.red && item.levelClass === 'red') return true;
if (filters.value.blue && item.levelClass === 'blue') return true;
if (filters.value.orange && item.levelClass === 'orange') return true;
if (filters.value.yellow && item.levelClass === 'yellow') return true;
return false;
});
});
// el-table
const headerCellStyle = () => ({
background: '#17466F',
color: 'rgba(255, 255, 255, 0.6)',
});
const cellStyle = () => ({
background: '#142E49',
color: 'rgba(255, 255, 255, 0.9)',
borderBottom: '1px solid rgba(64, 169, 255, 0.1)',
padding: '5px 5px',
});
const rowClassName = ({ rowIndex }) => {
return rowIndex % 2 === 0 ? 'even-row' : 'odd-row';
};
//
const clearFilters = () => {
console.log('清除筛选条件');
filters.value = {
red: false,
blue: false,
orange: false,
yellow: false,
};
//
emit('clearFilters');
};
//
const setWarningData = data => {
warningData.value = data;
};
defineExpose({
clearFilters,
setWarningData,
});
</script>
<style lang="scss" scoped>
@function vw($px) {
@return calc($px / 1920 * 100vw);
}
.weather-warning-wrapper {
position: fixed;
bottom: vw(0);
left: 52%;
transform: translateX(-50%);
z-index: 2;
}
.weather-warning-panel {
width: vw(900);
// background: url('../../../assets/RiskWarning_img/@2x.png') no-repeat center center;
// background-size: 100% 100%;
padding: vw(15);
position: relative;
.clear-icon {
position: absolute;
top: vw(-40);
right: vw(15);
width: vw(50);
height: vw(50);
cursor: pointer;
z-index: 10;
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
background-image: url('../../../assets/RiskWarning_img/二级标题栏bg1@2x.png');
background-size: 100% 100%;
background-position: left top;
padding: vw(15);
.header-title {
font-size: vw(18);
font-weight: 500;
color: #fff;
}
.filter-tags {
display: flex;
gap: vw(10);
.tag {
display: flex;
align-items: center;
gap: vw(5);
cursor: pointer;
font-size: vw(14);
color: rgba(255, 255, 255, 0.8);
input[type='checkbox'] {
width: vw(14);
height: vw(14);
cursor: pointer;
}
}
}
}
.table-container {
height: vw(130);
overflow: hidden;
:deep(.el-table) {
background: transparent;
.el-table__header-wrapper {
th {
background: #17466f;
color: rgba(255, 255, 255, 0.6);
font-weight: normal;
border-bottom: 1px solid rgba(64, 169, 255, 0.2);
}
}
.el-table__body-wrapper {
background: transparent;
tr {
background: transparent;
td {
background: #142e49;
color: rgba(255, 255, 255, 0.9);
border-bottom: 1px solid rgba(64, 169, 255, 0.1);
}
&.even-row {
td {
background: #142e49;
}
}
&.odd-row {
td {
background: #1a3a5c;
}
}
&:hover {
td {
background: rgba(64, 169, 255, 0.2);
}
}
}
}
}
}
}
.warning-level {
display: flex;
align-items: center;
justify-content: center;
gap: vw(5);
img {
width: vw(20);
height: vw(20);
}
}
.ml_10 {
margin-left: vw(10);
}
:deep(.el-table--fit .el-table__inner-wrapper:before) {
width: 0%;
}
:deep(.el-scrollbar__wrap--hidden-default) {
background: #142e49;
}
:deep(.el-table .cell) {
text-align: center;
}
:deep(.el-table:not(.el-table--border) .el-table__cell) {
font-size: vw(14);
padding: 0;
}
:deep(.el-table .cell) {
font-size: vw(16);
}
</style>

View File

@ -1,32 +1,74 @@
// 弹窗组件统一数据配置
import { ref } from 'vue';
import { request } from '@/utils/request';
// 行政区域选项
export const regionOptions = [
{ label: "重庆市", value: "重庆市" },
{ label: "万州区", value: "万州区" },
{ label: "沙坪坝区", value: "沙坪坝区" },
{ label: "渝中区", value: "渝中区" },
{ label: "巫溪县", value: "巫溪县" },
{ label: "涪陵区", value: "涪陵区" },
{ label: "合川区", value: "合川区" },
{ label: "万盛区", value: "万盛区" },
{ label: "长寿区", value: "长寿区" },
{ label: "城口区", value: "城口区" },
{ label: "柏梓镇", value: "柏梓镇" },
{ label: "江北区", value: "江北区" },
];
export const regionOptions = ref([
// { label: "重庆市", value: "重庆市" },
// { label: "万州区", value: "万州区" },
// { label: "沙坪坝区", value: "沙坪坝区" },
// { label: "渝中区", value: "渝中区" },
// { label: "巫溪县", value: "巫溪县" },
// { label: "涪陵区", value: "涪陵区" },
// { label: "合川区", value: "合川区" },
// { label: "万盛区", value: "万盛区" },
// { label: "长寿区", value: "长寿区" },
// { label: "城口区", value: "城口区" },
// { label: "柏梓镇", value: "柏梓镇" },
// { label: "江北区", value: "江北区" },
]);
// 类型选项
export const typeOptions = [
{ label: "边坡坍塌", value: "边坡坍塌" },
{ label: "路面塌陷", value: "路面塌陷" },
{ label: "桥梁损坏", value: "桥梁损坏" },
{ label: "隧道事故", value: "隧道事故" },
{ label: "交通主管部门", value: "交通主管部门" },
{ label: "公路机构", value: "公路机构" },
{ label: "养护站", value: "养护站" },
{ label: "护路员", value: "护路员" },
];
// 获取行政区域选项
export const fetchDistrictOptions = async () => {
try {
const res = await request({
url: '/snow-ops-platform/sm-event/dashboard/district-options',
method: 'GET',
});
if (res && res.code === '00000' && Array.isArray(res.data)) {
// 将接口返回的数据转换为选项格式
const options = res.data.map(item => ({
label: item.qxmc,
value: item.xzdm,
}));
// 保留"全部"选项,并添加接口返回的数据
regionOptions.value = [
{ label: "全部", value: "" },
...options,
];
return options;
}
} catch (error) {
console.error('获取行政区域选项失败:', error);
}
return regionOptions.value;
};
// 路况类型选项默认数据会被API数据替换
export const typeOptions = ref([
]);
// 获取路况类型选项
export const fetchRoadConditionOptions = async () => {
try {
const res = await request({
url: '/snow-ops-platform/sm-event/dashboard/road-condition-options',
method: 'GET',
});
if (res && res.code === '00000' && Array.isArray(res.data)) {
// 将接口返回的字符串数组转换为选项格式
const options = res.data.map(item => ({
label: item,
value: item,
}));
typeOptions.value = options;
return options;
}
} catch (error) {
console.error('获取路况类型选项失败:', error);
}
return typeOptions.value;
};
// 管控措施选项
export const controlMeasureOptions = [
@ -84,15 +126,9 @@ export const isEndedOptions = [
];
// 行政区域选项(带全部)
export const regionOptionsWithAll = [
{ label: "全部", value: "" },
{ label: "万州区", value: "wanzhou" },
{ label: "涪陵区", value: "fuling" },
{ label: "合川区", value: "hechuan" },
{ label: "万盛区", value: "wansheng" },
{ label: "长寿区", value: "changshou" },
{ label: "城口区", value: "chengkou" },
];
export const regionOptionsWithAll = ref([
]);
// 格式化日期时间为接口所需格式
export const formatDateTime = (date) => {
@ -120,4 +156,6 @@ export default {
isEndedOptions,
regionOptionsWithAll,
formatDateTime,
};
fetchRoadConditionOptions,
fetchDistrictOptions,
};

View File

@ -1,17 +1,9 @@
<template>
<div class="main">
<div class="top_title">
<img
class="title_bg"
src="../../assets/RiskWarning_img/一级标题栏bg@2x.png"
alt=""
/>
<img class="title_bg" src="../../assets/RiskWarning_img/一级标题栏bg@2x.png" alt="" />
<div class="title_img_box">
<img
class="title_img1"
src="../../assets/RiskWarning_img/位图@2x.png"
alt=""
/>
<img class="title_img1" src="../../assets/RiskWarning_img/位图@2x.png" alt="" />
<img
class="title_img2"
src="../../assets/RiskWarning_img/渝路畅行-风险预警一键响应@2x.png"
@ -43,7 +35,7 @@
@openWarningSituation="openDialog('warningSituation')"
@openResponseStatus="openDialog('responseStatus')"
@openDispatchDistrict="openDialog('dispatchDistrict')"
@showCenterCard="(item) => handleCenterCardClick(item)"
@showCenterCard="item => handleCenterCardClick(item)"
></left>
</div>
<div class="right">
@ -64,21 +56,33 @@
:dateRange="getdateRange"
:roadItem="roadItem"
@districtClick="handleDistrictClick"
@riskPointStatsChange="handleRiskPointStatsChange"
@update:roadvalArr="updateRoadvalArr"
@openHazardPointSituation="handleOpenHazardPointSituation"
/>
</div>
<!-- 地图遮罩层 -->
<div class="map-mask" aria-hidden="true"></div>
</div>
<div class="bottom">
<bottom
@roadItemClick="roadItemClick"
@changeActiveIndex="changeActiveIndex"
@hazardItemClick="handleHazardItemClick"
@clearMapMarkers="clearMapMarkers"
@showHazardPopupfn="showHazardPopupfn"
@hideRoadStats="handleHideRoadStats"
></bottom>
</div>
<top
class="top"
:roadItem="roadItem"
:showHazardPopup="showHazardPopup"
:riskPointStats="riskPointStats"
:roadvalArrtrue="roadvalArrtrue"
:showRoadStats="showRoadStats"
@openAIResult="openDialog('aiWarningResult')"
@dateRangeChange="handleDateRangeChange"
></top>
@ -164,6 +168,8 @@
<!-- 潼南基本信息对话框 -->
<tongnanInfoDialog
v-model:visible="dialogVisible.tongnanInfo"
:allCountyData="allCountyData"
:tongnanInfoItemData="tongnanInfoItemData"
@close="closeDialog('tongnanInfo')"
@call="openDialog('confirm')"
/>
@ -173,7 +179,13 @@
v-model:visible="dialogVisible.tongnanResponsible"
:allCountyData="allCountyData"
@close="closeDialog('tongnanResponsible')"
@detail="openDialog('tongnanInfo')"
@detail="openDialog('tongnanProjectPerson')"
/>
<!-- 项目负责人对话框 -->
<tongnanProjectPersonDialog
v-model:visible="dialogVisible.tongnanProjectPerson"
:allCountyData="allCountyData"
@close="closeDialog('tongnanProjectPerson')"
/>
<!-- 抢通情况对话框 -->
@ -208,6 +220,7 @@
:allCountyData="allCountyData"
@close="closeDialog('tongnanTeam')"
@view="openDialog('tongnanInfo')"
@itemdata="tongnanInfoItemDatafn()"
/>
<!-- 预警情况对话框 -->
@ -224,45 +237,54 @@
v-model:visible="dialogVisible.tunnelInfo"
@close="closeDialog('tunnelInfo')"
/>
<hazardPointSituationDialog
v-model:visible="dialogVisible.hazardPointSituation"
:data="hazardPointData"
@close="closeDialog('hazardPointSituation')"
/>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, provide } from "vue";
import useMapStore from "@/map/stores/mapStore";
import { useMapBase } from "../cockpit/composables/useMapBase";
import left from "./left.vue";
import right from "./right.vue";
import bottom from "./bottom.vue";
import top from "./top.vue";
import ChongqingMap from "./component/ChongqingMap.vue";
import { ref, onMounted, provide } from 'vue';
import useMapStore from '@/map/stores/mapStore';
import { useMapBase } from '../cockpit/composables/useMapBase';
import left from './left.vue';
import right from './right.vue';
import bottom from './bottom.vue';
import top from './top.vue';
import ChongqingMap from './component/ChongqingMap.vue';
import { fetchRoadConditionOptions, fetchDistrictOptions } from './component/index.js';
//
import responseSituationDiaLog from "./Dialog/responseSituationDiaLog.vue";
import warningInfoDialog from "./Dialog/warningInfoDialog.vue";
import eventDetailDialog from "./Dialog/eventDetailDialog.vue";
import confirmDialog from "./Dialog/confirmDialog.vue";
import riskPointDetailDialog from "./Dialog/riskPointDetailDialog.vue";
import impactPointDialog from "./Dialog/impactPointDialog.vue";
import impactPointDetailDialog from "./Dialog/impactPointDetailDialog.vue";
import responsePointDetailDialog from "./Dialog/responsePointDetailDialog.vue";
import responsePointInfoDialog from "./Dialog/responsePointInfoDialog.vue";
import responseStatusDialog from "./Dialog/responseStatusDialog.vue";
import aiWarningResultDialog from "./Dialog/aiWarningResultDialog.vue";
import tongnanInfoDialog from "./Dialog/tongnanInfoDialog.vue";
import tongnanResponsibleDialog from "./Dialog/tongnanResponsibleDialog.vue";
import clearanceSituationDialog from "./Dialog/clearanceSituationDialog.vue";
import controlSituationDialog from "./Dialog/controlSituationDialog.vue";
import dispatchDetailDialog from "./Dialog/dispatchDetailDialog.vue";
import dispatchDistrictDialog from "./Dialog/dispatchDistrictDialog.vue";
import tongnanTeamDialog from "./Dialog/tongnanTeamDialog.vue";
import warningSituationDialog from "./Dialog/warningSituationDialog.vue";
import tunnelInfoDialog from "./Dialog/tunnelInfoDialog.vue";
import centerInfoCard from "./Dialog/centerInfoCard.vue";
import responseSituationDiaLog from './Dialog/responseSituationDiaLog.vue';
import warningInfoDialog from './Dialog/warningInfoDialog.vue';
import eventDetailDialog from './Dialog/eventDetailDialog.vue';
import confirmDialog from './Dialog/confirmDialog.vue';
import riskPointDetailDialog from './Dialog/riskPointDetailDialog.vue';
import impactPointDialog from './Dialog/impactPointDialog.vue';
import impactPointDetailDialog from './Dialog/impactPointDetailDialog.vue';
import responsePointDetailDialog from './Dialog/responsePointDetailDialog.vue';
import responsePointInfoDialog from './Dialog/responsePointInfoDialog.vue';
import responseStatusDialog from './Dialog/responseStatusDialog.vue';
import aiWarningResultDialog from './Dialog/aiWarningResultDialog.vue';
import tongnanInfoDialog from './Dialog/tongnanInfoDialog.vue';
import tongnanResponsibleDialog from './Dialog/tongnanResponsibleDialog.vue';
import clearanceSituationDialog from './Dialog/clearanceSituationDialog.vue';
import controlSituationDialog from './Dialog/controlSituationDialog.vue';
import dispatchDetailDialog from './Dialog/dispatchDetailDialog.vue';
import dispatchDistrictDialog from './Dialog/dispatchDistrictDialog.vue';
import tongnanTeamDialog from './Dialog/tongnanTeamDialog.vue';
import warningSituationDialog from './Dialog/warningSituationDialog.vue';
import tunnelInfoDialog from './Dialog/tunnelInfoDialog.vue';
import centerInfoCard from './Dialog/centerInfoCard.vue';
import tongnanProjectPersonDialog from './Dialog/tongnanProjectPersonDialog.vue';
import hazardPointSituationDialog from './Dialog/hazardPointSituationDialog.vue';
import "./component/el-select.scss";
import "./component/date-picker-theme.scss";
import './component/el-select.scss';
import './component/date-picker-theme.scss';
//
const dialogVisible = ref({
@ -286,15 +308,31 @@ const dialogVisible = ref({
tongnanTeam: false,
warningSituation: false,
tunnelInfo: false,
tongnanProjectPerson: false,
hazardPointSituation: false,
});
const activeitem = ref({});
//
const riskPointStats = ref({
重大路外隐患点: 0,
重大路内隐患点: 0,
较大路外隐患点: 0,
较大路内隐患点: 0,
一般路内隐患点: 0,
一般路外隐患点: 0,
风险点总数: 0,
});
//
const hazardPointData = ref({});
//
const getdateRange = ref([]);
//
const handleDateRangeChange = (val) => {
console.log("日期范围变化:", val);
const handleDateRangeChange = val => {
console.log('日期范围变化:', val);
getdateRange.value = val;
};
@ -302,27 +340,67 @@ const handleDateRangeChange = (val) => {
const chongqingMapRef = ref(null);
//
const changeActiveIndex = (index) => {
const changeActiveIndex = index => {
activeitem.value = index;
};
const roadItem = ref({});
const roadItemClick = (item) => {
console.log("点击路段:", item);
const showRoadStats = ref(false);
const roadItemClick = item => {
console.log('点击路段:', item);
roadItem.value = item;
showRoadStats.value = true;
};
//
const handleHideRoadStats = () => {
console.log('隐藏路段统计');
showRoadStats.value = false;
};
//
const handleHazardItemClick = item => {
console.log('点击隐患点:', item);
//
if (chongqingMapRef.value) {
chongqingMapRef.value.handleHazardItemClick(item);
}
};
const showHazardPopup = ref(false);
const showHazardPopupfn = val => {
showHazardPopup.value = val;
};
//
const handleRiskPointStatsChange = stats => {
console.log('风险点统计数据变化:', stats);
riskPointStats.value = stats;
};
//
const roadvalArrtrue = ref([]);
const updateRoadvalArr = roadvalArr => {
roadvalArrtrue.value = roadvalArr;
// let num = 0;
// roadvalArrtrue.value.forEach(item => {
// num += item.value;
// });
// roadvalArrtrue.value.push({ label: '', value: num });
console.log('更新路段统计数据:', roadvalArr);
console.log('更新路段统计数据:', roadvalArrtrue);
};
//
const openResourceDetail = (item) => {
console.log("打开资源详情:", item);
const openResourceDetail = item => {
console.log('打开资源详情:', item);
//
if (item.label === "全市普通公路抢险队伍" || item.label === "人员") {
if (item.label === '全市普通公路抢险队伍' || item.label === '人员') {
//
if (chongqingMapRef.value) {
chongqingMapRef.value.getEmergencyForceData();
}
}
//
const key = item.label.toLowerCase().replace(/[^a-z]/g, "");
const key = item.label.toLowerCase().replace(/[^a-z]/g, '');
if (dialogVisible.value[key] !== undefined) {
dialogVisible.value[key] = true;
}
@ -330,51 +408,73 @@ const openResourceDetail = (item) => {
//
const clearMapMarkers = () => {
console.log("清除地图标记");
console.log('清除地图标记');
if (chongqingMapRef.value) {
chongqingMapRef.value.clearProjectMarkers();
}
};
//
const handleClearFilters = () => {
console.log('index.vue 处理清除筛选事件');
//
clearMapMarkers();
//
activeitem.value = {};
roadItem.value = {};
showHazardPopup.value = false;
};
//
const openDialog = (dialogName) => {
const openDialog = dialogName => {
dialogVisible.value[dialogName] = true;
};
const impactPointDetailItem = ref({});
//
const handleImpactPointClick = (item) => {
console.log("影响点点击:", item);
const handleImpactPointClick = item => {
console.log('影响点点击:', item);
impactPointDetailItem.value = item;
};
const handleImpactItem = ref({});
const handleImpactClickItem = (item) => {
console.log("影响点点击详情:", item);
const handleImpactClickItem = item => {
console.log('影响点点击详情:', item);
handleImpactItem.value = item;
};
const tongnanInfoItemData = ref({});
const tongnanInfoItemDatafn = item => {
console.log('点击详情:', item);
tongnanInfoItemData.value = item;
};
//
const closeDialog = (dialogName) => {
const closeDialog = dialogName => {
//
console.log("关闭弹窗", dialogName);
console.log('关闭弹窗', dialogName);
dialogVisible.value[dialogName] = false;
};
//
const handleOpenHazardPointSituation = item => {
console.log('打开涉灾隐患点情况弹窗:', item);
hazardPointData.value = item;
dialogVisible.value.hazardPointSituation = true;
};
//
const warningitem = ref({});
const handleWarningClick = (item) => {
console.log("气象预警点击:", item);
const handleWarningClick = item => {
console.log('气象预警点击:', item);
warningitem.value = item;
};
//
const confirmConfig = ref({
title: "提示",
message: "是否拨打电话?",
confirmText: "确定",
cancelText: "取消",
title: '提示',
message: '是否拨打电话?',
confirmText: '确定',
cancelText: '取消',
});
//
const openConfirm = (config) => {
const openConfirm = config => {
confirmConfig.value = { ...confirmConfig.value, ...config };
dialogVisible.value.confirm = true;
};
@ -382,23 +482,23 @@ const openConfirm = (config) => {
const showCenterCard = ref(false);
const allCountyData = ref({});
//
const handleDistrictClick = (item) => {
console.log("区县点击:", item);
const handleDistrictClick = item => {
console.log('区县点击:', item);
allCountyData.value = item;
if (item.data.roadType == "national") {
if (item.data.roadType == 'national') {
//
openDialog("tongnanTeam");
} else if (item.data.roadType == "rural") {
openDialog("responseSituation");
} else if (item.data.type == "project" && item.data.roadType == "-") {
openDialog('tongnanTeam');
} else if (item.data.roadType == 'rural') {
openDialog('responseSituation');
} else if (item.data.type == 'project' && item.data.roadType == '-') {
//
openDialog("tongnanResponsible");
openDialog('tongnanResponsible');
}
};
//
const handleCenterCardClick = (item) => {
console.log("中心卡片点击:", item);
const handleCenterCardClick = item => {
console.log('中心卡片点击:', item);
//
if (chongqingMapRef.value && item.data) {
@ -409,10 +509,7 @@ const handleCenterCardClick = (item) => {
chongqingMapRef.value.openCenterCard(cardData);
//
if (
item.data.length > 0 &&
(item.data[0].countyName || item.data[0].name)
) {
if (item.data.length > 0 && (item.data[0].countyName || item.data[0].name)) {
const firstCounty = item.data[0].countyName || item.data[0].name;
chongqingMapRef.value.locateToDistrict(firstCounty);
}
@ -420,15 +517,15 @@ const handleCenterCardClick = (item) => {
};
//
const getCardTitleByType = (type) => {
const getCardTitleByType = type => {
const titleMap = {
first: "国省道调度",
second: "农村公路调度",
third: "建设工程调度",
first: '国省道调度',
second: '农村公路调度',
third: '建设工程调度',
};
return titleMap[type] || "调度统计";
return titleMap[type] || '调度统计';
};
const handleCenterCardClickType = (item) => {
const handleCenterCardClickType = item => {
console.log(item.data);
showCenterCard.value = true;
// if (item.type === "second") {
@ -445,12 +542,12 @@ const refreshLeftData = ref(null);
const refreshRightData = ref(null);
//
const setRefreshLeftData = (callback) => {
const setRefreshLeftData = callback => {
refreshLeftData.value = callback;
};
// right.vue
const setRefreshRightData = (callback) => {
const setRefreshRightData = callback => {
refreshRightData.value = callback;
};
@ -466,10 +563,10 @@ const triggerRefreshLeftData = () => {
};
//
provide("setRefreshLeftData", setRefreshLeftData);
provide("triggerRefreshLeftData", triggerRefreshLeftData);
provide("setRefreshRightData", setRefreshRightData);
provide("getdateRange", getdateRange);
provide('setRefreshLeftData', setRefreshLeftData);
provide('triggerRefreshLeftData', triggerRefreshLeftData);
provide('setRefreshRightData', setRefreshRightData);
provide('getdateRange', getdateRange);
// ==================== ====================
@ -488,6 +585,8 @@ const mapBase = useMapBase(mapStore);
onMounted(() => {
//
mapBase.loadBaseData();
fetchRoadConditionOptions(); //
fetchDistrictOptions(); //
});
</script>
@ -502,7 +601,7 @@ onMounted(() => {
position: relative;
width: 100%;
height: 100%;
background-image: url("../../assets/RiskWarning_img/遮罩层.png");
background-image: url('../../assets/RiskWarning_img/遮罩层.png');
background-size: cover;
background-position: center;
}
@ -571,9 +670,17 @@ onMounted(() => {
position: absolute;
bottom: 5px;
left: 25%;
width: 50%;
width: 20%;
height: 43%;
}
.weather-warning-table {
position: absolute;
bottom: 5px;
left: 30%;
width: 40%;
height: 43%;
// z-index: 1;
}
.top {
position: absolute;
@ -591,7 +698,7 @@ onMounted(() => {
// left: 25%;
width: 100%;
height: 100%;
z-index: 2;
// z-index: 2;
}
/* 地图底层 - 填满整个容器 */
@ -609,8 +716,7 @@ onMounted(() => {
z-index: 1;
pointer-events: none;
/* 不阻挡交互 */
background: url(../../assets/RiskWarning_img/遮罩层.png) no-repeat
center/cover;
background: url(../../assets/RiskWarning_img/遮罩层.png) no-repeat center/cover;
}
//

View File

@ -404,8 +404,24 @@ const districtLoadLoad = async () => {
if (res.code == "00000") {
const data = res.data;
if (data) {
//
const simplifyDistrictName = (name) => {
if (!name) return name;
return name
.replace("彭水苗族土家族自治县", "彭水县")
.replace("石柱土家族自治县", "石柱县")
.replace("秀山土家族苗族自治县", "秀山县")
.replace("酉阳土家族苗族自治县", "酉阳县");
};
//
const processedData = data.map((item) => ({
...item,
name: simplifyDistrictName(item.name),
}));
//
const sortedData = data.sort((a, b) => {
const sortedData = processedData.sort((a, b) => {
const totalA =
(a.roadSectionCount || 0) +
(a.bridgeCount || 0) +

View File

@ -19,7 +19,8 @@
<div class="resource-info">
<div class="resource-label">{{ item.label }}</div>
<div class="resource-value">
{{ item.value }}<span class="unit">{{ item.unit }}</span>
{{ item.value }}
<span class="unit">{{ item.unit }}</span>
</div>
</div>
</div>
@ -28,8 +29,8 @@
<!-- 管控路段数 -->
<div class="control-section">
<div class="control-title display jc_sb ai_center">
<div class="f1">管控路段数 <span class="control-num">2</span></div>
<div class="f1">管控项目 <span class="control-num">2</span></div>
<div class="f1">管控路段数</div>
<div class="f1">管控项目</div>
</div>
<div style="display: flex; justify-content: space-between">
<div class="control-grid">
@ -78,11 +79,7 @@
</div>
</div>
<div class="patrol-grid">
<div
v-for="(item, index) in patrolData"
:key="index"
class="patrol-item"
>
<div v-for="(item, index) in patrolData" :key="index" class="patrol-item">
<div class="patrol-value">{{ item.value }}</div>
<div class="patrol-label">{{ item.label }}</div>
</div>
@ -93,16 +90,13 @@
<div class="rescue-section">
<div class="rescue-title">抢险投入情况</div>
<div class="rescue-grid">
<div
v-for="(item, index) in rescueData"
:key="index"
class="rescue-item"
>
<div v-for="(item, index) in rescueData" :key="index" class="rescue-item">
<!-- <div class="rescue-icon" :class="item.iconClass"></div> -->
<img class="rescue-icon" :src="item.img" alt="" />
<div class="rescue-info">
<div class="rescue-value">
{{ item.value }}<span class="unit">{{ item.unit }}</span>
{{ item.value }}
<span class="unit">{{ item.unit }}</span>
</div>
<div class="rescue-label">{{ item.label }}</div>
</div>
@ -116,9 +110,7 @@
<SectionHeader title="受灾情况">
<template #right>
<div class="header-filters">
<span class="filter-item active" @click="handleDateRangeClick()"
>本轮</span
>
<span class="filter-item active" @click="handleDateRangeClick()">本轮</span>
<div class="date-range-wrapper">
<el-date-picker
v-model="dateRange"
@ -180,7 +172,8 @@
:class="item.class"
>
<div class="damage-value">
{{ item.value }}<span class="unit">{{ item.unit }}</span>
{{ item.value }}
<span class="unit">{{ item.unit }}</span>
</div>
<div class="damage-label">{{ item.label }}</div>
</div>
@ -197,46 +190,42 @@
</template>
<script setup>
import { ref, computed, onMounted, inject, watch } from "vue";
import { request } from "@/utils/request";
import { ref, computed, onMounted, inject, watch } from 'vue';
import { request } from '@/utils/request';
import SectionHeader from "./component/sectionHeader.vue";
import { Calendar } from "@element-plus/icons-vue";
import SectionHeader from './component/sectionHeader.vue';
import { Calendar } from '@element-plus/icons-vue';
import icon1 from "../../assets/RiskWarning_img/icon1@2x.png";
import icon2 from "../../assets/RiskWarning_img/icon2@2x.png";
import icon3 from "../../assets/RiskWarning_img/icon3@2x.png";
import icon4 from "../../assets/RiskWarning_img/icon4@2x.png";
import icon1 from '../../assets/RiskWarning_img/icon1@2x.png';
import icon2 from '../../assets/RiskWarning_img/icon2@2x.png';
import icon3 from '../../assets/RiskWarning_img/icon3@2x.png';
import icon4 from '../../assets/RiskWarning_img/icon4@2x.png';
import icon11 from "../../assets/RiskWarning_img/icon-1@2x.png";
import icon12 from "../../assets/RiskWarning_img/icon-2@2x.png";
import icon13 from "../../assets/RiskWarning_img/icon-3@2x.png";
import icon11 from '../../assets/RiskWarning_img/icon-1@2x.png';
import icon12 from '../../assets/RiskWarning_img/icon-2@2x.png';
import icon13 from '../../assets/RiskWarning_img/icon-3@2x.png';
import icon51 from "../../assets/RiskWarning_img/编组5@2x.png";
import icon52 from "../../assets/RiskWarning_img/编组22@2x.png";
import icon55 from "../../assets/RiskWarning_img/路径55@2x.png";
import icon51 from '../../assets/RiskWarning_img/编组5@2x.png';
import icon52 from '../../assets/RiskWarning_img/编组22@2x.png';
import icon55 from '../../assets/RiskWarning_img/路径55@2x.png';
import icon62 from "../../assets/RiskWarning_img/路径62@2x.png";
import icon621 from "../../assets/RiskWarning_img/路径62@2x (1).png";
import icon622 from "../../assets/RiskWarning_img/路径62@2x (2).png";
import icon62 from '../../assets/RiskWarning_img/路径62@2x.png';
import icon621 from '../../assets/RiskWarning_img/路径62@2x (1).png';
import icon622 from '../../assets/RiskWarning_img/路径62@2x (2).png';
const emit = defineEmits([
"openClearanceSituation",
"openControlSituation",
"openResourceDetail",
]);
const emit = defineEmits(['openClearanceSituation', 'openControlSituation', 'openResourceDetail']);
//
const setRefreshRightData = inject("setRefreshRightData");
const getdateRange = inject("getdateRange", ref([]));
const setRefreshRightData = inject('setRefreshRightData');
const getdateRange = inject('getdateRange', ref([]));
//
const handleDateChange = (val) => {
const handleDateChange = val => {
dateRange.value = val;
getDisasterStats();
};
//
const handleDateRangeClick = (val) => {
const handleDateRangeClick = val => {
dateRange.value = [];
getDisasterStats();
};
@ -245,8 +234,8 @@ const handleDateRangeClick = (val) => {
const getYhYjllList = async () => {
try {
const res = await request({
url: "/snow-ops-platform/yhYjll/list",
method: "GET",
url: '/snow-ops-platform/yhYjll/list',
method: 'GET',
params: {
// longitude: 114.305556,
// latitude: 22.624722,
@ -254,10 +243,10 @@ const getYhYjllList = async () => {
},
});
console.log(res);
if (res.code == "00000") {
if (res.code == '00000') {
let gl1Rysls = 0; //
let gl1Yjllmcs = 0; //
res.data.forEach((item) => {
res.data.forEach(item => {
if (item.gl1Lx == 1 || item.gl1Lx == 2) {
gl1Yjllmcs = gl1Yjllmcs + 1;
}
@ -265,20 +254,20 @@ const getYhYjllList = async () => {
});
if (gl1Rysls > 10000) {
gl1Rysls = (gl1Rysls / 10000).toFixed(2);
resourceData.value[1].unit = "万人";
resourceData.value[1].unit = '万人';
} else {
resourceData.value[1].value = gl1Rysls;
resourceData.value[1].unit = "人";
resourceData.value[1].unit = '人';
}
resourceData.value[0].value = gl1Yjllmcs;
}
} catch (error) {
console.error("获取应急力量列表失败:", error);
console.error('获取应急力量列表失败:', error);
}
};
//
const getIconByType = (type) => {
const getIconByType = type => {
const iconMap = {
1: icon1, //
2: icon2, //
@ -292,16 +281,16 @@ const getIconByType = (type) => {
const getYhYjllListMaterials = async () => {
try {
const res = await request({
url: "/snow-ops-platform/yhYjll/listMaterials",
method: "GET",
url: '/snow-ops-platform/yhYjll/listMaterials',
method: 'GET',
params: {},
});
console.log("物资列表:", res);
if (res.code == "00000" && res.data) {
console.log('物资列表:', res);
if (res.code == '00000' && res.data) {
let equipment = 0; //
let materials = 0; //
res.data.forEach((item) => {
res.data.forEach(item => {
if (item.gl1Wzlx == 1) {
equipment = equipment + extractAndSumNumbers(item.gl1Wzsl);
} else if (item.gl1Wzlx == 2) {
@ -310,18 +299,18 @@ const getYhYjllListMaterials = async () => {
});
if (materials > 10000) {
resourceData.value[3].value = (materials / 10000).toFixed(2);
resourceData.value[3].unit = "万件";
resourceData.value[3].unit = '万件';
} else {
resourceData.value[3].value = materials;
resourceData.value[3].unit = "件";
resourceData.value[3].unit = '件';
}
console.log(equipment, materials);
//
resourceData.value[2].value = equipment || "0";
resourceData.value[2].value = equipment || '0';
}
} catch (error) {
console.error("获取物资列表失败:", error);
console.error('获取物资列表失败:', error);
}
};
@ -329,27 +318,27 @@ const getYhYjllListMaterials = async () => {
const getControlStats = async () => {
try {
const res = await request({
url: "/snow-ops-platform/sm-event/dashboard/control-stats",
method: "GET",
url: '/snow-ops-platform/sm-event/dashboard/control-stats',
method: 'GET',
});
console.log("管控统计数据:", res);
if (res.code == "00000" && res.data) {
console.log('管控统计数据:', res);
if (res.code == '00000' && res.data) {
const data = res.data;
// controlData1value
controlData1.value.forEach((item) => {
if (item.label === "封闭管控数") {
item.value = data.fullClosureCount || "0";
} else if (item.label === "半幅通行数") {
item.value = data.halfClosureCount || "0";
} else if (item.label === "限速(限车型)数") {
item.value = data.speedLimitCount || "0";
} else if (item.label === "告警阻拦处数") {
item.value = data.warningBlockCount || "0";
controlData1.value.forEach(item => {
if (item.label === '封闭管控数') {
item.value = data.fullClosureCount || '0';
} else if (item.label === '半幅通行数') {
item.value = data.halfClosureCount || '0';
} else if (item.label === '限速(限车型)数') {
item.value = data.speedLimitCount || '0';
} else if (item.label === '告警阻拦处数') {
item.value = data.warningBlockCount || '0';
}
});
}
} catch (error) {
console.error("获取管控统计数据失败:", error);
console.error('获取管控统计数据失败:', error);
}
};
@ -357,41 +346,41 @@ const getControlStats = async () => {
const getRescueInputStats = async () => {
try {
const res = await request({
url: "/snow-ops-platform/sm-event/dashboard/rescue-input-stats",
method: "GET",
url: '/snow-ops-platform/sm-event/dashboard/rescue-input-stats',
method: 'GET',
});
console.log("抢险投入统计数据:", res);
if (res.code == "00000" && res.data) {
console.log('抢险投入统计数据:', res);
if (res.code == '00000' && res.data) {
const data = res.data;
// rescueDatavalue
rescueData.value.forEach((item) => {
if (item.label === "本轮出动人次") {
item.value = data.investedManpower || "0";
item.unit = "人";
rescueData.value.forEach(item => {
if (item.label === '本轮出动人次') {
item.value = data.investedManpower || '0';
item.unit = '人';
if (item.value > 10000) {
item.value = (item.value / 10000).toFixed(2);
item.unit = "万人";
item.unit = '万人';
}
} else if (item.label === "本轮出动设备") {
item.value = data.investedMachinery || "0";
item.unit = "台";
} else if (item.label === '本轮出动设备') {
item.value = data.investedMachinery || '0';
item.unit = '台';
if (item.value > 10000) {
item.value = (item.value / 10000).toFixed(2);
item.unit = "万台";
item.unit = '万台';
}
} else if (item.label === "清理塌方") {
} else if (item.label === '清理塌方') {
//
item.value = data.clearedLandslide.toFixed(2) || 0;
item.unit = "立方米";
item.unit = '立方米';
if (item.value > 10000) {
item.value = (item.value / 10000).toFixed(2);
item.unit = "万立方米";
item.unit = '万立方米';
}
}
});
}
} catch (error) {
console.error("获取抢险投入统计数据失败:", error);
console.error('获取抢险投入统计数据失败:', error);
}
};
@ -399,102 +388,98 @@ const getRescueInputStats = async () => {
const getDisasterStats = async () => {
try {
const res = await request({
url: "/snow-ops-platform/sm-event/dashboard/disaster-stats",
method: "GET",
url: '/snow-ops-platform/sm-event/dashboard/disaster-stats',
method: 'GET',
});
console.log("灾害统计数据:", res);
if (res.code == "00000" && res.data) {
console.log('灾害统计数据:', res);
if (res.code == '00000' && res.data) {
const data = res.data;
// blockDatavalue
blockData.value.forEach((item) => {
if (item.label === "今日新增阻断数") {
item.current = data.todayNormalCount || "0";
item.total = data.todayTotalCount || "0";
} else if (item.label === "本轮累计阻断数") {
item.current = data.roundNormalCount || "0";
item.total = data.roundTotalCount || "0";
blockData.value.forEach(item => {
if (item.label === '今日新增阻断数') {
item.current = data.todayNormalCount || '0';
item.total = data.todayTotalCount || '0';
} else if (item.label === '本轮累计阻断数') {
item.current = data.roundNormalCount || '0';
item.total = data.roundTotalCount || '0';
}
});
// deathDatavalue
deathData.value.value = data.roundDeadCount || "0";
deathData.value.value = data.roundDeadCount || '0';
// damageDatavalue
damageData.value.forEach((item) => {
if (item.label === "本轮塌方量") {
damageData.value.forEach(item => {
if (item.label === '本轮塌方量') {
data.roundLandslideVolume = data.roundLandslideVolume.toFixed(2);
if (data.roundLandslideVolume > 10000) {
item.value = (data.roundLandslideVolume / 10000).toFixed(2) || "0";
item.unit = "万立方米";
item.value = (data.roundLandslideVolume / 10000).toFixed(2) || '0';
item.unit = '万立方米';
} else {
item.value = data.roundLandslideVolume || "0";
item.unit = "立方米";
item.value = data.roundLandslideVolume || '0';
item.unit = '立方米';
}
} else if (item.label === "汛期塌方量") {
data.floodSeasonLandslideVolume =
data.floodSeasonLandslideVolume.toFixed(2);
} else if (item.label === '汛期塌方量') {
data.floodSeasonLandslideVolume = data.floodSeasonLandslideVolume.toFixed(2);
if (data.floodSeasonLandslideVolume > 10000) {
item.value =
(data.floodSeasonLandslideVolume / 10000).toFixed(2) || "0";
item.unit = "万立方米";
item.value = (data.floodSeasonLandslideVolume / 10000).toFixed(2) || '0';
item.unit = '万立方米';
} else {
item.value = data.floodSeasonLandslideVolume || "0";
item.unit = "立方米";
item.value = data.floodSeasonLandslideVolume || '0';
item.unit = '立方米';
}
} else if (item.label === "当年塌方量") {
} else if (item.label === '当年塌方量') {
data.yearLandslideVolume = data.yearLandslideVolume.toFixed(2);
if (data.yearLandslideVolume > 10000) {
item.value = (data.yearLandslideVolume / 10000).toFixed(2) || "0";
item.unit = "万立方米";
item.value = (data.yearLandslideVolume / 10000).toFixed(2) || '0';
item.unit = '万立方米';
} else {
item.value = data.yearLandslideVolume || "0";
item.unit = "立方米";
item.value = data.yearLandslideVolume || '0';
item.unit = '立方米';
}
} else if (item.label === "本轮已损失") {
} else if (item.label === '本轮已损失') {
data.roundTotalLossAmount = data.roundTotalLossAmount.toFixed(2);
if (data.roundTotalLossAmount > 10000) {
item.value = (data.roundTotalLossAmount / 10000).toFixed(2) || "0";
item.unit = "亿元";
item.value = (data.roundTotalLossAmount / 10000).toFixed(2) || '0';
item.unit = '亿元';
} else {
item.value = data.roundTotalLossAmount || "0";
item.unit = "万元";
item.value = data.roundTotalLossAmount || '0';
item.unit = '万元';
}
} else if (item.label === "汛期已损失") {
data.floodSeasonTotalLossAmount =
data.floodSeasonTotalLossAmount.toFixed(2);
} else if (item.label === '汛期已损失') {
data.floodSeasonTotalLossAmount = data.floodSeasonTotalLossAmount.toFixed(2);
if (data.floodSeasonTotalLossAmount > 10000) {
item.value =
(data.floodSeasonTotalLossAmount / 10000).toFixed(2) || "0";
item.unit = "亿元";
item.value = (data.floodSeasonTotalLossAmount / 10000).toFixed(2) || '0';
item.unit = '亿元';
} else {
item.value = data.floodSeasonTotalLossAmount || "0";
item.unit = "万元";
item.value = data.floodSeasonTotalLossAmount || '0';
item.unit = '万元';
}
} else if (item.label === "当年已损失") {
} else if (item.label === '当年已损失') {
data.yearTotalLossAmount = data.yearTotalLossAmount.toFixed(2);
if (data.yearTotalLossAmount > 10000) {
item.value = (data.yearTotalLossAmount / 10000).toFixed(2) || "0";
item.unit = "亿元";
item.value = (data.yearTotalLossAmount / 10000).toFixed(2) || '0';
item.unit = '亿元';
} else {
item.value = data.yearTotalLossAmount || "0";
item.unit = "万元";
item.value = data.yearTotalLossAmount || '0';
item.unit = '万元';
}
}
});
}
} catch (error) {
console.error("获取灾害统计数据失败:", error);
console.error('获取灾害统计数据失败:', error);
}
};
//
const extractAndSumNumbers = (value) => {
const extractAndSumNumbers = value => {
//
if (typeof value === "number") {
if (typeof value === 'number') {
return value;
}
// 0
if (!value || typeof value !== "string") return 0;
if (!value || typeof value !== 'string') return 0;
//
const numbers = value.match(/\d+\.?\d*/g);
if (!numbers) return 0;
@ -503,17 +488,17 @@ const extractAndSumNumbers = (value) => {
};
//
const handleControlClick = (item) => {
if (item.label === "封闭管控数") {
emit("openClearanceSituation");
} else if (item.label === "关闭驻地数") {
emit("openControlSituation");
const handleControlClick = item => {
if (item.label === '封闭管控数') {
emit('openClearanceSituation');
} else if (item.label === '关闭驻地数') {
emit('openControlSituation');
}
};
//
const handleBlockClick = () => {
emit("openClearanceSituation");
emit('openClearanceSituation');
};
//
@ -522,117 +507,141 @@ const dateRange = ref([]);
//
const resourceData = ref([
{
label: "全市普通公路抢险队伍",
value: "",
unit: "支",
iconClass: "icon-team",
label: '全市普通公路抢险队伍',
value: '',
unit: '支',
iconClass: 'icon-team',
img: icon1,
},
{
label: "人员",
value: "",
unit: "人",
iconClass: "icon-person",
label: '人员',
value: '',
unit: '人',
iconClass: 'icon-person',
img: icon2,
},
{
label: "储备装备",
value: "",
unit: "台",
iconClass: "icon-equip",
label: '储备装备',
value: '',
unit: '台',
iconClass: 'icon-equip',
img: icon3,
},
{
label: "物资",
value: "",
unit: "件",
iconClass: "icon-material",
label: '物资',
value: '',
unit: '件',
iconClass: 'icon-material',
img: icon4,
},
]);
//
const handleResourceClick = (item) => {
emit("openResourceDetail", item);
const handleResourceClick = item => {
emit('openResourceDetail', item);
};
//
const controlData1 = ref([
{ label: "封闭管控数", value: "40" },
{ label: "半幅通行数", value: "40" },
{ label: "限速(限车型)数", value: "24" },
{ label: "告警阻拦处数", value: "32" },
{ label: '封闭管控数', value: '40' },
{ label: '半幅通行数', value: '40' },
{ label: '限速(限车型)数', value: '24' },
{ label: '告警阻拦处数', value: '32' },
]);
const controlData2 = [
{ label: "停工项目数", value: "30" },
{ label: "关闭驻地数", value: "42" },
{ label: "转移撤离人员数", value: "58" },
];
const controlData2 = ref([
{ label: '停工项目数', value: '0', key: 'stoped_project_count' },
{ label: '关闭驻地数', value: '0', key: 'closed_site_count' },
{ label: '转移撤离人员数', value: '0', key: 'displaced_population' },
]);
//
const getAffectedCountByProject = async () => {
try {
const res = await request({
url: '/snow-ops-platform/weather-warning/affected-count/_by_project',
method: 'GET',
});
console.log('气象预警受影响统计:', res);
if (res.code === '00000' && res.data) {
const data = res.data;
// controlData2
controlData2.value.forEach(item => {
const matchedData = data.find(d => d.name === item.key);
if (matchedData) {
item.value = String(matchedData.count);
}
});
}
} catch (error) {
console.error('获取气象预警受影响统计失败:', error);
}
};
//
const patrolData = [
{ label: "巡查路段数", value: "2" },
{ label: "巡查桥梁数", value: "1" },
{ label: "巡查边坡数", value: "6" },
{ label: "巡查隧道数", value: "10" },
{ label: "发现隐患数", value: "6" },
{ label: '巡查路段数', value: '2' },
{ label: '巡查桥梁数', value: '1' },
{ label: '巡查边坡数', value: '6' },
{ label: '巡查隧道数', value: '10' },
{ label: '发现隐患数', value: '6' },
];
//
const rescueData = ref([
{
label: "本轮出动人次",
value: "22341",
unit: "人次",
iconClass: "icon-rescue-person",
label: '本轮出动人次',
value: '22341',
unit: '人次',
iconClass: 'icon-rescue-person',
img: icon11,
},
{
label: "本轮出动设备",
value: "341",
unit: "台次",
iconClass: "icon-rescue-equip",
label: '本轮出动设备',
value: '341',
unit: '台次',
iconClass: 'icon-rescue-equip',
img: icon12,
},
{
label: "清理塌方",
value: "1367",
unit: "立方米",
iconClass: "icon-rescue-clear",
label: '清理塌方',
value: '1367',
unit: '立方米',
iconClass: 'icon-rescue-clear',
img: icon13,
},
]);
// -
const blockData = ref([
{ label: "今日新增阻断数", current: "19", total: "23" },
{ label: "本轮累计阻断数", current: "10", total: "23" },
{ label: '今日新增阻断数', current: '19', total: '23' },
{ label: '本轮累计阻断数', current: '10', total: '23' },
]);
//
const deathData = ref({ label: "本轮因灾死亡人数", value: "5" });
const deathData = ref({ label: '本轮因灾死亡人数', value: '5' });
//
const damageData = ref([
{ label: "本轮塌方量", value: "23", unit: "万立方米", class: "blue" },
{ label: "汛期塌方量", value: "23", unit: "万立方米", class: "blue" },
{ label: "当年塌方量", value: "23", unit: "万立方米", class: "blue" },
{ label: "本轮已损失", value: "80", unit: "万元", class: "red" },
{ label: "汛期已损失", value: "18", unit: "万元", class: "red" },
{ label: "当年已损失", value: "350", unit: "万元", class: "red" },
{ label: '本轮塌方量', value: '23', unit: '万立方米', class: 'blue' },
{ label: '汛期塌方量', value: '23', unit: '万立方米', class: 'blue' },
{ label: '当年塌方量', value: '23', unit: '万立方米', class: 'blue' },
{ label: '本轮已损失', value: '80', unit: '万元', class: 'red' },
{ label: '汛期已损失', value: '18', unit: '万元', class: 'red' },
{ label: '当年已损失', value: '350', unit: '万元', class: 'red' },
]);
//
const majorEvent = "0";
const majorEvent = '0';
// watch
const init = () => {
console.log("right.vue 刷新数据");
console.log('right.vue 刷新数据');
getYhYjllList();
getYhYjllListMaterials();
getControlStats();
getRescueInputStats();
getDisasterStats();
getAffectedCountByProject();
};
//
@ -646,13 +655,13 @@ onMounted(() => {
watch(
() => getdateRange.value,
(newVal) => {
console.log("right.vue 日期范围变化:", newVal);
newVal => {
console.log('right.vue 日期范围变化:', newVal);
if (newVal && newVal.length === 2) {
}
init();
},
{ deep: true, immediate: true },
{ deep: true, immediate: true }
);
</script>
@ -699,7 +708,7 @@ watch(
.section-header {
display: flex;
justify-content: space-between;
background-image: url("../../assets/RiskWarning_img/标题bg@2x.png");
background-image: url('../../assets/RiskWarning_img/标题bg@2x.png');
align-items: center;
margin-bottom: vw(8);
height: vw(50);
@ -721,7 +730,7 @@ watch(
justify-content: center;
&::before {
content: "←";
content: '←';
color: #fff;
font-size: vw(10);
}
@ -751,12 +760,7 @@ watch(
padding: vw(4) vw(8);
background: linear-gradient(270deg, rgba(18, 52, 97, 0) 0%, #203555 100%);
border: 2px solid transparent;
border-image: linear-gradient(
270deg,
rgba(80, 145, 201, 0),
rgba(39, 77, 153, 1)
)
2 2;
border-image: linear-gradient(270deg, rgba(80, 145, 201, 0), rgba(39, 77, 153, 1)) 2 2;
border-radius: 6px;
border-right: 0px;
@ -771,16 +775,16 @@ watch(
font-size: vw(18);
&.icon-team::before {
content: "👷";
content: '👷';
}
&.icon-person::before {
content: "👤";
content: '👤';
}
&.icon-equip::before {
content: "🚛";
content: '🚛';
}
&.icon-material::before {
content: "📦";
content: '📦';
}
}
@ -837,7 +841,7 @@ watch(
grid-template-columns: repeat(2, 1fr);
// background: rgba(62, 106, 172, 0.36);
// box-shadow: inset 0px 0px 8px 0px #379bff;
background-image: url("../../assets/RiskWarning_img/路径 62@2x.png");
background-image: url('../../assets/RiskWarning_img/路径 62@2x.png');
background-size: 100% 100%;
background-position: right;
gap: vw(8);
@ -904,7 +908,7 @@ watch(
padding: vw(6) vw(5);
// background: rgba(64, 169, 255, 0.1);
// border-radius: 4px;
background-image: url("../../assets/RiskWarning_img/路径62@2x (1).png");
background-image: url('../../assets/RiskWarning_img/路径62@2x (1).png');
background-size: 100% 100%;
background-position: right;
@ -946,7 +950,7 @@ watch(
// background: rgba(64, 169, 255, 0.1);
// border: 1px solid rgba(64, 169, 255, 0.2);
// border-radius: 6px;
background-image: url("../../assets/RiskWarning_img/路径62@2x (1).png");
background-image: url('../../assets/RiskWarning_img/路径62@2x (1).png');
background-size: 100% 100%;
background-position: right;
@ -960,13 +964,13 @@ watch(
margin-left: vw(5);
&.icon-rescue-person::before {
content: "👷";
content: '👷';
}
&.icon-rescue-equip::before {
content: "🚜";
content: '🚜';
}
&.icon-rescue-clear::before {
content: "🏔️";
content: '🏔️';
}
}
@ -1030,7 +1034,7 @@ watch(
// display: grid;
// grid-template-columns: repeat(5, 1fr);
// gap: 10px;
background-image: url("../../assets/RiskWarning_img/编组22@2x.png");
background-image: url('../../assets/RiskWarning_img/编组22@2x.png');
background-size: 100% 100%;
background-position: center;
@ -1038,12 +1042,7 @@ watch(
width: 2px;
height: vw(40);
margin: auto 0;
background: linear-gradient(
180deg,
transparent 0%,
#18f2f9 50%,
transparent 100%
);
background: linear-gradient(180deg, transparent 0%, #18f2f9 50%, transparent 100%);
// margin: 0 auto;
}
@ -1116,7 +1115,7 @@ watch(
padding: vw(4) 0;
// background: rgba(64, 169, 255, 0.1);
// border-radius: 6px;
background-image: url("../../assets/RiskWarning_img/路径62@2x.png");
background-image: url('../../assets/RiskWarning_img/路径62@2x.png');
background-size: 100% 100%;
background-position: left;
@ -1159,7 +1158,7 @@ watch(
padding: vw(12);
// background: rgba(64, 169, 255, 0.1);
// border-radius: 6px;
background-image: url("../../assets/RiskWarning_img/编组5@2x.png");
background-image: url('../../assets/RiskWarning_img/编组5@2x.png');
background-size: 100% 100%;
background-position: left;

View File

@ -1,9 +1,7 @@
<template>
<div class="filter-header">
<div class="filter-container">
<span class="filter-item active" @click="handleDateRangeClick()"
>本轮</span
>
<span class="filter-item active" @click="handleDateRangeClick()">本轮</span>
<div class="date-range-wrapper">
<el-date-picker
v-model="dateRange"
@ -18,22 +16,132 @@
/>
</div>
</div>
<!-- <img class="filter-icon-ai" src="../../assets/RiskWarning_img/AI1@2x.png" alt="" @click="handleAIClick" /> -->
<div class="hazard-stats" v-if="showHazardPopup">
<div
v-for="(item, index) in hazardStatsShowArr"
:key="index"
class="stat-item"
:class="item.class"
>
<span class="stat-label">{{ item.label }}</span>
<span class="stat-value-container display ai_center">
<span class="stat-value">{{ item.value }}</span>
<span class="stat-unit"></span>
</span>
</div>
</div>
<div class="road-stats" v-if="showRoadStats && roadStats.length > 0">
<div v-for="(item, index) in roadStats" :key="index" class="stat-item">
<span class="stat-label">{{ item.label }}</span>
<span class="stat-value-container display ai_center">
<span class="stat-value">{{ item.value }}</span>
<span class="stat-unit"></span>
</span>
</div>
</div>
</div>
</template>
<script setup>
import { ref, watch, inject } from "vue";
import { Calendar } from "@element-plus/icons-vue";
import { ref, watch, inject, defineProps, nextTick, provide, onMounted } from 'vue';
import { Calendar } from '@element-plus/icons-vue';
import { request } from '@/utils/request';
const emit = defineEmits(["openAIResult", "dateRangeChange"]);
const props = defineProps({
riskPointStats: {
type: Object,
default: () => ({}),
},
showHazardPopup: {
type: Boolean,
default: false,
},
roadvalArrtrue: {
type: Array,
default: () => [],
},
roadItem: {
type: Object,
default: () => ({}),
},
showRoadStats: {
type: Boolean,
default: false,
},
});
watch(
() => props.showHazardPopup,
newShow => {
hazardStatsShowArr.value = JSON.parse(JSON.stringify([]));
hazardStats.value.forEach(item => {
item.value = 0;
item.show = false;
});
}
);
const emit = defineEmits(['openAIResult', 'dateRangeChange']);
//
const triggerRefreshLeftData = inject("triggerRefreshLeftData");
const triggerRefreshLeftData = inject('triggerRefreshLeftData');
const dateRange = ref([]);
//
const hazardStatsShowArr = ref([]);
const hazardStats = ref([
{ label: '重大路内隐患点', value: 0, show: false, type: '重大路内隐患点', isWithinRedLine: '是' },
{ label: '重大路外隐患点', value: 0, show: false, type: '重大路外隐患点', isWithinRedLine: '否' },
{ label: '较大路内隐患点', value: 0, show: false, type: '较大路内隐患点', isWithinRedLine: '是' },
{ label: '较大路外隐患点', value: 0, show: false, type: '较大路外隐患点', isWithinRedLine: '否' },
{ label: '一般路内隐患点', value: 0, show: false, type: '一般路内隐患点', isWithinRedLine: '是' },
{ label: '一般路外隐患点', value: 0, show: false, type: '一般路外隐患点', isWithinRedLine: '否' },
{ label: '风险点总数', value: 0, show: false, type: '风险点总数', isWithinRedLine: '' },
]);
//
const roadStats = ref([
{ label: '高风险路段', value: 0, type: '高风险路段' },
{ label: '较高风险路段', value: 0, type: '较高风险路段' },
{ label: '中风险路段', value: 0, type: '中风险路段' },
{ label: '低风险路段', value: 0, type: '低风险路段' },
{ label: '风险点总数', value: 0, type: '风险点总数' },
]);
watch(
() => props.riskPointStats,
newStats => {
console.log('top.vue 收到风险点统计数据:', newStats);
if (newStats) {
hazardStatsShowArr.value = [];
hazardStats.value.forEach(item => {
if (
item.label.includes(newStats.riskLevel) &&
newStats.isWithinRedLine == item.isWithinRedLine &&
newStats.value >= 0
) {
item.value = newStats.value;
item.show = true;
}
if (item.show) {
hazardStatsShowArr.value.push(item);
hazardStats.value[6].show = true;
}
});
hazardStats.value[6].value =
hazardStats.value[0].value +
hazardStats.value[1].value +
hazardStats.value[2].value +
hazardStats.value[3].value +
hazardStats.value[4].value +
hazardStats.value[5].value;
}
},
{ immediate: true, deep: true }
);
// 23:59:59
const setEndOfDay = (date) => {
const setEndOfDay = date => {
if (!date) return date;
const d = new Date(date);
d.setHours(23, 59, 59, 999);
@ -50,9 +158,9 @@ watch(
dateRange,
(newVal, oldVal) => {
//
console.log("dateRange 变化:", newVal, oldVal);
console.log('dateRange 变化:', newVal, oldVal);
if (JSON.stringify(newVal) !== JSON.stringify(oldVal)) {
console.log("dateRange 发生变化:", newVal);
console.log('dateRange 发生变化:', newVal);
// 23:59:59
if (newVal && newVal.length === 2 && newVal[1]) {
newVal[1] = setEndOfDay(newVal[1]);
@ -62,15 +170,51 @@ watch(
// triggerRefreshLeftData();
// }
//
emit("dateRangeChange", newVal);
emit('dateRangeChange', newVal);
}
},
{ deep: true },
{ deep: true }
);
const handleAIClick = () => {
emit("openAIResult");
emit('openAIResult');
};
//
const fetchRiskLevelCount = async () => {
try {
const res = await request({
url: '/snow-ops-platform/risk-point/risk-level-count',
method: 'GET',
});
console.log('风险等级统计数据:', res);
if (res.code === '00000' && res.data) {
//
const data = res.data;
let roadTotal = 0;
roadStats.value.forEach(item => {
// 0
item.value = 0;
//
const matchedData = data.find(d => item.label.includes(d.level));
if (matchedData) {
item.value = Number(matchedData.count);
roadTotal += Number(matchedData.count);
}
});
//
roadStats.value[4].value = roadTotal;
console.log('更新后的roadStats:', roadStats.value);
}
} catch (error) {
console.error('获取风险等级统计数据失败:', error);
}
};
//
onMounted(() => {
fetchRiskLevelCount();
});
</script>
<style lang="scss" scoped>
@ -82,8 +226,8 @@ const handleAIClick = () => {
.filter-header {
padding: vw(10);
display: flex;
justify-content: space-between;
// display: flex;
// justify-content: space-between;
// align-items: center;
//
@ -104,6 +248,7 @@ const handleAIClick = () => {
min-height: 18px;
gap: vw(8);
font-size: vw(13);
margin-bottom: vw(20);
.filter-item {
color: #fff;
@ -165,4 +310,91 @@ const handleAIClick = () => {
min-height: 48px;
cursor: pointer;
}
//
.hazard-stats {
width: min-content;
display: grid;
background: url('@/assets/RiskWarning_img/隐患点弹窗背景@2x.png') no-repeat top left;
background-size: 100% 100%;
grid-template-columns: repeat(7, 1fr);
margin-bottom: vw(20);
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
// gap: vw(4);
&:last-child {
border-right: none;
}
.stat-label {
font-size: vw(14);
padding: vw(8) vw(15);
// background: #0e365d;
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.stat-value-container {
padding: vw(4) vw(10);
}
.stat-value {
font-size: vw(18);
font-weight: bold;
color: #ff4d4f;
}
.stat-unit {
font-size: vw(14);
color: #fff;
}
}
}
//
.road-stats {
padding: vw(10);
width: min-content;
display: grid;
background: url('@/assets/RiskWarning_img/隐患点弹窗背景@2x.png') no-repeat top left;
background-size: 100% 100%;
grid-template-columns: repeat(5, 1fr);
margin-bottom: vw(20);
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
gap: vw(4);
padding: 0 vw(10);
&:last-child {
border-right: none;
}
.stat-label {
font-size: vw(14);
color: rgba(255, 255, 255, 0.8);
white-space: nowrap;
}
.stat-value-container {
display: flex;
align-items: center;
gap: vw(2);
.stat-value {
font-size: vw(18);
font-weight: bold;
color: #ff4d4f;
}
.stat-unit {
font-size: vw(14);
color: rgba(255, 255, 255, 0.8);
}
}
}
}
</style>

View File

@ -372,6 +372,12 @@ export default () => {
path: '/dutyManagement'
});
};
// 跳转到消息推送设置
const gotoMessagePage = () => {
router.push({
path: '/messageManagement'
});
}
onMounted(() => {
getTableData();
@ -396,6 +402,7 @@ export default () => {
columns,
gotoLedgerPage,
gotoDutyPage,
gotoMessagePage,
modelVisible,
model,

View File

@ -17,6 +17,7 @@
<el-button type="primary" @click="script.gotoLedgerPage">线下帮扶台账</el-button>
<el-button type="primary" @click="script.openScheduleDiaog">立即排班</el-button>
<el-button type="primary" @click="script.gotoDutyPage">值班管理</el-button>
<el-button type="primary" @click="script.gotoMessagePage">消息推送设置</el-button>
<el-button type="primary" color="#952DE6" @click="">导出</el-button>
<input type="file" ref="fileInput" style="display: none" @change="handleFileSelect" accept=".*"></input>
<!-- <el-button type="primary" @click="script.gotoLedgerPage">驻地台账</el-button> -->

View File

@ -0,0 +1,267 @@
<template>
<div class="detail-container">
<el-form ref="formRef" :model="form" label-position="right" label-width="auto"
style="max-height: 60vh; overflow-y: hidden; padding-right: 50px" :rules="rules">
<el-form-item label="" prop="schedules">
<div class="user-select-container">
<!-- 左侧用户列表 -->
<div class="user-list-panel">
<div class="panel-title">用户列表</div>
<div class="user-list">
<div v-for="user in currentUsers" :key="user.userId" class="user-item"
:class="{ 'selected': isUserSelected(user) }" @click="toggleUserSelection(user)">
<span class="user-name">{{ user.realName || user.nickName || user.account }}</span>
<span class="user-position">{{ user.positionName }}</span>
</div>
</div>
</div>
<!-- 右侧已选择用户 -->
<div class="selected-panel">
<div class="panel-title">已选择用户 ({{ selectedUsers.length }})</div>
<div class="selected-user-list">
<div v-for="user in selectedUsers" :key="user.userId" class="selected-user-item">
<div class="user-info">
<span class="user-name">{{ user.realName || user.nickName || user.account }}</span>
<span class="user-position">{{ user.positionName }}</span>
</div>
<el-button type="danger" size="small" icon="Delete" circle @click.stop="removeSelectedUser(user)" />
</div>
</div>
</div>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ref, computed, onMounted, watch } from "vue";
import { request } from "@/utils/request";
import { ElMessage } from 'element-plus';
const formRef = ref(null);
//
const currentUsers = ref([]);
//
const selectedUsers = ref([]);
defineExpose({
formRef,
});
const props = defineProps({
form: {
type: Object,
default: () => ({}),
},
orgId: {
type: String,
default: ''
},
orgName: {
type: String,
default: ''
}
});
const rules = computed(() => {
return {
};
});
//
const isUserSelected = (user) => {
return selectedUsers.value.some(selected => selected.userId === user.userId);
};
//
const toggleUserSelection = (user) => {
if (isUserSelected(user)) {
removeSelectedUser(user);
} else {
// orgId
selectedUsers.value.push({
// ...user,
// orgId: user.orgId, // orgId
// timeRange: null
...user,
orgId: user.orgId,
orgName: props.orgName,
userId: user.userId,
userAccount: user.account,
userPhone: user.phone
});
}
};
//
const removeSelectedUser = (user) => {
const index = selectedUsers.value.findIndex(selected => selected.userId === user.userId);
if (index > -1) {
selectedUsers.value.splice(index, 1);
}
};
// ID
const getUsersByOrgId = async (orgId) => {
// console.log('@@@@@', orgId);
try {
const res = await request({
url: '/snow-ops-platform/user/orgUsers',
method: 'GET',
params: {
orgId
}
})
if (res.code === '00000') {
currentUsers.value = res.data || [];
// console.log('@@@@@', res.data);
} else {
throw new Error(res.message)
}
} catch (error) {
ElMessage.error('获取用户失败');
console.error('获取用户失败:', error);
}
}
onMounted(() => {
getUsersByOrgId(props.orgId);
})
watch(selectedUsers.value, (val) => {
props.form.data = val;
}, { deep: true })
</script>
<style scoped>
.form-part {
padding: 20px;
}
.text-center {
text-align: center;
}
.user-select-container {
display: flex;
gap: 20px;
width: 100%;
height: 60vh;
}
.user-list-panel {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 10px;
background-color: #fff;
height: 100%;
}
.selected-panel {
flex: 1;
border: 1px solid #dcdfe6;
border-radius: 4px;
padding: 10px;
background-color: #fff;
display: flex;
flex-direction: column;
height: 100%;
}
.user-list {
flex: 1;
overflow-y: auto;
}
.user-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
margin-bottom: 4px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
border: 1px solid transparent;
}
.user-item:hover {
background-color: #f5f7fa;
}
.user-item.selected {
background-color: #409eff;
color: white;
border-color: #409eff;
}
.user-name {
font-size: 14px;
font-weight: 500;
}
.user-position {
font-size: 12px;
opacity: 0.7;
}
.selected-user-list {
flex: 1;
overflow-y: auto;
}
.selected-user-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
margin-bottom: 4px;
border-radius: 4px;
background-color: #f0f9ff;
border: 1px solid #e1f5fe;
gap: 12px;
}
.time-picker-container {
flex: 1.2;
min-width: 280px;
}
.time-picker-container :deep(.el-date-editor) {
width: 100%;
}
.user-info {
display: flex;
flex-direction: column;
min-width: 120px;
flex: 0.8;
}
.selected-user-item .user-name {
font-size: 14px;
font-weight: 500;
color: #303133;
}
.selected-user-item .user-position {
font-size: 12px;
color: #606266;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
font-size: 14px;
padding-right: 8px;
}
</style>

View File

@ -0,0 +1,224 @@
import { h, ref, onMounted, reactive, watch, toRaw, nextTick } from "vue";
import { request } from "@/utils/request";
import { useRoute, useRouter } from 'vue-router'
import AddDialog from "./addDialog.vue";
const modelVisible = ref(false); // 弹窗状态
const drawerVisible = ref(false); // 抽屉状态
// 弹窗内容
const model = reactive({
});
const form = reactive({
});
const INIT_FORM = {
};
// 抽屉内容
const drawer = reactive({
title: '',
content: null,
props: {},
onCancel: null,
onConfirm: null,
direction: 'rtl',
size: '50%'
});
const dialogRef = ref(null); // 弹窗实例
const drawerRef = ref(null); // 抽屉实例
// 消息推送组织列表固定六个增加personList存储该组织的人员数组
const messageOrgList = ref([
{ title: '中心领导', orgName: '中心领导', personList: [] },
{ title: '法规处', orgName: '法规处', personList: [] },
{ title: '养护处', orgName: '养护处', personList: [] },
{ title: '农村公路处', orgName: '农村公路处', personList: [] },
{ title: '建设处', orgName: '建设处', personList: [] },
{ title: '市公路应急中心/管理段', orgName: '公路管理段', personList: [] },
])
const messagePushPerson = ref([])
const userOrgsList = ref([])
// 查询所有消息推送人员
const getAllMessagePushPerson = async () => {
try {
const res = await request({
url: '/snow-ops-platform/messagePushPerson/listAll',
method: 'GET',
})
if (res.code === '00000') {
messagePushPerson.value = res.data
// 按orgId分组填充人员数据到对应组织
messageOrgList.value.forEach(org => {
org.personList = messagePushPerson.value.filter(person => person.orgId === org.orgId)
});
// console.log('@@@@@@', messageOrgList.value);
} else {
throw new Error(res.message)
}
} catch (error) {
ElMessage.error('获取消息推送人员失败');
console.error('获取消息推送人员失败:', error);
}
}
// 查询所有组织
const getUserOrgs = async () => {
try {
const res = await request({
url: '/snow-ops-platform/user/userOrgs',
method: 'GET',
})
if (res.code === '00000') {
userOrgsList.value = res.data.data
// 遍历后端返回的组织数据与固定组织列表匹配并赋值orgId
messageOrgList.value.forEach(fixedOrg => {
const matchedOrg = userOrgsList.value.find(org =>
org.orgName === fixedOrg.orgName
);
if (matchedOrg) {
fixedOrg.orgId = matchedOrg.orgId;
}
});
// 在获取到orgId后立即根据orgId填充对应的人员数据
messageOrgList.value.forEach(org => {
org.personList = messagePushPerson.value.filter(person => person.orgId === org.orgId)
});
// console.log('@@@@',messageOrgList.value);
} else {
throw new Error(res.message)
}
} catch (error) {
ElMessage.error('获取组织失败');
console.error('获取组织失败:', error);
}
}
// 添加消息推送人
const handelAdd = async (data) => {
try {
const loading = ElLoading.service({
lock: true,
text: '操作中',
background: 'rgba(0, 0, 0, 0.7)',
})
const res = await request({
url: '/snow-ops-platform/messagePushPerson/add',
method: 'POST',
data: data
})
loading.close();
if (res.code === '00000') {
ElMessage.success('添加成功');
modelVisible.value = false;
await getAllMessagePushPerson();
} else {
throw new Error(res.message)
}
} catch (error) {
ElMessage.error('添加失败');
console.error('添加失败:', error);
}
}
// 打开添加人员弹窗
const openAddDialog = (orgId, orgName) => {
model.title = '添加人员';
Object.assign(form, INIT_FORM);
model.props = {
orgId: orgId,
orgName: orgName,
form: form,
};
model.content = AddDialog;
model.onCancel = () => {
modelVisible.value = false;
};
model.onConfirm = async () => {
await dialogRef?.value?.dynamicComponentRef?.formRef.validate().then(async () => {
// console.log('@@@@@@form',form);
await handelAdd(form.data)
// await addSchedule(form)
// await publishWarning(form)
})
.catch((err) => {
ElMessage.error('请处理表单中的错误项');
});
};
model.width = "70%"
modelVisible.value = true;
}
// 删除消息推送人
const deletePushPerson = async (person) => {
try {
await ElMessageBox.confirm(
`确定要删除【${person.realName}】吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
}
)
const loading = ElLoading.service({
lock: true,
text: '操作中',
background: 'rgba(0, 0, 0, 0.7)',
})
const res = await request({
url: '/snow-ops-platform/messagePushPerson/deleteByUserId',
method: 'POST',
data: {
userId: person.userId
}
})
loading.close();
if (res.code === '00000') {
ElMessage.success('删除成功');
await getAllMessagePushPerson();
} else {
throw new Error(res.message)
}
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('删除失败');
console.error('删除失败:', error);
}
}
}
export default () => {
const router = useRouter();
onMounted(async () => {
await getUserOrgs();
await getAllMessagePushPerson();
})
return {
modelVisible,
model,
drawerVisible,
drawer,
dialogRef,
drawerRef,
openAddDialog,
deletePushPerson,
messageOrgList,
}
}

View File

@ -0,0 +1,121 @@
<template>
<div class="root">
<div class="content-box">
<el-card shadow="never" v-for="org in script.messageOrgList.value" class="org-box">
<template #header>
<div class="card-header">
<span>{{ org.title }}</span>
<el-button type="primary" size="small" text
@click="script.openAddDialog(org.orgId, org.orgName)">添加</el-button>
</div>
</template>
<!-- 人员列表 -->
<div class="person-list">
<div v-if="org.personList.length === 0" class="empty-text">暂无人员</div>
<div v-else class="person-item" v-for="person in org.personList" :key="person.userId">
<div class="person-info">
<span class="name">{{ person.realName }}</span>
<!-- <span class="account">({{ person.userAccount }})</span> -->
<span class="phone" v-if="person.userPhone">{{ person.userPhone }}</span>
</div>
<el-button type="danger" size="small" text @click="script.deletePushPerson(person)">删除</el-button>
</div>
</div>
</el-card>
</div>
<div class="model-box">
<MyDialog v-model="script.modelVisible.value" :title="script.model?.title"
:dynamicComponent="script.model?.content" :component-props="script.model?.props"
:onConfirm="script.model?.onConfirm" :onCancel="script.model?.onCancel" ref="dialogRef"
:width="script.model?.width">
</MyDialog>
<MyDrawer v-model="script.drawerVisible.value" :title="script.drawer?.title"
:dynamicComponent="script.drawer?.content" :component-props="script.drawer?.props"
:onConfirm="script.drawer?.onConfirm" :onCancel="script.drawer?.onCancel" ref="drawerRef"
:direction="script.drawer?.direction" :size="script.drawer?.size">
</MyDrawer>
</div>
</div>
</template>
<script setup>
import DynamicTable from "@/component/DynamicTable/index.js";
import { Search, ArrowDown } from "@element-plus/icons-vue";
import MyDialog from "@/component/MyDialog/index.js";
import MyDrawer from "@/component/MyDrawer/index.js";
import scriptFn from "./index.js";
const script = scriptFn();
const { dialogRef, drawerRef } = script;
</script>
<style scoped>
.root {
height: 100%;
padding: 25px;
}
.content-box {
width: 100%;
height: 100%;
display: flex;
flex-direction: row;
gap: 20px;
}
.org-box {
flex: 1;
height: 100%;
min-width: fit-content;
}
.card-header {
display: flex;
flex-direction: row;
justify-content: space-between;
white-space: nowrap;
align-items: center;
}
.person-list {
margin-top: 10px;
}
.empty-text {
color: #999;
text-align: center;
padding: 20px 0;
}
.person-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.person-item:last-child {
border-bottom: none;
}
.person-info {
display: flex;
gap: 10px;
align-items: center;
}
.name {
font-weight: bold;
}
.account {
color: #666;
font-size: 0.9em;
}
.phone {
color: #666;
font-size: 0.9em;
}
</style>