修改大屏的地图,使用天地图。进入页面加载涉灾隐患点数据,修改所有大屏项目文字改为驻地,大屏驻地图标加载优化,去除本轮字段,增加网络电话弹窗。

This commit is contained in:
fanjia 2026-05-07 09:32:05 +08:00
parent 3b23b530d9
commit de2d0184a9
20 changed files with 2245 additions and 1888 deletions

View File

@ -399,7 +399,7 @@ const currentWarningInfo = computed(() => {
//
const impactTabs = ref([
{ key: 'point', label: '影响点' },
{ key: 'project', label: '影响项目' },
{ key: 'project', label: '影响驻地' },
]);
const activeImpactTab = ref('project');
@ -413,7 +413,7 @@ const aiiTypeArr = ref([
{ number: '21', label: '风险路段' },
{ number: '21', label: '桥梁' },
{ number: '21', label: '隧道' },
{ number: '21', label: '项目' },
{ number: '21', label: '驻地' },
]);
//
const impactProjectDataTable = ref([

View File

@ -116,7 +116,14 @@ watch(
(newVal) => {
console.log('newVal', newVal)
if (newVal.label) {
filterForm.value.roadConditionType = newVal.label.substring(0, newVal.label.length - 1) || ''
// label
const labelMap = {
封闭管控数: '全幅封闭',
告警阻拦处数: '告警阻拦',
'限速(限车型)数': '限速',
半幅通行数: '半幅封闭',
}
filterForm.value.roadConditionType = labelMap[newVal.label] || newVal.label.substring(0, newVal.label.length - 1) || ''
currentPage.value = 1
fetchData()
} else {

View File

@ -1,4 +1,6 @@
<template>
<div>
<!-- 确认弹窗 -->
<div v-if="visible" class="confirm-dialog-overlay" @click="handleOverlayClick">
<div class="confirm-dialog" @click.stop>
<!-- 四个角的装饰 -->
@ -30,10 +32,20 @@
</div>
</div>
</div>
<!-- 软电话配置弹窗 -->
<SoftPhoneConfigDialog
v-model:visible="softPhoneVisible"
@confirm="handleSoftPhoneConfirm"
@cancel="handleSoftPhoneCancel"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { Close } from '@element-plus/icons-vue';
import SoftPhoneConfigDialog from './softPhoneConfigDialog.vue';
const props = defineProps({
visible: {
@ -60,12 +72,28 @@ const props = defineProps({
const emit = defineEmits(['update:visible', 'confirm', 'cancel']);
//
//
const softPhoneVisible = ref(false);
// -
const handleConfirm = () => {
emit('confirm');
softPhoneVisible.value = true;
};
//
const handleSoftPhoneConfirm = (configData) => {
console.log('软电话配置数据:', configData);
// confirm
emit('confirm', configData);
emit('update:visible', false);
};
//
const handleSoftPhoneCancel = () => {
//
console.log('软电话配置已取消');
};
//
const handleCancel = () => {
emit('cancel');

View File

@ -54,13 +54,21 @@
</div>
<!-- 照片 - 路段数据展示 -->
<div v-if="isRoadData && hazardData.photos" class="info-block display">
<div v-if="hazardData.photos" class="info-block display">
<div class="block-title mr_10">照片</div>
<div class="photo-list">
<img v-for="(photo, index) in hazardData.photos" :key="index" :src="photo" class="photo-item" @click="previewPhoto(photo)" />
<img v-for="(photo, index) in hazardData.photos" :key="index" :src="photo" class="photo-item" @click="previewPhoto(index)" />
</div>
</div>
<!-- 图片预览组件 -->
<el-image-viewer
v-if="previewVisible"
:url-list="hazardData.photos"
:initial-index="previewIndex"
@close="previewVisible = false"
/>
<!-- 风险描述 -->
<div class="info-block">
<div class="block-title">风险描述</div>
@ -313,11 +321,14 @@ const handleCurrentChange = (val) => {
currentPage.value = val
}
//
const previewVisible = ref(false)
const previewIndex = ref(0)
//
const previewPhoto = (photo) => {
// 使 Element Plus
const { preview } = require('element-plus')
preview(photo)
const previewPhoto = (index) => {
previewIndex.value = index
previewVisible.value = true
}
//

View File

@ -219,6 +219,10 @@ const props = defineProps({
type: Object,
default: () => {},
},
getdateRange: {
type: Array,
default: () => [],
},
})
const emit = defineEmits(['update:visible', 'close', 'detail', 'itemClick'])
@ -228,7 +232,7 @@ const filterForm = ref({
pointType: '',
pointLevel: '',
region: '',
roadType: '',
roadType: 'G,S',
})
// 使
@ -328,14 +332,20 @@ const impactData = ref([
{ name: '影响桥梁', count: 0, icon: Icon0, type: '桥梁' },
{ name: '影响隧道', count: 0, icon: Icon2, type: '隧道' },
{ name: '影响边坡', count: 0, icon: Icon1, type: '边坡' },
{ name: '影响项目', count: 0, icon: Icon3, type: '项目' },
{ name: '影响项目', count: 0, icon: Icon3, type: '驻地' },
])
//
const loadBarChartData = async () => {
console.log('原始时间范围:', props.handleImpactItem)
try {
const res = await request({
url: '/snow-ops-platform/weather-warning/affected-count',
method: 'GET',
params: {
start: formatDateTime(props.getdateRange[0]),
end: formatDateTime(props.getdateRange[1]),
warningId: props.handleImpactItem.warningId,
},
})
if (res.code == '00000') {
@ -347,12 +357,12 @@ const loadBarChartData = async () => {
bridge: '桥梁',
tunnel: '隧道',
slope: '边坡',
project: '项目',
project: '驻地',
Road: '路段',
Bridge: '桥梁',
Tunnel: '隧道',
Slope: '边坡',
Project: '项目',
Project: '驻地',
}
// name
@ -384,13 +394,13 @@ const getColumnsByType = (type) => {
桥梁: slopeColumns,
隧道: tunnelColumns,
边坡: roadColumns,
项目: projectColumns,
驻地: projectColumns,
}
// { name: '', count: 0, icon: Icon4, type: '' },
// { name: '', count: 0, icon: Icon0, type: '' },
// { name: '', count: 0, icon: Icon2, type: '' },
// { name: '', count: 0, icon: Icon1, type: '' },
// { name: '', count: 0, icon: Icon3, type: '' },
// { name: '', count: 0, icon: Icon3, type: '' },
return typeMap[type] || bridgeColumns
}
@ -400,7 +410,7 @@ const getApiUrlByType = (type) => {
桥梁: '/snow-ops-platform/weather-warning/affected-object/bridge',
边坡: '/snow-ops-platform/weather-warning/affected-object/slope',
隧道: '/snow-ops-platform/weather-warning/affected-object/tunnel',
项目: '/snow-ops-platform/weather-warning/affected-object/project',
驻地: '/snow-ops-platform/weather-warning/affected-object/project',
路段: '/snow-ops-platform/weather-warning/affected-object/road-section',
}
return urlMap[type] || '/snow-ops-platform/weather-warning/affected-object/bridge'
@ -481,21 +491,22 @@ const handleFilterChange = () => {
const getTimeParams = () => {
console.log('原始时间范围:', props.handleImpactItem)
let countyName = ''
let countyId = ''
console.log('区域:', filterForm.value)
regionOptions.value.forEach((item) => {
if (item.label === filterForm.value.region) {
countyName = item.label || ''
if (item.label === filterForm.value.region || item.value === filterForm.value.region) {
countyId = item.value || ''
}
})
return {
// start: `${year}-${month}-01 00:00:00`,
// end: `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`,
start: formatDateTime(props.handleImpactItem.dateRange?.[0] || ''),
end: formatDateTime(props.handleImpactItem.dateRange?.[1] || ''),
start: formatDateTime(props.getdateRange[0] ? props.getdateRange[0] : props.handleImpactItem.dateRange?.[0] || ''),
end: formatDateTime(props.getdateRange[1] ? props.getdateRange[1] : props.handleImpactItem.dateRange?.[1] || ''),
limit: pageSize.value,
offset: (currentPage.value - 1) * pageSize.value,
countyName: countyName || '',
countyId: countyId || '',
riskLevel: filterForm.value.pointLevel || '',
roadTypes: filterForm.value.roadType || '',
}
@ -692,7 +703,7 @@ const processUnifiedData = (item, type) => {
}
// SQL
if (cardTypeVal.value === '项目') {
if (cardTypeVal.value === '驻地') {
return {
...baseData,
// - 使COUNTY
@ -765,6 +776,14 @@ const fetchData = async () => {
}))
total.value = res.total || 0
}
} else if (cardTypeVal.value == '驻地') {
if (res.data) {
tableData.value = res.data.map((item, index) => ({
...processDataByType(item, cardType.value),
id: index + 1,
}))
total.value = res.total || 0
}
} else {
if (res.code === '00000' && res.data) {
//
@ -825,7 +844,7 @@ watch(
// }
// tableData.value = []
// cardType.value = '0'
// loadBarChartData()
loadBarChartData()
// currentPage.value = 1
// fetchData()
// }

View File

@ -1,6 +1,6 @@
<template>
<base-dialog
v-model:visible="props.visible"
:visible="props.visible"
title="巡查里程"
:table-data="tableData"
:table-columns="tableColumns"
@ -11,6 +11,7 @@
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@close="handleClose"
@update:visible="handleClose"
>
<!-- 统计卡片区域 -->
<template #header>
@ -39,7 +40,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"
@ -48,12 +49,7 @@
clearable
@change="handleFilterChange"
>
<el-option
v-for="item in districtOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-option v-for="item in regionOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
</div>
@ -62,184 +58,143 @@
</template>
<script setup>
import { ref, watch } from 'vue';
import BaseDialog from '../component/baseDialog.vue';
import { request } from '@/utils/request';
import selectedIcon from '../../../assets/xiangying/选中bg@2x.png';
import unselectedIcon from '../../../assets/xiangying/未选中bg@2x.png';
import { ref, watch } from 'vue'
import BaseDialog from '../component/baseDialog.vue'
import { request } from '@/utils/request'
import selectedIcon from '../../../assets/xiangying/选中bg@2x.png'
import unselectedIcon from '../../../assets/xiangying/未选中bg@2x.png'
//
import IconNational from '../../../assets/xiangying/国省道icon.png';
import IconRuralRoad from '../../../assets/xiangying/农村公路icon.png';
import IconNational from '../../../assets/xiangying/国省道icon.png'
import IconRuralRoad from '../../../assets/xiangying/农村公路icon.png'
import { formatDateTime, regionOptions } from '../component/index.js'
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
});
getdateRange: {
type: Array,
default: () => [],
},
})
const emit = defineEmits(['update:visible', 'close']);
const emit = defineEmits(['update:visible', 'close'])
//
const nationalRoadMileage = ref(345);
const ruralRoadMileage = ref(4333);
const nationalRoadMileage = ref(345)
const ruralRoadMileage = ref(4333)
//
const mileageData = ref([
{ name: '国省道', value: 345, icon: IconNational, type: 'national' },
{ name: '农村公路', value: 4333, icon: IconRuralRoad, type: 'rural' },
]);
])
//
const cardType = ref('0');
const cardType = ref('0')
//
const handleClick = (index, item) => {
cardType.value = index + '';
cardType.value = index + ''
//
currentPage.value = 1;
fetchData();
};
currentPage.value = 1
fetchData()
}
//
const filterForm = ref({
district: '',
});
//
const districtOptions = ref([
{ label: '万州区', value: 'wanzhou' },
{ label: '黔江区', value: 'qianjiang' },
{ label: '涪陵区', value: 'fuling' },
{ label: '渝中区', value: 'yuzhong' },
{ label: '大渡口区', value: 'dadukou' },
{ label: '江北区', value: 'jiangbei' },
{ label: '沙坪坝区', value: 'shapingba' },
{ label: '九龙坡区', value: 'jiulongpo' },
{ label: '南岸区', value: 'nanan' },
{ label: '北碚区', value: 'beibei' },
{ label: '渝北区', value: 'yubei' },
{ label: '巴南区', value: 'banan' },
{ label: '长寿区', value: 'changshou' },
{ label: '江津区', value: 'jiangjin' },
{ label: '合川区', value: 'hechuan' },
{ label: '永川区', value: 'yongchuan' },
{ label: '南川区', value: 'nanchuan' },
{ label: '綦江区', value: 'qijiang' },
{ label: '大足区', value: 'dazu' },
{ label: '璧山区', value: 'bishan' },
{ label: '铜梁区', value: 'tongliang' },
{ label: '潼南区', value: 'tongnan' },
{ label: '荣昌区', value: 'rongchang' },
{ label: '开州区', value: 'kaizhou' },
{ label: '梁平区', value: 'liangping' },
{ label: '武隆区', value: 'wulong' },
]);
})
//
const tableHeight = ref(300);
const tableHeight = ref(300)
// -
const tableColumns = ref([
{ prop: 'id', label: '序号' },
{ prop: 'district', label: '所属区县' },
{ prop: 'mileage', label: '巡查里程' },
]);
])
//
const tableData = ref([]);
const tableData = ref([])
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
//
const handleClose = () => {
emit('update:visible', false);
emit('close');
};
emit('update:visible', false)
emit('close')
}
//
const handleSizeChange = val => {
pageSize.value = val;
fetchData();
};
const handleSizeChange = (val) => {
pageSize.value = val
fetchData()
}
const handleCurrentChange = val => {
currentPage.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val
fetchData()
}
//
const handleFilterChange = () => {
currentPage.value = 1;
fetchData();
};
//
const fetchStatsData = async () => {
try {
const res = await request({
url: '/snow-ops-platform/patrol/mileage-stats',
method: 'GET',
});
if (res.code === '00000' && res.data) {
const nationalVal = res.data.nationalRoadMileage || 345;
const ruralVal = res.data.ruralRoadMileage || 4333;
nationalRoadMileage.value = nationalVal;
ruralRoadMileage.value = ruralVal;
// mileageData
mileageData.value[0].value = nationalVal;
mileageData.value[1].value = ruralVal;
currentPage.value = 1
fetchData()
}
} catch (error) {
console.error('获取巡查里程统计数据失败:', error);
}
};
//
const fetchData = async () => {
try {
const res = await request({
url: '/snow-ops-platform/patrol/mileage-list',
url: '/snow-ops-platform/yhWgxc/county-mileage',
method: 'GET',
params: {
pageNum: currentPage.value,
pageSize: pageSize.value,
districtCode: filterForm.value.district,
startTime: formatDateTime(props.getdateRange?.[0] || ''),
endTime: formatDateTime(props.getdateRange?.[1] || ''),
qxbm: filterForm.value.district || '',
},
});
})
if (res.code === '00000' && res.data) {
const data = res.data;
const data = res.data
tableData.value = data.records.map((item, index) => {
return {
id: currentPage.value * pageSize.value - (pageSize.value - index - 1),
district: item.districtName || '-',
mileage: item.mileage ? `${item.mileage}公里` : '-',
};
});
total.value = data.total;
district: item.qxmc || '-',
mileage: item.mileage ? `${item.mileage}公里` : '0',
}
})
total.value = data.total
} else {
tableData.value = []
total.value = 0
}
} catch (error) {
console.error('获取巡查里程列表数据失败:', error);
console.error('获取巡查里程列表数据失败:', error)
}
}
};
// visible
watch(
() => props.visible,
newVal => {
if (newVal) {
currentPage.value = 1;
fetchStatsData();
fetchData();
}
}
);
(newVal) => {
tableData.value = []
total.value = 0
currentPage.value = 1
// fetchStatsData()
fetchData()
},
)
</script>
<style lang="scss" scoped>
@ -251,7 +206,6 @@ watch(
margin-bottom: 16px;
padding: 0 4px;
.stat-card {
display: flex;
flex: 1;

View File

@ -41,52 +41,21 @@
<!-- 筛选区域 -->
<template #filter>
<div class="filter-row">
<!-- <div class="filter-item">
<el-select :teleported="false"
v-model="filterForm.pointType"
placeholder="影响点类型"
class="filter-select"
>
<el-option
v-for="option in pointTypeOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</div> -->
<div class="filter-item">
<span class="filter-label">影响点等级</span>
<el-select
:teleported="false"
v-model="filterForm.pointLevel"
placeholder="影响点等级"
class="filter-select"
>
<el-option
v-for="option in pointLevelOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
<el-select :teleported="false" v-model="filterForm.pointLevel" placeholder="影响点等级" class="filter-select">
<el-option v-for="option in pointLevelOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</div>
<div class="filter-item">
<span class="filter-label">是否回应</span>
<el-select
:teleported="false"
v-model="filterForm.isResponded"
placeholder="是否回应"
class="filter-select"
>
<el-option
v-for="option in isRespondedOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
<el-select :teleported="false" v-model="filterForm.isResponded" placeholder="是否回应" class="filter-select">
<el-option v-for="option in isRespondedOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</div>
<div class="filter-item">
<el-button type="primary" @click="handleSearch">查询</el-button>
</div>
</div>
</template>
@ -100,11 +69,7 @@
<div class="person-info">
<div class="person-name center">
<span style="margin-right: 5px">{{ row.trafficDept.name }}</span>
<img
class="response-icon"
:src="row.trafficDept.isResponded ? row.trafficDept.img : row.trafficDept.img"
alt
/>
<img class="response-icon" :src="row.trafficDept.isResponded ? row.trafficDept.img : row.trafficDept.img" alt />
</div>
<span class="person-phone">{{ row.trafficDept.phone }}</span>
</div>
@ -144,9 +109,9 @@
</template>
<!-- 回应状态列插槽 -->
<!-- <template #responseStatus="{ row }">
<!-- <template #responseStatusData="{ row }">
<span class="response-status" :class="row.responseClass">{{
row.responseStatus
row.responseStatusData
}}</span>
</template> -->
@ -166,27 +131,22 @@
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { Close, ArrowLeft, ArrowRight } from '@element-plus/icons-vue';
import {
pointTypeOptions,
pointLevelOptions,
isRespondedOptions,
formatDateTime,
} from '../component/index.js';
import baseDialog from '../component/baseDialog.vue';
import { request } from '@/utils/request.js';
import { ref, computed, watch, onMounted } from 'vue'
import { Close, ArrowLeft, ArrowRight } from '@element-plus/icons-vue'
import { pointTypeOptions, pointLevelOptions, isRespondedOptions, formatDateTime } 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: {
@ -197,18 +157,22 @@ const props = defineProps({
type: Array,
default: () => [],
},
});
responseStatusData: {
type: Object,
default: () => {},
},
})
const emit = defineEmits(['update:visible', 'close', 'detail']);
const emit = defineEmits(['update:visible', 'close', 'detail'])
//
const filterForm = ref({
pointType: '',
pointLevel: '',
isResponded: '',
});
})
const cardType = ref('路段');
const cardType = ref('路段')
//
const statsCardsData = ref([
@ -216,8 +180,8 @@ const statsCardsData = ref([
{ type: '桥梁', label: '影响桥梁', value: 0, icon: Icon0 },
{ type: '隧道', label: '影响隧道', value: 0, icon: Icon2 },
{ type: '边坡', label: '影响边坡', value: 0, icon: Icon1 },
{ type: '项目', label: '影响项目', value: 0, icon: Icon3 },
]);
{ type: '驻地', label: '影响驻地', value: 0, icon: Icon3 },
])
//
const tableColumns = ref([
@ -234,37 +198,40 @@ const tableColumns = ref([
{ prop: 'generalStaff', label: '一般人员(路长履职)', width: '100px', slot: 'generalStaff' },
{ prop: 'urgeTime', label: '最新催告时间', width: '', slot: 'urgeTime' },
{ prop: 'operation', label: '操作', width: '', slot: 'operation' },
]);
])
//
const tableData = ref([]);
const tableData = ref([])
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(36)
//
const handleClose = () => {
emit('update:visible', false);
emit('close');
};
emit('update:visible', false)
emit('close')
}
//
const handleClick = type => {
cardType.value = type;
fetchData();
};
const handleClick = (type) => {
cardType.value = type
fetchData()
}
//
const loadBarChartData = async () => {
try {
const res = await request({
url: '/snow-ops-platform/weather-warning/affected-count',
method: 'GET',
});
params: {
warningId: props.responseStatusData.warningId,
},
})
if (res.code == '00000') {
const data = res.data;
const data = res.data
if (data && Array.isArray(data)) {
//
const nameMap = {
@ -272,44 +239,44 @@ const loadBarChartData = async () => {
bridge: '桥梁',
tunnel: '隧道',
slope: '边坡',
project: '项目',
project: '驻地',
Road: '路段',
Bridge: '桥梁',
Tunnel: '隧道',
Slope: '边坡',
Project: '项目',
};
statsCardsData.value.forEach(item => {
data.forEach(stat => {
if (stat.extension == item.type) {
item.value = stat.count || 0;
Project: '驻地',
}
});
});
statsCardsData.value.forEach((item) => {
data.forEach((stat) => {
if (stat.extension == item.type) {
item.value = stat.count || 0
}
})
})
}
}
} catch (error) {
console.error('加载柱状图数据失败:', error);
console.error('加载柱状图数据失败:', error)
}
}
};
// base-dialog
//
const handleDetail = item => {
emit('detail', item);
};
const handleDetail = (item) => {
emit('detail', item)
}
//
const handleSizeChange = val => {
pageSize.value = val;
fetchData();
};
const handleSizeChange = (val) => {
pageSize.value = val
fetchData()
}
const handleCurrentChange = val => {
currentPage.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val
fetchData()
}
//
const fetchData = async () => {
@ -317,29 +284,32 @@ const fetchData = async () => {
const params = {
start: '',
end: '',
};
offset: (currentPage.value - 1) * pageSize.value,
limit: pageSize.value,
warningId: props.responseStatusData.warningId,
}
if (props.dispatchDateRange && props.dispatchDateRange.length > 0) {
params.start = formatDateTime(props.dispatchDateRange[0]) || '';
params.end = formatDateTime(props.dispatchDateRange[1]) || '';
params.start = formatDateTime(props.dispatchDateRange[0]) || ''
params.end = formatDateTime(props.dispatchDateRange[1]) || ''
}
const res = await request({
url: '/snow-ops-platform/weather-warning/notice-entity',
method: 'GET',
params,
});
})
if (res && res.data) {
tableData.value = res.data.map((item, index) => {
// events
const trafficDeptEvent = item.events?.find(e => e.noticeRoles?.includes('交通主管')) || {};
const roadKeeperEvent = item.events?.find(e => e.noticeRoles?.includes('护路员')) || {};
const roadOrgEvent = item.events?.find(e => e.noticeRoles?.includes('公路机构')) || {};
const maintenanceEvent = item.events?.find(e => e.noticeRoles?.includes('养护站')) || {};
const trafficDeptEvent = item.events?.find((e) => e.noticeRoles?.includes('交通主管')) || {}
const roadKeeperEvent = item.events?.find((e) => e.noticeRoles?.includes('护路员')) || {}
const roadOrgEvent = item.events?.find((e) => e.noticeRoles?.includes('公路机构')) || {}
const maintenanceEvent = item.events?.find((e) => e.noticeRoles?.includes('养护站')) || {}
//
const lastNoticeTime = item.events?.[0]?.lastNoticeTime || '';
const urgeTimeParts = lastNoticeTime ? lastNoticeTime.split('T') : ['', ''];
const lastNoticeTime = item.events?.[0]?.lastNoticeTime || ''
const urgeTimeParts = lastNoticeTime ? lastNoticeTime.split('T') : ['', '']
return {
id: index + 1,
@ -373,25 +343,25 @@ const fetchData = async () => {
isResponded: roadKeeperEvent.replyState === 'read',
},
generalStaff: {
name: item.GL1_QLGCS || "-",
phone: item.GL1_QLGCSDH || "-",
name: item.GL1_QLGCS || '-',
phone: item.GL1_QLGCSDH || '-',
},
urgeTime: {
date: urgeTimeParts[0] || '-',
time: urgeTimeParts[1] ? urgeTimeParts[1].substring(0, 8) : '-',
},
};
});
total.value = res.data.length;
}
})
total.value = res.data.length
}
} catch (error) {
console.error('获取通知实体数据失败:', error);
console.error('获取通知实体数据失败:', error)
}
}
};
//
const getTypeLabel = type => {
const getTypeLabel = (type) => {
const typeMap = {
'road-section': '路段',
road_section: '路段',
@ -399,46 +369,52 @@ const getTypeLabel = type => {
bridge: '桥梁',
tunnel: '隧道',
slope: '边坡',
project: '项目',
};
return typeMap[type] || type || '-';
};
project: '驻地',
}
return typeMap[type] || type || '-'
}
//
const getLevelClass = riskLevel => {
if (!riskLevel) return '';
if (riskLevel.includes('高')) return 'level-serious';
if (riskLevel.includes('一般')) return 'level-normal';
return '';
};
const getLevelClass = (riskLevel) => {
if (!riskLevel) return ''
if (riskLevel.includes('高')) return 'level-serious'
if (riskLevel.includes('一般')) return 'level-normal'
return ''
}
//
const handleSearch = () => {
fetchData()
loadBarChartData()
}
// visible
watch(
() => props.visible,
newVal => {
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
currentPage.value = 1
fetchData()
loadBarChartData()
}
}
);
},
)
//
watch(
() => props.dispatchDateRange,
() => {
if (props.visible) {
currentPage.value = 1;
fetchData();
currentPage.value = 1
fetchData()
}
},
{ deep: true }
);
{ deep: true },
)
onMounted(() => {
//
loadBarChartData();
});
loadBarChartData()
})
</script>
<style lang="scss" scoped>

View File

@ -0,0 +1,370 @@
<template>
<div v-if="visible" class="softphone-dialog-overlay" @click="handleOverlayClick">
<div class="softphone-dialog" @click.stop>
<!-- 四个角的装饰 -->
<div class="corner corner-top-left"></div>
<div class="corner corner-top-right"></div>
<div class="corner corner-bottom-left"></div>
<div class="corner corner-bottom-right"></div>
<!-- 标题栏 -->
<div class="dialog-header">
<div class="header-title">软电话配置</div>
<div class="close-btn" @click="handleCancel">
<el-icon><Close /></el-icon>
</div>
</div>
<!-- 内容区域 -->
<div class="dialog-content">
<!-- 状态按钮组 -->
<div class="status-buttons">
<button
v-for="status in statusList"
:key="status.value"
:class="['status-btn', { active: currentStatus === status.value }]"
@click="currentStatus = status.value"
>
{{ status.label }}
</button>
</div>
<!-- 表单区域 -->
<div class="form-area">
<div class="form-item">
<span class="form-label">服务器环境</span>
<input v-model="formData.serverEnv" type="text" class="form-input" placeholder="请输入服务器环境" />
</div>
<div class="form-item">
<span class="form-label">工号</span>
<input v-model="formData.workId" type="text" class="form-input" placeholder="请输入工号" />
</div>
<div class="form-item">
<span class="form-label">坐席密码</span>
<input v-model="formData.seatPassword" type="password" class="form-input" placeholder="请输入坐席密码" />
</div>
</div>
</div>
<!-- 按钮区域 -->
<div class="dialog-footer">
<button class="btn-cancel" @click="handleCancel">取消</button>
<button class="btn-confirm" @click="handleConfirm">确定</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, watch } from 'vue'
import { Close } from '@element-plus/icons-vue'
const props = defineProps({
visible: {
type: Boolean,
default: false,
},
})
const emit = defineEmits(['update:visible', 'confirm', 'cancel'])
//
const statusList = [
{ label: '未登录', value: 'notLogin' },
{ label: '示忙', value: 'busy' },
{ label: '示闲', value: 'idle' },
{ label: '呼出', value: 'callOut' },
{ label: '保持', value: 'hold' },
{ label: '接回', value: 'retrieve' },
{ label: '挂断', value: 'hangUp' },
{ label: '会议', value: 'conference' },
{ label: '应答', value: 'answer' },
{ label: '转出', value: 'transfer' },
{ label: '咨询', value: 'consult' },
]
//
const currentStatus = ref('notLogin')
//
const formData = reactive({
serverEnv: '',
workId: '',
seatPassword: '',
})
//
const handleConfirm = () => {
emit('confirm', {
status: currentStatus.value,
...formData,
})
emit('update:visible', false)
}
//
const handleCancel = () => {
emit('cancel')
emit('update:visible', false)
}
//
const handleOverlayClick = () => {
handleCancel()
}
// visible
watch(
() => props.visible,
(newVal) => {
if (newVal) {
currentStatus.value = 'notLogin'
formData.serverEnv = ''
formData.workId = ''
formData.seatPassword = ''
}
},
)
</script>
<style lang="scss" scoped>
.softphone-dialog-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
z-index: 2200;
}
.softphone-dialog {
width: 500px;
background: linear-gradient(135deg, rgba(20, 50, 90, 0.95) 0%, rgba(10, 30, 60, 0.98) 100%);
border: 1px solid rgba(64, 169, 255, 0.3);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
overflow: hidden;
animation: dialogSlideIn 0.3s ease-out;
position: relative;
@media (max-width: 768px) {
width: 90vw;
max-width: 90vw;
}
//
.corner {
position: absolute;
width: 20px;
height: 20px;
border: 1px solid #40a9ff;
z-index: 100;
pointer-events: none;
&.corner-top-left {
top: 0;
left: 0;
border-right: none;
border-bottom: none;
}
&.corner-top-right {
top: 0;
right: 0;
border-left: none;
border-bottom: none;
}
&.corner-bottom-left {
bottom: 0;
left: 0;
border-right: none;
border-top: none;
}
&.corner-bottom-right {
bottom: 0;
right: 0;
border-left: none;
border-top: none;
}
}
}
@keyframes dialogSlideIn {
from {
opacity: 0;
transform: scale(0.8) translateY(-50px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
//
.dialog-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background: linear-gradient(90deg, rgba(64, 169, 255, 0.15) 0%, rgba(64, 169, 255, 0.05) 100%);
border-bottom: 1px solid rgba(64, 169, 255, 0.2);
.header-title {
font-size: 16px;
font-weight: 600;
color: #fff;
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.close-btn {
width: 19px;
height: 19px;
display: flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.6);
cursor: pointer;
font-size: 13px;
transition: color 0.3s;
flex-shrink: 0;
&:hover {
color: #fff;
}
}
}
//
.dialog-content {
padding: 20px;
max-height: 60vh;
overflow-y: auto;
}
//
.status-buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-bottom: 24px;
padding-bottom: 16px;
border-bottom: 1px solid rgba(64, 169, 255, 0.2);
.status-btn {
padding: 6px 12px;
background: transparent;
border: 1px solid rgba(64, 169, 255, 0.4);
border-radius: 4px;
color: rgba(255, 255, 255, 0.7);
font-size: 12px;
cursor: pointer;
transition: all 0.3s;
&:hover {
border-color: rgba(64, 169, 255, 0.6);
color: rgba(255, 255, 255, 0.9);
}
&.active {
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border-color: #40a9ff;
color: #fff;
}
}
}
//
.form-area {
display: flex;
flex-direction: column;
gap: 16px;
}
.form-item {
display: flex;
align-items: center;
gap: 16px;
.form-label {
width: 80px;
font-size: 14px;
color: rgba(255, 255, 255, 0.8);
flex-shrink: 0;
}
.form-input {
flex: 1;
height: 36px;
padding: 0 12px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(64, 169, 255, 0.3);
border-radius: 4px;
color: #fff;
font-size: 14px;
transition: all 0.3s;
&::placeholder {
color: rgba(255, 255, 255, 0.4);
}
&:focus {
outline: none;
border-color: rgba(64, 169, 255, 0.6);
background: rgba(255, 255, 255, 0.08);
}
}
}
//
.dialog-footer {
display: flex;
justify-content: center;
gap: 16px;
padding: 0 20px 20px;
flex-wrap: wrap;
.btn-cancel {
min-width: 80px;
height: 32px;
padding: 0 24px;
background-color: transparent;
border: 1px solid rgba(64, 169, 255, 0.4);
color: rgba(255, 255, 255, 0.8);
font-size: 14px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background-color: rgba(64, 169, 255, 0.1);
border-color: rgba(64, 169, 255, 0.6);
color: #fff;
}
}
.btn-confirm {
min-width: 80px;
height: 32px;
padding: 0 24px;
background: linear-gradient(135deg, #40a9ff 0%, #1890ff 100%);
border: none;
color: #fff;
font-size: 14px;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s;
&:hover {
background: linear-gradient(135deg, #69c0ff 0%, #40a9ff 100%);
}
}
}
</style>

View File

@ -26,12 +26,7 @@
@change="handleWarningLevelChange"
clearable
>
<el-option
v-for="option in warningLevelOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
<el-option v-for="option in warningLevelOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</div>
<div class="filter-item">
@ -43,12 +38,7 @@
@change="handleWarningLevelChange"
clearable
>
<el-option
v-for="option in regionOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
<el-option v-for="option in regionOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</div>
<div class="filter-item">
@ -60,12 +50,7 @@
@change="handleWarningLevelChange"
clearable
>
<el-option
v-for="option in isEndedOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
<el-option v-for="option in isEndedOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</div>
<div class="filter-item">
@ -77,12 +62,7 @@
@change="handleWarningLevelChange"
clearable
>
<el-option
v-for="option in isRespondedOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
<el-option v-for="option in isRespondedOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</div>
<div class="filter-item">
@ -107,17 +87,12 @@
</template>
<script setup>
import { ref, watch } from 'vue';
import { Search } from '@element-plus/icons-vue';
import baseDialog from '../component/baseDialog.vue';
import { formatDateTime } from '../component/index.js';
import { request } from '@/utils/request';
import {
warningLevelOptions,
regionOptions,
isEndedOptions,
isRespondedOptions,
} from '../component/index.js';
import { ref, watch } from 'vue'
import { Search } from '@element-plus/icons-vue'
import baseDialog from '../component/baseDialog.vue'
import { formatDateTime } from '../component/index.js'
import { request } from '@/utils/request'
import { warningLevelOptions, regionOptions, isEndedOptions, isRespondedOptions } from '../component/index.js'
const props = defineProps({
visible: {
@ -128,9 +103,9 @@ const props = defineProps({
type: Array,
default: () => [],
},
});
})
const emit = defineEmits(['update:visible', 'close', 'openImpactPoint']);
const emit = defineEmits(['update:visible', 'close', 'openImpactPoint', 'getResponseStatusrowFn'])
//
const filterForm = ref({
@ -138,7 +113,7 @@ const filterForm = ref({
region: '',
isEnded: '',
isResponded: '',
});
})
//
const tableColumns = ref([
@ -153,82 +128,56 @@ const tableColumns = ref([
{ prop: 'responded', label: '已回应', width: '' },
{ prop: 'notResponded', label: '未回应', width: '' },
{ prop: 'urged', label: '已催告', width: '' },
]);
])
//
const tableData = ref([]);
const tableData = ref([])
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(36);
//
const tableHeight = ref(300);
//
const headerCellStyle = () => {
return {
backgroundColor: '#1C4979',
color: '#fff',
fontSize: '14px',
fontWeight: '500',
textAlign: 'center',
padding: '12px 16px',
border: 'none',
};
};
const cellStyle = () => {
return {
backgroundColor: 'transparent',
color: 'rgba(255, 255, 255, 0.85)',
fontSize: '13px',
textAlign: 'center',
padding: '12px 16px',
border: 'none',
};
};
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(36)
//
const handleClose = () => {
emit('update:visible', false);
emit('close');
};
emit('update:visible', false)
emit('close')
}
// base-dialog
//
const handleCalledClick = () => {
emit('responseStatus');
};
const handleCalledClick = (row) => {
emit('responseStatus')
emit('getResponseStatusrowFn', row)
}
//
const handleSearch = () => {
console.log('查询条件:', filterForm.value);
currentPage.value = 1;
fetchData();
};
console.log('查询条件:', filterForm.value)
currentPage.value = 1
fetchData()
}
//
const handleSizeChange = val => {
pageSize.value = val;
fetchData();
};
const handleSizeChange = (val) => {
pageSize.value = val
fetchData()
}
const handleCurrentChange = val => {
currentPage.value = val;
fetchData();
};
const handleCurrentChange = (val) => {
currentPage.value = val
fetchData()
}
//
const handleWarningLevelChange = () => {
currentPage.value = 1;
fetchData();
};
currentPage.value = 1
fetchData()
}
//
const fetchData = async () => {
try {
console.log(props.dispatchDateRange);
console.log(props.dispatchDateRange)
const params = {
start: '',
end: '',
@ -239,30 +188,28 @@ const fetchData = async () => {
countyName: '',
isExpire: filterForm.value.isEnded,
isReply: filterForm.value.isResponded,
};
}
if (regionOptions.value.length > 0) {
//
const selectedRegion = regionOptions.value.find(
option => option.value === filterForm.value.region
);
const selectedRegion = regionOptions.value.find((option) => option.value === filterForm.value.region)
if (selectedRegion) {
params.countyName = selectedRegion.label == '全部' ? '' : selectedRegion.label || '';
params.countyName = selectedRegion.label == '全部' ? '' : selectedRegion.label || ''
}
}
if (props.dispatchDateRange.length > 0) {
params.start = formatDateTime(props.dispatchDateRange[0]) || '';
params.end = formatDateTime(props.dispatchDateRange[1]) || '';
params.start = formatDateTime(props.dispatchDateRange[0]) || ''
params.end = formatDateTime(props.dispatchDateRange[1]) || ''
}
const res = await request({
url: '/snow-ops-platform/weather-warning/notice-count',
method: 'GET',
params,
});
})
console.log(res);
console.log(res)
if (res) {
//
tableData.value = res.data.map((item, index) => ({
@ -278,46 +225,47 @@ const fetchData = async () => {
responded: item.replyCount || 0,
notResponded: (item.notifyCount || 0) - (item.replyCount || 0),
urged: item.retryCount || 0,
}));
total.value = res.total || res.data.length;
warningId: item.warningId || '-',
}))
total.value = res.total || res.data.length
}
} catch (error) {
console.error('获取预警通知统计失败:', error);
console.error('获取预警通知统计失败:', error)
}
}
};
//
const getLevelClass = riskLevel => {
if (!riskLevel) return '';
if (riskLevel.includes('红')) return 'level-red';
if (riskLevel.includes('橙')) return 'level-orange';
if (riskLevel.includes('黄')) return 'level-yellow';
if (riskLevel.includes('蓝')) return 'level-blue';
return '';
};
const getLevelClass = (riskLevel) => {
if (!riskLevel) return ''
if (riskLevel.includes('红')) return 'level-red'
if (riskLevel.includes('橙')) return 'level-orange'
if (riskLevel.includes('黄')) return 'level-yellow'
if (riskLevel.includes('蓝')) return 'level-blue'
return ''
}
// visible
watch(
() => props.visible,
newVal => {
(newVal) => {
if (newVal) {
currentPage.value = 1;
fetchData();
currentPage.value = 1
fetchData()
}
}
);
},
)
//
watch(
() => props.dispatchDateRange,
() => {
if (props.visible) {
currentPage.value = 1;
fetchData();
currentPage.value = 1
fetchData()
}
},
{ deep: true }
);
{ deep: true },
)
</script>
<style lang="scss" scoped>

View File

@ -17,56 +17,20 @@
<div class="filter-row">
<div class="filter-item">
<span class="filter-label">预警等级</span>
<el-select
v-model="filterForm.riskLeve"
placeholder="请选择"
class="filter-select el-select"
clearable
size="small"
:teleported="false"
>
<el-option
v-for="item in warningLevelOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="filterForm.riskLeve" placeholder="请选择" class="filter-select el-select" clearable size="small" :teleported="false">
<el-option v-for="item in warningLevelOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="filter-item">
<span class="filter-label">影响区域</span>
<el-select
v-model="filterForm.countyName"
placeholder="请选择"
class="filter-select"
clearable
size="small"
:teleported="false"
>
<el-option
v-for="item in regionOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="filterForm.countyName" placeholder="请选择" class="filter-select" clearable size="small" :teleported="false">
<el-option v-for="item in regionOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="filter-item">
<span class="filter-label">是否失效</span>
<el-select
v-model="filterForm.isEnded"
placeholder="请选择"
class="filter-select"
clearable
size="small"
:teleported="false"
>
<el-option
v-for="item in isEndedOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="filterForm.isEnded" placeholder="请选择" class="filter-select" clearable size="small" :teleported="false">
<el-option v-for="item in isEndedOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
@ -101,14 +65,14 @@
</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: {
@ -119,44 +83,44 @@ const props = defineProps({
type: Object,
default: () => ({}),
},
});
})
const emit = defineEmits(['update:visible', 'close', 'impactClick']);
const emit = defineEmits(['update:visible', 'close', 'impactClick'])
//
const dateRange = ref([]);
const dateRange = ref([])
onMounted(() => {
//
if (props.visible) {
initDialogData();
initDialogData()
}
});
})
// 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 || '';
currentPage.value = 1;
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;
dateRange.value = newVal
filterForm.value.dateRange = newVal
}
},
{ deep: true }
);
{ deep: true },
)
//
const filterForm = ref({
@ -164,12 +128,12 @@ const filterForm = ref({
countyName: '',
isEnded: '',
dateRange: dateRange.value,
});
})
//
const handleDateChange = val => {
filterForm.value.dateRange = val;
};
const handleDateChange = (val) => {
filterForm.value.dateRange = val
}
//
// index.js
@ -181,7 +145,7 @@ const handleDateChange = val => {
// index.js
//
const tableHeight = ref(300);
const tableHeight = ref(300)
//
const tableColumns = ref([
@ -193,22 +157,22 @@ const tableColumns = ref([
{ prop: 'warningTime', label: '生效时间', width: '' },
{ prop: 'endTime', label: '失效时间', width: '' },
{ prop: 'impactCount', label: '影响点数量', width: '', slot: 'impactCount' },
]);
])
//
const tableData = ref([]);
const tableData = ref([])
//
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
//
const loading = ref(false);
const loading = ref(false)
//
const fetchWarningData = async () => {
loading.value = true;
loading.value = true
try {
const params = {
offset: (currentPage.value - 1) * pageSize.value,
@ -219,38 +183,36 @@ const fetchWarningData = async () => {
weatherType: '',
isExpire: '',
countyName: '',
};
}
//
if (filterForm.value.riskLeve) {
params.riskLevel = filterForm.value.riskLeve;
params.riskLevel = filterForm.value.riskLeve
}
if (filterForm.value.countyName) {
const selectedRegion = regionOptions.value.find(
option => option.value === filterForm.value.countyName
);
const selectedRegion = regionOptions.value.find((option) => option.value === filterForm.value.countyName)
if (selectedRegion) {
params.countyName = selectedRegion.label == '全部' ? '' : selectedRegion.label || '';
params.countyName = selectedRegion.label == '全部' ? '' : selectedRegion.label || ''
}
}
if (filterForm.value.isEnded !== undefined && filterForm.value.isEnded !== '') {
params.isExpire = filterForm.value.isEnded;
params.isExpire = filterForm.value.isEnded
}
if (filterForm.value.dateRange && filterForm.value.dateRange.length === 2) {
params.start = formatDateTime(filterForm.value.dateRange[0]);
params.end = formatDateTime(filterForm.value.dateRange[1]);
params.start = formatDateTime(filterForm.value.dateRange[0])
params.end = formatDateTime(filterForm.value.dateRange[1])
}
const res = await request({
url: '/snow-ops-platform/weather-warning/affected-count/_by_weather',
method: 'GET',
params,
});
})
if (res.code === '00000' && res.data) {
//
const list = res.data.data || res.data.records || [];
total.value = res.data.total || 0;
const list = res.data.data || res.data.records || []
total.value = res.data.total || 0
tableData.value = list.map((item, index) => ({
index: index + 1,
@ -261,79 +223,80 @@ const fetchWarningData = async () => {
warningTime: item.startTime || '-',
endTime: item.endTime || '-',
impactCount: item.affectedCount || 0,
}));
warningId: item.warningId || '',
}))
} else {
tableData.value = [];
total.value = 0;
tableData.value = []
total.value = 0
}
} catch (error) {
console.error('获取预警数据失败:', error);
tableData.value = [];
total.value = 0;
console.error('获取预警数据失败:', error)
tableData.value = []
total.value = 0
} finally {
loading.value = false;
loading.value = false
}
}
};
//
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');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
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')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
//
const getWarningClass = level => {
const getWarningClass = (level) => {
const classMap = {
红色预警: '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);
const handleImpactClick = (item) => {
emit('impactClick', item)
emit('impactClickItem', {
...item,
dateRange: filterForm.value.dateRange || [],
riskLeve: filterForm.value.riskLeve || '',
});
};
})
}
//
const handleSizeChange = val => {
pageSize.value = val;
console.log('分页大小变化:', val);
currentPage.value = 1;
fetchWarningData();
};
const handleSizeChange = (val) => {
pageSize.value = val
console.log('分页大小变化:', val)
currentPage.value = 1
fetchWarningData()
}
const handleCurrentChange = val => {
console.log('当前页码变化:', val);
currentPage.value = val;
fetchWarningData();
};
const handleCurrentChange = (val) => {
console.log('当前页码变化:', val)
currentPage.value = val
fetchWarningData()
}
//
const initDialogData = () => {
//
if (getdateRange.value && getdateRange.value.length === 2) {
dateRange.value = getdateRange.value;
dateRange.value = getdateRange.value
}
// warningitem
@ -343,83 +306,83 @@ const initDialogData = () => {
countyName: props.warningitem.countyName || '',
isEnded: '',
dateRange: dateRange.value,
};
}
} else {
filterForm.value = {
riskLeve: '',
countyName: '',
isEnded: '',
dateRange: dateRange.value,
};
}
}
//
currentPage.value = 1;
console.log('初始化筛选条件:', filterForm.value);
fetchWarningData();
};
currentPage.value = 1
console.log('初始化筛选条件:', filterForm.value)
fetchWarningData()
}
//
const resetData = () => {
//
tableData.value = [];
total.value = 0;
currentPage.value = 1;
tableData.value = []
total.value = 0
currentPage.value = 1
//
filterForm.value = {
riskLeve: '',
countyName: '',
isEnded: '',
dateRange: [],
};
}
//
dateRange.value = [];
};
dateRange.value = []
}
// visible
watch(
() => props.visible,
newVal => {
(newVal) => {
if (newVal) {
//
initDialogData();
initDialogData()
} else {
//
resetData();
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();
if (oldVal && Object.keys(oldVal).length > 0 && props.visible) {
currentPage.value = 1
fetchWarningData()
}
},
{ deep: true }
);
{ deep: true },
)
// dateRange filterForm.dateRange
watch(
() => dateRange.value,
newVal => {
filterForm.value.dateRange = newVal;
(newVal) => {
filterForm.value.dateRange = newVal
},
{ deep: true }
);
{ deep: true },
)
watch(
() => filterForm.value.dateRange,
newVal => {
dateRange.value = newVal;
(newVal) => {
dateRange.value = newVal
},
{ deep: true }
);
{ deep: true },
)
</script>
<style lang="scss" scoped>

View File

@ -8,7 +8,7 @@
: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>
@ -16,16 +16,11 @@
</div>
</div>
<!-- 气象预警监测表格组件 -->
<WeatherWarningTable ref="weatherWarningTableRef" @clearFilters="handleClearFilters" />
<WeatherWarningTable ref="weatherWarningTableRef" :getdateRange="getdateRange" @clearFilters="handleClearFilters" />
<!-- 涉灾隐患点图片弹窗 -->
<div v-if="showHazardPopup" class="hazard-popup" :style="popupStyle">
<div class="hazard-popup-content">
<div
v-for="(item, index) in hazardItems"
:key="index"
class="hazard-item"
@click="handleHazardItemClick(item)"
>
<div v-for="(item, index) in hazardItems" :key="index" class="hazard-item" @click="handleHazardItemClick(item)">
<img :src="item.icon" :alt="item.label" />
<span>{{ item.label }}</span>
</div>
@ -34,12 +29,7 @@
<!-- 路段图片弹窗 -->
<div v-if="showRoadPopup" class="hazard-popup road-popup" :style="roadPopupStyle">
<div class="hazard-popup-content">
<div
v-for="(item, index) in roadItems"
:key="index"
class="hazard-item"
@click="handleRoadItemClick(item)"
>
<div v-for="(item, index) in roadItems" :key="index" class="hazard-item" @click="handleRoadItemClick(item)">
<img :src="item.icon" :alt="item.label" />
<span>{{ item.label }}</span>
</div>
@ -49,48 +39,43 @@
</template>
<script setup>
import { ref, computed, nextTick } from 'vue';
import WeatherWarningTable from './component/WeatherWarningTable.vue';
import { ref, computed, nextTick, inject } 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 engineeringIconIconIcon 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 engineeringIconIconIcon 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 engineeringIconIcon 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 engineeringIconIcon 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 getdateRange = inject('getdateRange', ref([]))
const emit = defineEmits(['changeActiveIndex', 'clearMapMarkers', 'hazardItemClick', 'roadItemClick', 'showHazardPopupfn'])
const activeIndex = ref(-1);
const activeIndex = ref(-1)
const menuItems = [
{
@ -108,7 +93,7 @@ const menuItems = [
icon1: roadIconIcon1,
},
{
label: '项目',
label: '驻地',
icon: 'icon-warning',
iconClass: 'warning',
icon: warningIconIcon,
@ -149,12 +134,12 @@ const menuItems = [
icon: engineeringIconIconIcon,
icon1: engineeringIconIcon,
},
];
]
//
const showHazardPopup = ref(false);
const popupStyle = ref({});
const navIconRefs = ref([]);
const showHazardPopup = ref(false)
const popupStyle = ref({})
const navIconRefs = ref([])
//
const hazardItems = ref([
@ -164,11 +149,11 @@ const hazardItems = ref([
{ icon: hazardIconIcon4, label: '较大路外隐患点', isWithinRedLine: '否' },
{ icon: hazardIconIcon1, label: '一般路内隐患点', isWithinRedLine: '是' },
{ icon: hazardIconIcon2, label: '一般路外隐患点', isWithinRedLine: '否' },
]);
])
//
const showRoadPopup = ref(false);
const roadPopupStyle = ref({});
const showRoadPopup = ref(false)
const roadPopupStyle = ref({})
//
const roadItems = ref([
@ -176,108 +161,108 @@ const roadItems = ref([
{ icon: tunnelLineIcon2, label: '较高风险路段' },
{ icon: tunnelLineIcon1, label: '中风险路段' },
{ icon: tunnelLineIcon, label: '低风险路段' },
]);
])
// nav-icon-boxref
const setNavIconRef = (el, index) => {
if (el) {
navIconRefs.value[index] = el;
navIconRefs.value[index] = el
}
};
const weatherWarningTableRef = ref(null);
}
const weatherWarningTableRef = ref(null)
//
const handleClick = (item, index) => {
//
if (item.label === '涉灾隐患点') {
//
showHazardPopup.value = !showHazardPopup.value;
showHazardPopup.value = !showHazardPopup.value
if (showHazardPopup.value) {
showRoadPopup.value = false;
showRoadPopup.value = false
}
if (showHazardPopup.value) {
//
const navIconBox = navIconRefs.value[index];
const navIconBox = navIconRefs.value[index]
if (navIconBox) {
const rect = navIconBox.getBoundingClientRect();
const rect = navIconBox.getBoundingClientRect()
popupStyle.value = {
position: 'fixed',
left: rect.right + 10 + 'px',
top: rect.top + 'px',
zIndex: 2,
};
}
}
}
} else if (item.label === '路段') {
//
//
showRoadPopup.value = !showRoadPopup.value;
showRoadPopup.value = !showRoadPopup.value
if (showRoadPopup.value) {
showHazardPopup.value = false;
showHazardPopup.value = false
}
if (showRoadPopup.value) {
//
const navIconBox = navIconRefs.value[index];
const navIconBox = navIconRefs.value[index]
if (navIconBox) {
const rect = navIconBox.getBoundingClientRect();
const rect = navIconBox.getBoundingClientRect()
roadPopupStyle.value = {
position: 'fixed',
left: rect.right + 10 + 'px',
top: rect.top + 'px',
zIndex: 2,
};
}
}
emit('showHazardPopupfn', false);
}
emit('showHazardPopupfn', false)
} else {
showHazardPopup.value = false;
showRoadPopup.value = false;
emit('showHazardPopupfn', false);
showHazardPopup.value = false
showRoadPopup.value = false
emit('showHazardPopupfn', false)
//
emit('hideRoadStats');
emit('hideRoadStats')
}
activeIndex.value = index;
activeIndex.value = index
if (item.label !== '路段') {
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);
};
emit('showHazardPopupfn', true)
}
const roadItem = ref({});
const roadItem = ref({})
//
const handleRoadItemClick = item => {
console.log('点击路段:', item);
roadItem.value = item;
emit('roadItemClick', item);
const handleRoadItemClick = (item) => {
console.log('点击路段:', item)
roadItem.value = item
emit('roadItemClick', item)
//
// showRoadPopup.value = false;
};
}
//
const handleClearFilters = () => {
console.log('清除筛选条件');
console.log('清除筛选条件')
//
activeIndex.value = -1;
activeIndex.value = -1
//
showHazardPopup.value = false;
showRoadPopup.value = false;
showHazardPopup.value = false
showRoadPopup.value = false
//
emit('clearMapMarkers');
emit('showHazardPopupfn', false);
};
emit('clearMapMarkers')
emit('showHazardPopupfn', false)
}
</script>
<style lang="scss" scoped>

View File

@ -2,12 +2,7 @@
<!-- 气象预警监测表格 -->
<div class="weather-warning-wrapper">
<div class="weather-warning-panel">
<img
class="clear-icon"
src="../../../assets/RiskWarning_img/清除icon@2x.png"
alt=""
@click="clearFilters"
/>
<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">
@ -48,13 +43,7 @@
<div class="warning-level">
<img
:src="
row.levelClass === 'red'
? redIcon
: row.levelClass === 'blue'
? blueIcon
: row.levelClass === 'orange'
? orangeIcon
: yellowIcon
row.levelClass === 'red' ? redIcon : row.levelClass === 'blue' ? blueIcon : row.levelClass === 'orange' ? orangeIcon : yellowIcon
"
alt=""
/>
@ -70,14 +59,28 @@
</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';
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'
import { formatDateTime } from './index'
const emit = defineEmits(['clearFilters']);
const emit = defineEmits(['clearFilters'])
const props = defineProps({
getdateRange: {
type: Array,
default: () => [],
},
})
watch(
() => props.getdateRange,
(newVal) => {
console.log('日期范围变化:===========', newVal)
fetchWeatherWarningData()
},
)
//
const filters = ref({
@ -85,98 +88,94 @@ const filters = ref({
blue: false,
orange: false,
yellow: false,
});
})
//
const warningData = ref([]);
const warningData = ref([])
//
const tableRef = ref(null);
const isScrolling = ref(true);
const scrollTimer = ref(null);
const scrollSpeed = 50; //
const scrollStep = 1; //
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;
});
return filters.value.red || filters.value.blue || filters.value.orange || filters.value.yellow
})
//
const startAutoScroll = () => {
if (scrollTimer.value) {
clearInterval(scrollTimer.value);
clearInterval(scrollTimer.value)
}
if (!isScrolling.value || hasFilter.value) return;
if (!isScrolling.value || hasFilter.value) return
const tableBody = document.querySelector(
'.weather-warning-panel .el-table__body-wrapper .el-scrollbar__wrap'
);
if (!tableBody) 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;
tableBody.scrollTop += scrollStep
// 使 >=
const maxScroll = tableBody.scrollHeight - tableBody.clientHeight;
const maxScroll = tableBody.scrollHeight - tableBody.clientHeight
if (tableBody.scrollTop >= maxScroll - 2) {
tableBody.scrollTop = 0;
tableBody.scrollTop = 0
}
}
}, scrollSpeed);
};
}, scrollSpeed)
}
//
const stopAutoScroll = () => {
if (scrollTimer.value) {
clearInterval(scrollTimer.value);
scrollTimer.value = null;
clearInterval(scrollTimer.value)
scrollTimer.value = null
}
}
};
//
const resetScrollToTop = () => {
const tableBody = document.querySelector(
'.weather-warning-panel .el-table__body-wrapper .el-scrollbar__wrap'
);
const tableBody = document.querySelector('.weather-warning-panel .el-table__body-wrapper .el-scrollbar__wrap')
if (tableBody) {
tableBody.scrollTop = 0;
tableBody.scrollTop = 0
}
}
};
//
const handleMouseEnter = () => {
stopAutoScroll();
};
stopAutoScroll()
}
//
const handleMouseLeave = () => {
if (!hasFilter.value) {
isScrolling.value = true;
startAutoScroll();
isScrolling.value = true
startAutoScroll()
}
}
};
//
watch(hasFilter, newVal => {
watch(hasFilter, (newVal) => {
if (newVal) {
//
stopAutoScroll();
resetScrollToTop();
stopAutoScroll()
resetScrollToTop()
} else {
//
isScrolling.value = true;
isScrolling.value = true
nextTick(() => {
startAutoScroll();
});
startAutoScroll()
})
}
});
})
//
const loading = ref(false);
const loading = ref(false)
//
const fetchWeatherWarningData = async () => {
loading.value = true;
loading.value = true
try {
const res = await request({
url: '/snow-ops-platform/weather-warning/affected-count/_by_weather',
@ -184,117 +183,118 @@ const fetchWeatherWarningData = async () => {
params: {
offset: 0,
limit: 100000,
start: props.getdateRange?.[0] ? formatDateTime(props.getdateRange[0] || '') : '',
end: props.getdateRange?.[1] ? formatDateTime(props.getdateRange[1] || '') : '',
},
});
console.log('气象预警数据:', res);
})
console.log('气象预警数据:', res)
if (res.code === '00000' && res.data) {
//
warningData.value = res.data.data.map(item => ({
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 = [];
warningData.value = []
}
loading.value = false;
loading.value = false
nextTick(() => {
startAutoScroll();
startAutoScroll()
//
const tableContainer = document.querySelector('.weather-warning-panel .table-container');
const tableContainer = document.querySelector('.weather-warning-panel .table-container')
if (tableContainer) {
tableContainer.addEventListener('mouseenter', handleMouseEnter);
tableContainer.addEventListener('mouseleave', handleMouseLeave);
tableContainer.addEventListener('mouseenter', handleMouseEnter)
tableContainer.addEventListener('mouseleave', handleMouseLeave)
}
});
})
} catch (error) {
console.error('获取气象预警数据失败:', error);
warningData.value = [];
loading.value = false;
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 '';
};
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();
});
console.log('获取气象预警数据')
fetchWeatherWarningData()
})
onUnmounted(() => {
stopAutoScroll();
const tableContainer = document.querySelector('.weather-warning-panel .table-container');
stopAutoScroll()
const tableContainer = document.querySelector('.weather-warning-panel .table-container')
if (tableContainer) {
tableContainer.removeEventListener('mouseenter', handleMouseEnter);
tableContainer.removeEventListener('mouseleave', handleMouseLeave);
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;
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;
});
});
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';
};
return rowIndex % 2 === 0 ? 'even-row' : 'odd-row'
}
//
const clearFilters = () => {
console.log('清除筛选条件');
console.log('清除筛选条件')
filters.value = {
red: false,
blue: false,
orange: false,
yellow: false,
};
}
//
emit('clearFilters');
};
emit('clearFilters')
}
//
const setWarningData = data => {
warningData.value = data;
};
const setWarningData = (data) => {
warningData.value = data
}
defineExpose({
clearFilters,
setWarningData,
});
})
</script>
<style lang="scss" scoped>

View File

@ -179,7 +179,7 @@ export const logUserOperation = (type, command) => {
// 使用 fetch 的 keepalive 选项确保页面卸载后请求仍能完成
fetch('/snow-ops-platform/weather-warning/users/logs', {
method: 'PUT',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
@ -201,121 +201,6 @@ export const logUserOperation = (type, command) => {
}
}
// 监听外部应用打开并记录日志的辅助函数
const listenForAppLaunch = (type, command) => {
let hasLogged = false
let hasLostFocus = false // 是否已经失去焦点
let focusTimer = null // 用于延迟检测的定时器
const startTime = Date.now()
console.log('开始监听外部应用打开...', { type, command, initialHidden: document.hidden, startTime })
// 确认打开外部应用
const confirmLaunch = () => {
if (hasLogged) return
hasLogged = true
console.log('确认用户点击了"打开"按钮,调用日志接口')
logUserOperation(type, command)
cleanup()
}
// 检测打开状态
const checkLaunchStatus = () => {
if (hasLogged) return
const now = Date.now()
const elapsed = now - startTime
const isHidden = document.hidden
const currentHasFocus = document.hasFocus()
console.log(`检测状态 [${elapsed}ms]:`, { isHidden, hasFocus: currentHasFocus, hasLostFocus, hasLogged })
// 情况1: 页面被隐藏 → 用户点击了"打开"
if (isHidden) {
confirmLaunch()
return
}
// 情况2: 失去焦点后重新获得焦点 → 用户点击了"取消"
if (hasLostFocus && currentHasFocus) {
console.log('用户点击了"取消"按钮,不记录日志')
cleanup()
return
}
// 记录当前焦点状态
if (!currentHasFocus && !hasLostFocus) {
hasLostFocus = true
console.log('页面失去焦点,开始等待用户选择...')
// 设置延迟检测1.5秒后如果仍未获得焦点,认为用户点击了"打开"
focusTimer = setTimeout(() => {
if (!hasLogged && hasLostFocus && !document.hasFocus()) {
console.log('1.5秒未重新获得焦点,认为用户点击了"打开"')
confirmLaunch()
}
}, 1500)
}
}
// 方法1: 监听 visibilitychange 事件
const handleVisibilityChange = () => {
console.log('visibilitychange 触发, document.hidden:', document.hidden)
checkLaunchStatus()
}
// 方法2: 监听 window blur 事件
const handleWindowBlur = () => {
console.log('window blur 触发')
hasLostFocus = true
// 设置延迟检测1.5秒后如果仍未获得焦点,认为用户点击了"打开"
focusTimer = setTimeout(() => {
if (!hasLogged && hasLostFocus && !document.hasFocus()) {
console.log('1.5秒未重新获得焦点,认为用户点击了"打开"')
confirmLaunch()
}
}, 1500)
}
// 方法3: 监听 window focus 事件(判断用户是否点击取消)
const handleWindowFocus = () => {
console.log('window focus 触发')
checkLaunchStatus()
}
// 方法4: 定时轮询检测
let checkInterval = setInterval(() => {
checkLaunchStatus()
const elapsed = Date.now() - startTime
// 10秒后停止轮询
if (elapsed > 10000 || hasLogged) {
clearInterval(checkInterval)
}
}, 200)
// 清理函数
const cleanup = () => {
console.log('清理监听器')
clearInterval(checkInterval)
clearTimeout(focusTimer)
document.removeEventListener('visibilitychange', handleVisibilityChange)
window.removeEventListener('blur', handleWindowBlur)
window.removeEventListener('focus', handleWindowFocus)
}
document.addEventListener('visibilitychange', handleVisibilityChange)
window.addEventListener('blur', handleWindowBlur)
window.addEventListener('focus', handleWindowFocus)
// 10秒后自动清理
setTimeout(() => {
if (!hasLogged) {
console.log('10秒超时未检测到打开操作清理监听器')
cleanup()
}
}, 10000)
}
// 打开视频会议
export const openVideoConference = async (item) => {
@ -419,14 +304,21 @@ export const opencallConference = async (item) => {
logUserOperation('phone-based-confirmation', JSON.stringify(jsobj))
}
// 基础 API 地址配置根据当前网址动态判断存在bxztpc时外网地址否则内网地址
const BASE_API_URL = window.location.href.includes('bxztpc')
? 'http://58.144.223.132:30008/'
: PROXY_TARGET
// 从本地存储获取 token
const getTokenFromStorage = () => {
try {
// 首先尝试从 URL 获取 token
const urlParams = new URLSearchParams(window.location.search)
const urlToken = urlParams.get('token')
console.log('从 URL 获取的 token:', urlToken)
if (urlToken) {
// 更新本地存储
localStorage.setItem('token', urlToken)
sessionStorage.setItem('token', urlToken)
console.log('从 URL 获取并更新 token 成功')
return urlToken
}
// 尝试从 localStorage 获取
const token = localStorage.getItem('token')
if (token) return token
@ -442,6 +334,8 @@ const getTokenFromStorage = () => {
}
}
// 基础 API 地址配置根据当前网址动态判断存在bxztpc时外网地址否则内网地址
const BASE_API_URL = 'http://58.144.223.132:30008/'
// 获取图片地址数组
export const getImageUrlList = (fileIdStr) => {
if (!fileIdStr || typeof fileIdStr !== 'string') {
@ -450,7 +344,10 @@ export const getImageUrlList = (fileIdStr) => {
}
// 按逗号分割字符串
const fileIdList = fileIdStr.split(',').map(id => id.trim()).filter(id => id)
const fileIdList = fileIdStr
.split(',')
.map((id) => id.trim())
.filter((id) => id)
if (fileIdList.length === 0) {
console.warn('fileIdStr 分割后为空')
@ -461,7 +358,7 @@ export const getImageUrlList = (fileIdStr) => {
const token = getTokenFromStorage()
// 构建图片地址数组
const imageUrlList = fileIdList.map(fileId => {
const imageUrlList = fileIdList.map((fileId) => {
return `${BASE_API_URL}apis/file-api/private/preview?fileId=${fileId}&token=${token}`
})

View File

@ -1,113 +0,0 @@
<template>
<div class="risk-table">
<table>
<thead>
<tr>
<th class="col-type">
<div>项目</div>
<div>类型</div>
</th>
<th class="col-red">红色预警</th>
<th class="col-orange">橙色预警</th>
<th class="col-yellow">黄色预警</th>
<th class="col-blue">蓝色预警</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in tableData" :key="index">
<td class="col-type">{{ item.type }}</td>
<td class="col-red">{{ item.red }}</td>
<td class="col-orange">{{ item.orange }}</td>
<td class="col-yellow">{{ item.yellow }}</td>
<td class="col-blue">{{ item.blue }}</td>
</tr>
</tbody>
</table>
</div>
</template>
<script setup>
defineProps({
tableData: {
type: Array,
default: () => [
{ type: '桥梁', red: 0, orange: 1, yellow: 8, blue: 11 },
{ type: '隧道', red: 0, orange: 0, yellow: 6, blue: 12 },
{ type: '边坡', red: 0, orange: 1, yellow: 9, blue: 9 },
{ type: '路段', red: 0, orange: 2, yellow: 11, blue: 7 },
{ type: '项目', red: 0, orange: 2, yellow: 11, blue: 7 },
],
},
});
</script>
<style lang="scss" scoped>
.risk-table {
width: 100%;
height: 100%;
padding: 10px;
box-sizing: border-box;
table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
color: #fff;
th,
td {
padding: 4px 4px;
text-align: center;
border: none;
}
thead {
th {
font-weight: normal;
font-size: 12px;
color: rgba(255, 255, 255, 0.8);
&.col-type {
color: rgba(255, 255, 255, 0.6);
}
}
}
tbody {
tr {
&:nth-child(odd) {
background-color: rgba(255, 255, 255, 0.05);
}
td {
font-size: 14px;
&.col-type {
color: rgba(255, 255, 255, 0.9);
}
}
}
}
.col-red {
color: #ff4d4f;
}
.col-orange {
color: #ff7a45;
}
.col-yellow {
color: #ffc53d;
}
.col-blue {
color: #40a9ff;
}
.col-type {
width: 20%;
}
}
}
</style>

View File

@ -103,6 +103,7 @@
v-model:visible="dialogVisible.warningInfo"
@close="closeDialog('warningInfo')"
@responseStatus="openDialog('responseStatus')"
@getResponseStatusrowFn="getResponseStatusfn"
/>
<!-- 事件详情对话框 -->
@ -129,6 +130,7 @@
<!-- 影响点情况对话框 -->
<impactPointDialog
:handleImpactItem="handleImpactItem"
:getdateRange="getdateRange"
v-model:visible="dialogVisible.impactPoint"
@close="closeDialog('impactPoint')"
@detail="openDialog('impactPointDetail')"
@ -151,6 +153,7 @@
<!-- 响应状态对话框 -->
<responseStatusDialog
:dispatchDateRange="dispatchDateRange"
:responseStatusData="responseStatusRow"
v-model:visible="dialogVisible.responseStatus"
@close="closeDialog('responseStatus')"
@detail="openDialog('responsePointDetail')"
@ -240,7 +243,7 @@
<imageInspectionDialog v-model:visible="dialogVisible.imageInspection" @close="closeDialog('imageInspection')" />
<!-- 巡查里程对话框 -->
<patrolMileageDialog v-model:visible="dialogVisible.patrolMileage" @close="closeDialog('patrolMileage')" />
<patrolMileageDialog v-model:visible="dialogVisible.patrolMileage" :getdateRange="getdateRange" @close="closeDialog('patrolMileage')" />
<!-- 巡查情况对话框 -->
<patrolSituationDialog v-model:visible="dialogVisible.patrolSituation" @close="closeDialog('patrolSituation')" />
@ -387,8 +390,9 @@ const handleHideRoadStats = () => {
const handleHazardItemClick = (item) => {
console.log('点击隐患点:', item)
//
let items = { ...item }
if (chongqingMapRef.value) {
chongqingMapRef.value.handleHazardItemClick(item)
chongqingMapRef.value.handleHazardItemClick(items, false)
}
}
const showHazardPopup = ref(false)
@ -459,17 +463,17 @@ const impactPointDetailItem = ref({})
//
const handleImpactPointClick = (item) => {
console.log('影响点点击:', item)
impactPointDetailItem.value = item
impactPointDetailItem.value = { ...item }
}
const handleImpactItem = ref({})
const handleImpactClickItem = (item) => {
console.log('影响点点击详情:', item)
handleImpactItem.value = item
handleImpactItem.value = { ...item }
}
const tongnanInfoItemData = ref({})
const tongnanInfoItemDatafn = (item) => {
console.log('点击详情:', item)
tongnanInfoItemData.value = item
tongnanInfoItemData.value = { ...item }
}
//
const closeDialog = (dialogName) => {
@ -482,7 +486,7 @@ const closeDialog = (dialogName) => {
const handleOpenHazardPointSituation = (item) => {
console.log('打开涉灾隐患点情况弹窗:', item)
hazardPointDialogTitle.value = '涉灾隐患点情况'
hazardPointData.value = item
hazardPointData.value = { ...item }
dialogVisible.value.hazardPointSituation = true
}
@ -502,28 +506,33 @@ const handleOpenRoadSectionSituation = (item) => {
const warningitem = ref({})
const handleWarningClick = (item) => {
console.log('气象预警点击:', item)
warningitem.value = item
warningitem.value = { ...item }
}
const clearanceSituationDialogItemData = ref({})
const handleItemData = (item) => {
console.log('点击详情:', item)
clearanceSituationDialogItemData.value = item
clearanceSituationDialogItemData.value = { ...item }
}
const dispatchDateRange = ref([])
const handleDispatchDateRange = (range) => {
dispatchDateRange.value = range
dispatchDateRange.value = [...range]
}
const rightDateRange = ref([])
const updateDateRange = (range) => {
console.log('更新日期范围:', range)
rightDateRange.value = range
rightDateRange.value = [...range]
}
const filterForm = ref({})
const updateFilterForm = (item) => {
console.log('更新筛选表单:', item)
filterForm.value = item
filterForm.value = { ...item }
}
const responseStatusRow = ref({})
const getResponseStatusfn = (row) => {
console.log('已叫应详情:', row)
responseStatusRow.value = row
}
//
@ -571,7 +580,7 @@ const handleDistrictClick = (item) => {
} else if (item.data.roadType == 'rural') {
openDialog('responseSituation')
} else if (item.data.type == 'project' && item.data.roadType == '-') {
//
//
openDialog('tongnanResponsible')
}
}

View File

@ -70,7 +70,7 @@
<el-table-column prop="bridgeCount" label="桥梁" :min-width="vw(50)" />
<el-table-column prop="tunnelCount" label="隧道" :min-width="vw(50)" />
<el-table-column prop="slopeCount" label="边坡" :min-width="vw(50)" />
<el-table-column prop="projectCount" label="项目" :min-width="vw(50)" />
<el-table-column prop="projectCount" label="驻地" :min-width="vw(50)" />
</el-table>
</div>
@ -79,7 +79,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"
@ -153,6 +153,7 @@ watch(
() => getdateRange.value,
(newVal) => {
console.log('left.vue 日期范围变化:', newVal)
dateRange.value = JSON.parse(JSON.stringify(newVal))
init()
},
{ deep: true },
@ -258,7 +259,7 @@ const impactData = ref([
{ name: '桥梁', count: 0 },
{ name: '隧道', count: 0 },
{ name: '边坡', count: 0 },
{ name: '项目', count: 0 },
{ name: '驻地', count: 0 },
])
const dateRange = ref([])
@ -593,12 +594,12 @@ const loadBarChartData = async () => {
bridge: '桥梁',
tunnel: '隧道',
slope: '边坡',
project: '项目',
project: '驻地',
Road: '路段',
Bridge: '桥梁',
Tunnel: '隧道',
Slope: '边坡',
Project: '项目',
Project: '驻地',
}
// name
@ -615,7 +616,7 @@ const loadBarChartData = async () => {
impactData.value[2].count = item.count || 0
} else if (item.name == '边坡') {
impactData.value[3].count = item.count || 0
} else if (item.name == '项目') {
} else if (item.name == '驻地') {
impactData.value[4].count = item.count || 0
}
})
@ -628,7 +629,7 @@ const loadBarChartData = async () => {
{ name: '桥梁', count: 0 },
{ name: '隧道', count: 0 },
{ name: '边坡', count: 0 },
{ name: '项目', count: 0 },
{ name: '驻地', count: 0 },
]),
)
}

View File

@ -107,7 +107,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"
@ -205,6 +205,7 @@ 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 { formatDateTime } from './component/index.js'
const getdateRange = inject('getdateRange', ref([]))
const emit = defineEmits([
'openClearanceSituation',
@ -218,7 +219,6 @@ const emit = defineEmits([
//
const setRefreshRightData = inject('setRefreshRightData')
const getdateRange = inject('getdateRange', ref([]))
const props = defineProps({})
@ -586,10 +586,10 @@ const handleResourceClick = (item) => {
//
const controlData1 = ref([
{ label: '封闭管控数', value: '40' },
{ label: '半幅通行数', value: '40' },
{ label: '限速(限车型)数', value: '24' },
{ label: '告警阻拦处数', value: '32' },
{ label: '封闭管控数', value: '40', key: '全幅封闭' },
{ label: '半幅通行数', value: '40', key: '半幅封闭' },
{ label: '限速(限车型)数', value: '24', key: '限速' },
{ label: '告警阻拦处数', value: '32', key: '告警阻拦' },
])
const controlData2 = ref([
{ label: '停工项目数', value: '0', key: 'stoped_project_count' },
@ -756,6 +756,7 @@ watch(
() => getdateRange.value,
(newVal) => {
console.log('right.vue 日期范围变化:', newVal)
dateRange.value = newVal
init()
},
{ deep: true, immediate: true },

View File

@ -81,7 +81,16 @@ const emit = defineEmits(['openAIResult', 'dateRangeChange'])
//
const triggerRefreshLeftData = inject('triggerRefreshLeftData')
const dateRange = ref([])
// 00:00:00 - 23:59:59
const getTodayDateRange = () => {
const today = new Date()
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0)
const endOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59)
return [startOfDay, endOfDay]
}
const dateRange = ref(getTodayDateRange())
//
const hazardStatsShowArr = ref([])
@ -219,6 +228,10 @@ const fetchRiskLevelCount = async () => {
//
onMounted(() => {
//
const todayRange = getTodayDateRange()
dateRange.value = todayRange
emit('dateRangeChange', todayRange)
fetchRiskLevelCount()
})
</script>

View File

@ -98,7 +98,7 @@ const columns = [
},
{
prop: "affectedProjectCount",
label: "影响项目",
label: "影响驻地",
},
{
prop: "responseStatus",